Skip to content

Typography

📖

Problem

Text appears everywhere in a shell — clock, workspace labels, app names, notification bodies, launcher search. Without a typography system, font sizes become inconsistent: one label is 13px, another is 14px, headings vary arbitrarily.

Naive Approach

Set font.pixelSize on every Text element individually. Change the font family in one place, forget to update the others.

MentalModel

💡Mental Model

A type scale is a set of predefined font sizes with semantic names. h1 (large heading), body (normal text), caption (small labels). Each size is derived from a base value multiplied by a ratio (typically 1.25). This is the same approach used by Material Design and Tailwind.

Idea

Create a Typography.qml singleton with readonly property groups for font families and sizes. Also export helper functions for Text component configuration.

Build It

qml
// Typography.qml
pragma Singleton
import Quickshell

QtObject {
  readonly property string fontFamily: "Inter, sans-serif"
  readonly property string monoFamily: "JetBrains Mono, monospace"

  readonly property real baseSize: 14

  // Type scale (1.25 ratio)
  readonly property real h1: Math.round(baseSize * 1.953)
  readonly property real h2: Math.round(baseSize * 1.563)
  readonly property real h3: Math.round(baseSize * 1.25)
  readonly property real body: baseSize
  readonly property real small: Math.round(baseSize * 0.8)
  readonly property real caption: Math.round(baseSize * 0.64)

  readonly property real weightNormal: 400
  readonly property real weightMedium: 500
  readonly property real weightBold: 700
}
qml
// Usage
import QtQuick
import "Typography.qml" as T

Column {
  spacing: 4

  Text {
    text: "Section Title"
    font.pixelSize: T.Typography.h3
    font.family: T.Typography.fontFamily
    font.weight: T.Typography.weightBold
  }

  Text {
    text: "This is body text at the base size."
    font.pixelSize: T.Typography.body
    font.family: T.Typography.fontFamily
  }

  Text {
    text: "Small caption text"
    font.pixelSize: T.Typography.caption
    color: "#888888"
  }
}

BuildIt Breakdown

The type scale uses a 1.25 ratio (major third) — the same as Material Design. Math.round ensures integer pixel sizes for crisp rendering. font.family supports fallback with "sans-serif" after the named font. Additional properties like letterSpacing and lineHeight can be added per size.

Improve It

Add TextStyle QML components that pre-configure a Text with typography tokens. Add letterSpacing variants for display text. Support variable font weight axes. Add a mono helper for code snippets.

CommonMistake

⚠️Common Mistake

Setting font.pixelSize with a non-integer value. Sub-pixel font sizes cause anti-aliasing artifacts and inconsistent rendering across monitors. Always round to an integer.

Under the Hood

🔧Under the Hood

QML's Text element uses Qt's text rendering engine. font.family accepts comma-separated fallbacks. The font is resolved at render time using platform fontconfig. font.weight maps to OpenType weight values (100-900). pixelSize is absolute; use font.pointSize for DPI-aware sizing if your shell needs to scale across displays.

Exercises

Exercise

Create a Typography.qml with body and caption sizes, then use them in a clock widget.

Exercise

Build a Label component that accepts a variant property (h1, h2, body, caption) and applies typography tokens automatically.

Exercise

Implement DPI-aware font scaling that adjusts the base size based on Screen.devicePixelRatio.

Recap

What You've Learned

Built with VitePress. Licensed under MIT.