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 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
rootFontSizepushes 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.
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.
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.