Typography

Espalier's typography system is built on three interlocking parts: a geometric type scale, a fluid scaling mechanism, and proportional spacing. Together they produce a reading experience that remains visually consistent and accessible from narrow mobile screens to wide desktop monitors without media query breakpoints.

Type scale

A seven-step scale (--esp-type-tiny through --esp-type-huge) generated from a single geometric ratio. The steps provide the shared size vocabulary; complete recipes below turn them into recurring content voices and opt-in display treatments.

→ Type Scale guide

Type roles

The scale names sizes; roles name the recurring voices a page sets in them. Use the recipe on the native element that carries the content's meaning — a <p> for a lead, <label> for a native control, or <figcaption> for a figure. Espalier deliberately does not add a generic text wrapper: native semantics matter, and HTML's role attribute belongs to ARIA.

Role What it is Contrast contract
Overline The small tag above a heading ("NEW", a section kicker) Lc 90 at any size
Label Text that names a control or datum, not prose Lc 75 at the default 18px-plus size
Lead The opening paragraph set larger than body Lc 75 at the default 22.5px-plus size
Caption Attribution or context under an image, table, or figure Lc 90 at any size
.overline {
  font-size: var(--esp-type-overline-font-size);
  font-weight: var(--esp-type-overline-font-weight);
  letter-spacing: var(--esp-type-overline-letter-spacing);
  text-transform: var(--esp-type-overline-text-transform);
  color: var(--esp-type-overline-color);
}

.label {
  font-size: var(--esp-type-label-font-size);
  font-weight: var(--esp-type-label-font-weight);
  letter-spacing: var(--esp-type-label-letter-spacing);
  color: var(--esp-type-label-color);
}

.lead {
  font-size: var(--esp-type-lead-font-size);
  font-weight: var(--esp-type-lead-font-weight);
  letter-spacing: var(--esp-type-lead-letter-spacing);
  color: var(--esp-type-lead-color);
  max-inline-size: var(--esp-measure);
}

.caption {
  font-size: var(--esp-type-caption-font-size);
  font-weight: var(--esp-type-caption-font-weight);
  letter-spacing: var(--esp-type-caption-letter-spacing);
  color: var(--esp-type-caption-color);
}

These recipes use role-specific semantic inks rather than link, heading, or ordinary text colors. A theme's ink.color binding supplies all four hues; the engine enforces each role at its tier and re-emits the aliases inside a named context, so an inverted band receives its own safe overline and caption colors without consumer overrides.

Display headings

Display is an opt-in presentation recipe for a native heading, not another semantic role or an eighth scale step. Apply all six properties to the <h1> or <h2> that already expresses the document structure:

.display {
  font-family: var(--esp-type-display-font-family);
  font-size: var(--esp-type-display-font-size);
  font-weight: var(--esp-type-display-font-weight);
  line-height: var(--esp-type-display-line-height);
  letter-spacing: var(--esp-type-display-letter-spacing);
  color: var(--esp-type-display-color);
}
<h1 class="display">Rooted in warmth</h1>

The size is deliberately cross-step: at the 320px fluid viewport floor it equals --esp-type-large, and at the 1200px ceiling it equals --esp-type-huge. The hybrid rem + vw middle remains responsive to browser zoom. Its color alias is re-emitted by theme contexts, while family, weight, leading, and tracking inherit so a wrapper can customize the recipe. Ordinary headings keep their existing --esp-type-huge styling unless this class is applied.

Three rules keep the voices coherent:

  • Roles are built from scale steps and weights, never from arbitrary pixel sizes — that is what keeps them fluid and zoom-safe for free.
  • Size decides which contrast tier protects a role. Lc 90 covers text at any size; Lc 75 covers 16px-plus; Lc 60 is heading-scale only. Overline and caption therefore use Lc 90, while label and lead use Lc 75 at their default 18px-plus and 22.5px-plus sizes. If a custom rootFontSize pushes label or lead below 16px, that customization has crossed the role's documented tier and needs its own type-and-contrast review. Quietness comes from size, weight, and tracking, never opacity or a lower-contrast heading color.
  • Tracking (letter-spacing) belongs to small, uppercase, or label-like settings only; body sizes and above are already fitted by the font.

Fluid scaling

All type tokens use clamp() with a rem + vw hybrid so they interpolate smoothly across viewport widths while remaining responsive to browser zoom. No breakpoints required.

→ Fluid Typography guide

Spacing

The spacing system uses the same geometric approach as the type scale, with seven individual steps and six cross-step "fluid pairs" that span a wider scaling range for section-level padding and gaps.

→ Fluid Spacing guide

Accessibility

Espalier enforces APCA contrast targets on all generated text tokens. Fluid scaling includes a rem component to support text enlargement; verify the complete page at 200% zoom. Line lengths in narrow layout mode are constrained to an accessible measure.

→ Accessible Reading guide

Components API Guides Getting started Styling Espalier Browser support GitHub npm package Taproot I/O