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 that satisfies WCAG 2.1 SC 1.4.4 (Resize Text). 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