Semantic Theming Voice and Fonts in Typography (CEM) — 2025 Revision

Replacement for the 2020 article. This revision focuses on consumer-flow purpose (what the user is trying to do) and semantic meaning (what the content is), and translates that into speech, fontography and typography tokens that can be used consistently across products. discussion | git

Companion specs:


Sufler reading the text with various font sizes

1. Problem statement

Typography is a high-leverage part of semantic theming: it shapes comprehension, scanning efficiency, and decision-making. Yet most systems treat it as “choose a font and a scale”. In consumer-facing products, this is insufficient.

We need a semantic way to express typography so that:


2. CEM lens: consumer flow and meaning

Typography decisions should be driven by two orthogonal dimensions:

  1. Consumer flow purpose — what the user is doing at that moment:
  1. Semantic meaning of content — what the content is:

The 2020 set covered the first five categories; this revision also adds Data, because consumer “scan/compare/decide” flows rely heavily on numeric alignment and stable rendering.


3. Definitions

3.1 Fontography vs Typography vs Voice

The critical rule: voice is modality-independent semantics; ink and speech are projections of voice. CEM Typography Model stack diagram with ontography, Typography, and Voice


4. Principles

P1. Semantic intent first

Tokens should communicate intent (reading, data, tag) rather than implementation details (Roboto 14px).

P2. Bounded variation

Humans recognize a limited number of distinct variations reliably. Prefer a compact set (e.g., 7 steps) for weight, size, and spacing.

P3. Accessibility by construction

Default tokens must work with user scaling and accessibility settings.

P4. Internationalization and fallback are first-class

A semantic role must behave consistently across languages and glyph coverage.

P5. Data is a distinct semantic

Numbers and identifiers require predictable alignment and glyph selection.


5. Token model

5.1 Token groups

  1. Fontography families (semantic aliases)
  2. Thickness scale (bounded)
  3. Typography size scale (bounded)
  4. Typography line-height scale (role-based)
  5. Typography tracking/casing policies
  6. Typography feature policies (numerics, ligatures, optical sizing)
  7. Typography reading ergonomics (measure and paragraph rhythm)
  8. Voice semantics (cross-modal prominence) mapped to:

5.2 Canonical tokens (CSS custom properties)

These values are safe defaults. Brands override families first; products override density next.

:root {
    /* =========================
     * 1) Fontography — semantic families
     * (consumer meaning first)
     * ========================= */

    /* Rule: font family stacks are theme-invariant (light/dark/contrast).
     * Override families only for brand, platform, or locale coverage.
     */

    /* Long-form reading / comprehension */
    --cem-fontography-reading-family: "Roboto", "Source Sans Pro",
    system-ui, -apple-system, "Segoe UI", "Helvetica Neue", Arial,
    "Noto Sans", "Liberation Sans", sans-serif;

    /* UI labels / navigation / compact scanning */
    --cem-fontography-ui-family: var(--cem-fontography-reading-family);

    /* Code / scripts */
    --cem-fontography-script-family: ui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas,
    "Liberation Mono", "Courier New", "Noto Sans Mono", monospace;

    /* Initialism / condensed emphasis (optional override) */
    --cem-fontography-initialism-family: "Barlow Semi Condensed", "Roboto Condensed", "Arial Narrow",
    var(--cem-fontography-ui-family);

    /* Brand / display: visually distinctive from the default reading/UI sans stack. */
    --cem-fontography-brand-family: "Fraunces", "Playfair Display", Georgia, "Times New Roman", serif;


    /* =========================
     * 2) Thickness — 7-step bounded scale
     * (aligns with theme-data.xhtml “Thickness” concept)
     * ========================= */
    --cem-thickness-xx-light: 100;
    --cem-thickness-x-light: 200;
    --cem-thickness-light: 300;
    --cem-thickness-normal: 400;
    --cem-thickness-bold: 700;
    --cem-thickness-x-bold: 800;
    --cem-thickness-xx-bold: 900;


    /* =========================
     * 3) Typography — 7-step size scale (rem-based)
     * =========================  *
     * Rule: size scale is theme-invariant. Contrast modes adjust ink/decoration, not the global scale.
     */
    --cem-typography-size-xxs: 0.625rem;
    --cem-typography-size-xs: 0.75rem;
    --cem-typography-size-s: 0.875rem;
    --cem-typography-size-m: 1rem;
    --cem-typography-size-l: 1.5rem;
    --cem-typography-size-xl: 2.125rem;
    --cem-typography-size-xxl: 3rem;


    /* =========================
     * 4) Typography — line-height primitives
     * ========================= */
    --cem-typography-line-height-reading: 1.55;
    --cem-typography-line-height-ui: 1.2;
    --cem-typography-line-height-script: 1.4;
    --cem-typography-line-height-badge: 1;


    /* =========================
     * 5) Typography — tracking / casing primitives
     * ========================= */
    --cem-typography-letter-spacing-reading: normal;
    --cem-typography-letter-spacing-ui: 0.01em;
    --cem-typography-letter-spacing-caps: 0.08em;


    /* =========================
     * 6) Typography — feature policies
     * ========================= */
    --cem-typography-feature-numeric-data: tabular-nums lining-nums;
    --cem-typography-feature-ligatures-script: none;
    --cem-typography-feature-optical-sizing: auto;


    /* =========================
     * 7) Typography — reading ergonomics
     * ========================= */
    --cem-typography-reading-measure-max: 65ch;
    --cem-typography-reading-paragraph-gap: 0.75em;


    /* =========================
     * 8) Voice — cross-modal prominence
     * (same semantics for ink + speech)
     * ========================= */

    /* Ink projection (font weight / icon stroke strength)
     * All 7 thickness levels used for distinct voice-to-weight mapping:
     * whisper(100) → soft(200) → gentle(300) → regular(400) → firm(700) → strong(800) → loud(900)
     */
    --cem-voice-whisper-ink-thickness: var(--cem-thickness-xx-light);
    --cem-voice-soft-ink-thickness: var(--cem-thickness-x-light);
    --cem-voice-gentle-ink-thickness: var(--cem-thickness-light);
    --cem-voice-regular-ink-thickness: var(--cem-thickness-normal);
    --cem-voice-firm-ink-thickness: var(--cem-thickness-bold);
    --cem-voice-strong-ink-thickness: var(--cem-thickness-x-bold);
    --cem-voice-loud-ink-thickness: var(--cem-thickness-xx-bold);

    /* Icon stroke multiplier projection (optional; for stroke-based icon sets) */
    --cem-voice-whisper-icon-stroke-multiplier: 0.90;
    --cem-voice-soft-icon-stroke-multiplier: 0.95;
    --cem-voice-gentle-icon-stroke-multiplier: 1.00;
    --cem-voice-regular-icon-stroke-multiplier: 1.00;
    --cem-voice-firm-icon-stroke-multiplier: 1.10;
    --cem-voice-strong-icon-stroke-multiplier: 1.20;
    --cem-voice-loud-icon-stroke-multiplier: 1.30;

    /* Speech projection (product read-aloud; not screen-reader control) */
    --cem-voice-whisper-speech-volume: 0.65;
    --cem-voice-soft-speech-volume: 0.75;
    --cem-voice-gentle-speech-volume: 0.85;
    --cem-voice-regular-speech-volume: 1.00;
    --cem-voice-firm-speech-volume: 1.00;
    --cem-voice-strong-speech-volume: 1.00;
    --cem-voice-loud-speech-volume: 1.00;

    --cem-voice-whisper-speech-rate: 1.00;
    --cem-voice-soft-speech-rate: 1.00;
    --cem-voice-gentle-speech-rate: 1.00;
    --cem-voice-regular-speech-rate: 1.00;
    --cem-voice-firm-speech-rate: 0.98;
    --cem-voice-strong-speech-rate: 0.96;
    --cem-voice-loud-speech-rate: 0.94;

    --cem-voice-whisper-speech-pitch: 0.95;
    --cem-voice-soft-speech-pitch: 0.98;
    --cem-voice-gentle-speech-pitch: 1.00;
    --cem-voice-regular-speech-pitch: 1.00;
    --cem-voice-firm-speech-pitch: 1.03;
    --cem-voice-strong-speech-pitch: 1.06;
    --cem-voice-loud-speech-pitch: 1.10;

    /* SSML emphasis (optional; if your TTS engine supports SSML) */
    --cem-voice-whisper-ssml-emphasis: reduced;
    --cem-voice-soft-ssml-emphasis: reduced;
    --cem-voice-gentle-ssml-emphasis: moderate;
    --cem-voice-regular-ssml-emphasis: moderate;
    --cem-voice-firm-ssml-emphasis: moderate;
    --cem-voice-strong-ssml-emphasis: strong;
    --cem-voice-loud-ssml-emphasis: strong;


    /* =========================
     * Semantic endpoints
     * (consumer meaning visible at the front of the name)
     * ========================= */

    /* Reading */
    --cem-typography-reading-font-family: var(--cem-fontography-reading-family);
    --cem-typography-reading-font-size: var(--cem-typography-size-m);
    --cem-typography-reading-line-height: var(--cem-typography-line-height-reading);
    --cem-typography-reading-letter-spacing: var(--cem-typography-letter-spacing-reading);
    --cem-typography-reading-font-weight: var(--cem-voice-regular-ink-thickness);
    --cem-typography-reading-speech-volume: var(--cem-voice-regular-speech-volume);
    --cem-typography-reading-speech-rate: var(--cem-voice-regular-speech-rate);
    --cem-typography-reading-speech-pitch: var(--cem-voice-regular-speech-pitch);
    --cem-typography-reading-ssml-emphasis: var(--cem-voice-regular-ssml-emphasis);

    /* Heading weight: strong voice, independent of gentle UI text */
    --cem-typography-heading-font-weight: var(--cem-voice-strong-ink-thickness);

    /* UI */
    --cem-typography-ui-font-family: var(--cem-fontography-ui-family);
    --cem-typography-ui-font-size: var(--cem-typography-size-m);
    --cem-typography-ui-line-height: var(--cem-typography-line-height-ui);
    --cem-typography-ui-letter-spacing: var(--cem-typography-letter-spacing-ui);
    --cem-typography-ui-font-weight: var(--cem-voice-gentle-ink-thickness);
    --cem-typography-ui-speech-volume: var(--cem-voice-gentle-speech-volume);
    --cem-typography-ui-speech-rate: var(--cem-voice-gentle-speech-rate);
    --cem-typography-ui-speech-pitch: var(--cem-voice-gentle-speech-pitch);
    --cem-typography-ui-ssml-emphasis: var(--cem-voice-gentle-ssml-emphasis);

    /* Tag */
    --cem-typography-tag-font-family: var(--cem-fontography-ui-family);
    --cem-typography-tag-font-size: var(--cem-typography-size-s);
    --cem-typography-tag-line-height: var(--cem-typography-line-height-ui);
    --cem-typography-tag-letter-spacing: var(--cem-typography-letter-spacing-ui);
    --cem-typography-tag-font-weight: var(--cem-voice-firm-ink-thickness);
    --cem-typography-tag-speech-volume: var(--cem-voice-firm-speech-volume);
    --cem-typography-tag-speech-rate: var(--cem-voice-firm-speech-rate);
    --cem-typography-tag-speech-pitch: var(--cem-voice-firm-speech-pitch);
    --cem-typography-tag-ssml-emphasis: var(--cem-voice-firm-ssml-emphasis);

    /* Script */
    --cem-typography-script-font-family: var(--cem-fontography-script-family);
    --cem-typography-script-font-size: var(--cem-typography-size-s);
    --cem-typography-script-line-height: var(--cem-typography-line-height-script);
    --cem-typography-script-letter-spacing: normal;
    --cem-typography-script-font-weight: var(--cem-voice-regular-ink-thickness);
    --cem-typography-script-font-variant-ligatures: var(--cem-typography-feature-ligatures-script);
    --cem-typography-script-speech-volume: var(--cem-voice-regular-speech-volume);
    --cem-typography-script-speech-rate: var(--cem-voice-gentle-speech-rate);
    --cem-typography-script-speech-pitch: var(--cem-voice-gentle-speech-pitch);
    --cem-typography-script-ssml-emphasis: var(--cem-voice-gentle-ssml-emphasis);

    /* Data */
    --cem-typography-data-font-family: var(--cem-fontography-ui-family);
    --cem-typography-data-font-size: var(--cem-typography-size-m);
    --cem-typography-data-line-height: var(--cem-typography-line-height-ui);
    --cem-typography-data-letter-spacing: var(--cem-typography-letter-spacing-ui);
    --cem-typography-data-font-weight: var(--cem-voice-regular-ink-thickness);
    --cem-typography-data-font-variant-numeric: var(--cem-typography-feature-numeric-data);
    --cem-typography-data-speech-volume: var(--cem-voice-regular-speech-volume);
    --cem-typography-data-speech-rate: var(--cem-voice-firm-speech-rate);
    --cem-typography-data-speech-pitch: var(--cem-voice-regular-speech-pitch);
    --cem-typography-data-ssml-emphasis: var(--cem-voice-regular-ssml-emphasis);

    /* Initialism */
    --cem-typography-initialism-font-family: var(--cem-fontography-initialism-family);
    --cem-typography-initialism-font-size: var(--cem-typography-size-s);
    --cem-typography-initialism-line-height: var(--cem-typography-line-height-badge);
    --cem-typography-initialism-letter-spacing: var(--cem-typography-letter-spacing-caps);
    --cem-typography-initialism-font-weight: var(--cem-voice-firm-ink-thickness);
    --cem-typography-initialism-text-transform: uppercase;
    --cem-typography-initialism-speech-volume: var(--cem-voice-firm-speech-volume);
    --cem-typography-initialism-speech-rate: var(--cem-voice-firm-speech-rate);
    --cem-typography-initialism-speech-pitch: var(--cem-voice-firm-speech-pitch);
    --cem-typography-initialism-ssml-emphasis: var(--cem-voice-firm-ssml-emphasis);

    /* Iconized */
    --cem-typography-iconized-font-family: var(--cem-fontography-initialism-family);
    --cem-typography-iconized-font-size: calc(var(--cem-typography-size-m) * 2);
    --cem-typography-iconized-line-height: var(--cem-typography-line-height-badge);
    --cem-typography-iconized-letter-spacing: var(--cem-typography-letter-spacing-caps);
    --cem-typography-iconized-font-weight: var(--cem-voice-strong-ink-thickness);
    --cem-typography-iconized-text-transform: uppercase;
    --cem-typography-iconized-speech-volume: var(--cem-voice-strong-speech-volume);
    --cem-typography-iconized-speech-rate: var(--cem-voice-strong-speech-rate);
    --cem-typography-iconized-speech-pitch: var(--cem-voice-strong-speech-pitch);
    --cem-typography-iconized-ssml-emphasis: var(--cem-voice-strong-ssml-emphasis);

    /* Brand / Display */
    --cem-typography-brand-font-family: var(--cem-fontography-brand-family);
    --cem-typography-brand-font-size: var(--cem-typography-size-xxl);
    --cem-typography-brand-line-height: 1.1;
    --cem-typography-brand-letter-spacing: normal;
    --cem-typography-brand-font-weight: var(--cem-voice-strong-ink-thickness);
    --cem-typography-brand-speech-volume: var(--cem-voice-strong-speech-volume);
    --cem-typography-brand-speech-rate: var(--cem-voice-strong-speech-rate);
    --cem-typography-brand-speech-pitch: var(--cem-voice-strong-speech-pitch);
    --cem-typography-brand-ssml-emphasis: var(--cem-voice-strong-ssml-emphasis);
}

5.3 Theme projections (dark and contrast)

Themes should not swap font families or rescale the type ramp. Instead, they project semantics into ink (weight/stroke) and decoration (outlines/underlines), keeping layout stable.

Dark theme: slightly lighter ink weight to compensate for perceived boldness on dark backgrounds.

:root[data-cem-theme="dark"] {
    /* Keep families and size scale unchanged; only adjust ink projection. */
    --cem-voice-whisper-ink-thickness: 100;
    --cem-voice-soft-ink-thickness: 180;
    --cem-voice-gentle-ink-thickness: 280;
    --cem-voice-regular-ink-thickness: 350; /* ≈ 400 but lighter */
    --cem-voice-firm-ink-thickness: 650; /* ≈ 700 but lighter */
    --cem-voice-strong-ink-thickness: 750; /* ≈ 800 but lighter */
    --cem-voice-loud-ink-thickness: 850; /* ≈ 900 but lighter */
}

cem-voice-ladder.svg

High-contrast theme: preserve sizes; increase ink and add non-color cues where needed.

:root[data-cem-theme="contrast"] {
    /* Prefer stronger ink steps (or nearest available weights for non-variable fonts). */
    --cem-voice-gentle-ink-thickness: 400;
    --cem-voice-regular-ink-thickness: 500;
    --cem-voice-firm-ink-thickness: 750;
    --cem-voice-strong-ink-thickness: 850;

    /* Optional: add decoration cues at component level (not shown here). */
}

6. Semantic typography roles

This section defines what developers apply. Each role is a stable semantic endpoint, with minimal knobs.

Naming guideline: roles should describe meaning/purpose, not placement. Prefer reading, data, tag over body2, subtitle.

Consumer flow → semantic typography roles → default voice

6.1 Reading

Used for.
explanations, help text, descriptions, articles, long content2 column page in opened book

Behavior:

.cem-typography-reading {
    font-family: var(--cem-typography-reading-font-family);
    font-size: var(--cem-typography-reading-font-size);
    line-height: var(--cem-typography-reading-line-height);
    letter-spacing: var(--cem-typography-reading-letter-spacing);
    font-weight: var(--cem-typography-reading-font-weight);

    font-optical-sizing: var(--cem-typography-feature-optical-sizing);
    max-inline-size: var(--cem-typography-reading-measure-max);
}

.cem-typography-reading p {
    margin-block: 0 var(--cem-typography-reading-paragraph-gap);
}

6.2 UI (scan/navigate)

Used for.
navigation, button labels, form labels, menus, secondary textnavigation buttons image

Behavior:

.cem-typography-ui {
    font-family: var(--cem-typography-ui-font-family);
    font-size: var(--cem-typography-ui-font-size);
    line-height: var(--cem-typography-ui-line-height);
    letter-spacing: var(--cem-typography-ui-letter-spacing);
    font-weight: var(--cem-typography-ui-font-weight);

    font-optical-sizing: var(--cem-typography-feature-optical-sizing);
}

6.3 Tag

Used for.
compact labels, chips, metadata, status tagschips example image

Behavior:

.cem-typography-tag {
    font-family: var(--cem-typography-tag-font-family);
    font-size: var(--cem-typography-tag-font-size);
    line-height: var(--cem-typography-tag-line-height);
    letter-spacing: var(--cem-typography-tag-letter-spacing);
    font-weight: var(--cem-typography-tag-font-weight);
}

6.4 Script

Used for.
code blocks, commands, identifiers that must be copy-safescript example image

Behavior:

.cem-typography-script {
    font-family: var(--cem-typography-script-font-family);
    font-size: var(--cem-typography-script-font-size);
    line-height: var(--cem-typography-script-line-height);
    letter-spacing: var(--cem-typography-script-letter-spacing);
    font-weight: var(--cem-typography-script-font-weight);

    font-variant-ligatures: var(--cem-typography-script-font-variant-ligatures);
}

6.5 Data (new)

Used for:

Reason:

Behavior:

.cem-typography-data {
    font-family: var(--cem-typography-data-font-family);
    font-size: var(--cem-typography-data-font-size);
    line-height: var(--cem-typography-data-line-height);
    letter-spacing: var(--cem-typography-data-letter-spacing);
    font-weight: var(--cem-typography-data-font-weight);

    font-variant-numeric: var(--cem-typography-data-font-variant-numeric);
}

script example image

6.6 Initialism

Used for.
acronyms, avatar initials, short codes meant to be “badge-like”initials button example image

Reason:

Behavior:

.cem-typography-initialism {
    font-family: var(--cem-typography-initialism-font-family);
    font-size: var(--cem-typography-initialism-font-size);
    line-height: var(--cem-typography-initialism-line-height);
    letter-spacing: var(--cem-typography-initialism-letter-spacing);
    font-weight: var(--cem-typography-initialism-font-weight);

    text-transform: var(--cem-typography-initialism-text-transform);
}

6.7 Iconized

Used for.
letter-as-icon (monograms, single-letter pictograms)initials button example image

Behavior:

.cem-typography-iconized {
    font-family: var(--cem-typography-iconized-font-family);
    font-size: var(--cem-typography-iconized-font-size);
    line-height: var(--cem-typography-iconized-line-height);
    letter-spacing: var(--cem-typography-iconized-letter-spacing);
    font-weight: var(--cem-typography-iconized-font-weight);

    text-transform: var(--cem-typography-iconized-text-transform);
}

6.8 Brand / Display (optional)

Used for:

Behavior:

.cem-typography-brand {
    font-family: var(--cem-typography-brand-font-family);
    font-size: var(--cem-typography-brand-font-size);
    line-height: var(--cem-typography-brand-line-height);
    letter-spacing: var(--cem-typography-brand-letter-spacing);
    font-weight: var(--cem-typography-brand-font-weight);
}

7. Consumer-flow mapping (how to choose roles)

Use this table as a decision shortcut.

Consumer flow purposeDominant typography role(s)Default voice level
Orient / NavigateUI, Tag, Initialismgentle → firm
Scan / CompareData, UI, Tagregular → firm
Read / LearnReadingregular
ActUI (often bold), Tag (when actions are chip-like)firm
Confirm / ReflectReading (explanations), Data (metrics), Tag (status)regular → firm

8. Density and accessibility modes

Typography must respond to accessibility settings and product density.

8.1 Density tokens (optional extension)

If you need an explicit density switch, add one semantic knob:

:root {
    --cem-typography-density: comfortable; /* compact | comfortable */
}

In compact density, prefer smaller sizes for UI and tags while keeping Reading stable.

8.2 Minimums

8.3 Contrast modes


9. Internationalization and fallback

Semantic tokens must include fallback coverage.

Guidelines:


10. Practical implementation notes

10.1 Design tokens vs component tokens

10.2 Voice vs screen readers vs product read-aloud

Screen readers primarily follow HTML semantics and ARIA, not CSS typography. Do not rely on voice tokens to control assistive technology output.

Voice tokens are designed to provide consistent prominence across:

10.3 Example: SpeechSynthesis integration

This example reads the active typography role’s speech tokens from computed style and applies them to a TTS utterance.

function cemSpeak( el, text )
{
    const cs = getComputedStyle( el );

    // Choose one: read from the role tokens applied to the element.
    const rate = parseFloat( cs.getPropertyValue( '--cem-typography-reading-speech-rate' ) ) || 1;
    const pitch = parseFloat( cs.getPropertyValue( '--cem-typography-reading-speech-pitch' ) ) || 1;
    const volume = parseFloat( cs.getPropertyValue( '--cem-typography-reading-speech-volume' ) ) || 1;

    const utter = new SpeechSynthesisUtterance( text );
    utter.rate = rate;
    utter.pitch = pitch;
    utter.volume = volume;

    speechSynthesis.speak( utter );
}

10.4 Don’t over-tokenize

Start with these semantic roles:

Add Brand/Iconized only when a real use-case exists.


12. Token manifest tables

These tables are the machine-readable contract consumed by cem-voice-fonts-typography.html. They mirror the canonical declarations in §5.2 and §6, and add the dark / contrast ink-thickness projections from §5.3.

12.1 Foundation

cem-typography-fontography
TokenValueDescriptiontier
--cem-fontography-reading-family"Roboto", "Source Sans Pro", system-ui, -apple-system, "Segoe UI", "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serifLong-form reading family stackrequired
--cem-fontography-ui-familyvar(--cem-fontography-reading-family)UI labels / navigation / compact scanningrequired
--cem-fontography-script-familyui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", "Noto Sans Mono", monospaceCode / monospaced scriptsrequired
--cem-fontography-initialism-family"Barlow Semi Condensed", "Roboto Condensed", "Arial Narrow", var(--cem-fontography-ui-family)Initialism / condensed emphasisrecommended
--cem-fontography-brand-family"Fraunces", "Playfair Display", Georgia, "Times New Roman", serifBrand / display familyrecommended
cem-typography-thickness
TokenValueDescriptiontier
--cem-thickness-xx-light100Hairline weightrequired
--cem-thickness-x-light200Extra-light weightrequired
--cem-thickness-light300Light weightrequired
--cem-thickness-normal400Normal/regular weightrequired
--cem-thickness-bold700Bold weightrequired
--cem-thickness-x-bold800Extra-bold weightrequired
--cem-thickness-xx-bold900Heaviest weightrequired

The scale anchors fine print at 0.625rem, body text at 1rem, and page titles at 3rem. At a 16px root, the practical sizes are 10, 12, 14, 16, 24, 34, and 48px. These round geometric steps on each side of the medium anchor; the upper sizes progress by approximately √2 for clear heading separation. Heading roles below are suggested visual uses, while H1–H3 still express document structure. All sizes remain relative to the root font size and invariant across themes.

cem-typography-size
TokenValueDescriptiontier
--cem-typography-size-xxs0.625remFine print (10px @16px root)required
--cem-typography-size-xs0.75remCompact labels (12px)required
--cem-typography-size-s0.875remSupporting text (14px)required
--cem-typography-size-m1remDefault reading/UI size (16px)required
--cem-typography-size-l1.5remSubsection title / H3 (24px)required
--cem-typography-size-xl2.125remSection title / H2 (34px)required
--cem-typography-size-xxl3remPage title / H1 / brand (48px)required
cem-typography-line-height
TokenValueDescriptiontier
--cem-typography-line-height-reading1.55Long-form reading line-heightrequired
--cem-typography-line-height-ui1.2UI label / navigation line-heightrequired
--cem-typography-line-height-script1.4Code / script line-heightrequired
--cem-typography-line-height-badge1Single-line badge / initialism line-heightrequired
cem-typography-letter-spacing
TokenValueDescriptiontier
--cem-typography-letter-spacing-readingnormalDefault reading trackingrequired
--cem-typography-letter-spacing-ui0.01emUI / navigation trackingrequired
--cem-typography-letter-spacing-caps0.08emCaps / initialism trackingrequired
cem-typography-feature
TokenValueDescriptiontier
--cem-typography-feature-numeric-datatabular-nums lining-numsNumeric features for data rolerequired
--cem-typography-feature-ligatures-scriptnoneLigature policy for code / script rolerequired
--cem-typography-feature-optical-sizingautoOptical-sizing default (variable fonts)required
cem-typography-reading-ergonomics
TokenValueDescriptiontier
--cem-typography-reading-measure-max65chMax measure for reading flowsrequired
--cem-typography-reading-paragraph-gap0.75emParagraph rhythm for reading flowsrequired

12.2 Voice channels (cross-modal prominence)

7 voices × 6 channels. ink-thickness is required (drives weight); other channels are recommended (drive icon stroke and TTS prosody).

cem-typography-voice-ink-thickness
TokenValueDescriptiontier
--cem-voice-whisper-ink-thicknessvar(--cem-thickness-xx-light)Whisper voice → 100required
--cem-voice-soft-ink-thicknessvar(--cem-thickness-x-light)Soft voice → 200required
--cem-voice-gentle-ink-thicknessvar(--cem-thickness-light)Gentle voice → 300required
--cem-voice-regular-ink-thicknessvar(--cem-thickness-normal)Regular voice → 400required
--cem-voice-firm-ink-thicknessvar(--cem-thickness-bold)Firm voice → 700required
--cem-voice-strong-ink-thicknessvar(--cem-thickness-x-bold)Strong voice → 800required
--cem-voice-loud-ink-thicknessvar(--cem-thickness-xx-bold)Loud voice → 900required
cem-typography-voice-icon-stroke-multiplier
TokenValueDescriptiontier
--cem-voice-whisper-icon-stroke-multiplier0.90Whisper icon-stroke multiplierrecommended
--cem-voice-soft-icon-stroke-multiplier0.95Soft icon-stroke multiplierrecommended
--cem-voice-gentle-icon-stroke-multiplier1.00Gentle icon-stroke multiplierrecommended
--cem-voice-regular-icon-stroke-multiplier1.00Regular icon-stroke multiplierrecommended
--cem-voice-firm-icon-stroke-multiplier1.10Firm icon-stroke multiplierrecommended
--cem-voice-strong-icon-stroke-multiplier1.20Strong icon-stroke multiplierrecommended
--cem-voice-loud-icon-stroke-multiplier1.30Loud icon-stroke multiplierrecommended
cem-typography-voice-speech-volume
TokenValueDescriptiontier
--cem-voice-whisper-speech-volume0.65Whisper TTS volumerecommended
--cem-voice-soft-speech-volume0.75Soft TTS volumerecommended
--cem-voice-gentle-speech-volume0.85Gentle TTS volumerecommended
--cem-voice-regular-speech-volume1.00Regular TTS volumerecommended
--cem-voice-firm-speech-volume1.00Firm TTS volumerecommended
--cem-voice-strong-speech-volume1.00Strong TTS volumerecommended
--cem-voice-loud-speech-volume1.00Loud TTS volumerecommended
cem-typography-voice-speech-rate
TokenValueDescriptiontier
--cem-voice-whisper-speech-rate1.00Whisper TTS raterecommended
--cem-voice-soft-speech-rate1.00Soft TTS raterecommended
--cem-voice-gentle-speech-rate1.00Gentle TTS raterecommended
--cem-voice-regular-speech-rate1.00Regular TTS raterecommended
--cem-voice-firm-speech-rate0.98Firm TTS raterecommended
--cem-voice-strong-speech-rate0.96Strong TTS raterecommended
--cem-voice-loud-speech-rate0.94Loud TTS raterecommended
cem-typography-voice-speech-pitch
TokenValueDescriptiontier
--cem-voice-whisper-speech-pitch0.95Whisper TTS pitchrecommended
--cem-voice-soft-speech-pitch0.98Soft TTS pitchrecommended
--cem-voice-gentle-speech-pitch1.00Gentle TTS pitchrecommended
--cem-voice-regular-speech-pitch1.00Regular TTS pitchrecommended
--cem-voice-firm-speech-pitch1.03Firm TTS pitchrecommended
--cem-voice-strong-speech-pitch1.06Strong TTS pitchrecommended
--cem-voice-loud-speech-pitch1.10Loud TTS pitchrecommended
cem-typography-voice-ssml-emphasis
TokenValueDescriptiontier
--cem-voice-whisper-ssml-emphasisreducedWhisper SSML emphasisrecommended
--cem-voice-soft-ssml-emphasisreducedSoft SSML emphasisrecommended
--cem-voice-gentle-ssml-emphasismoderateGentle SSML emphasisrecommended
--cem-voice-regular-ssml-emphasismoderateRegular SSML emphasisrecommended
--cem-voice-firm-ssml-emphasismoderateFirm SSML emphasisrecommended
--cem-voice-strong-ssml-emphasisstrongStrong SSML emphasisrecommended
--cem-voice-loud-ssml-emphasisstrongLoud SSML emphasisrecommended

12.3 Theme ink-thickness projections (override-only)

Dark mode lightens ink to compensate for perceived boldness on dark backgrounds. Contrast mode strengthens ink where AA contrast benefits.

cem-typography-voice-ink-thickness-dark
TokenDark valueDescription
--cem-voice-whisper-ink-thickness100Whisper (unchanged)
--cem-voice-soft-ink-thickness180Soft (slightly lighter than 200)
--cem-voice-gentle-ink-thickness280Gentle (slightly lighter than 300)
--cem-voice-regular-ink-thickness350Regular (lighter than 400)
--cem-voice-firm-ink-thickness650Firm (lighter than 700)
--cem-voice-strong-ink-thickness750Strong (lighter than 800)
--cem-voice-loud-ink-thickness850Loud (lighter than 900)
--cem-typography-heading-font-weightvar(--cem-voice-strong-ink-thickness)Resolve heading alias in the themed scope
cem-typography-voice-ink-thickness-contrast
TokenContrast valueDescription
--cem-voice-gentle-ink-thickness400Gentle bumped to normal
--cem-voice-regular-ink-thickness500Regular bumped
--cem-voice-firm-ink-thickness750Firm bumped
--cem-voice-strong-ink-thickness850Strong bumped
--cem-typography-heading-font-weightvar(--cem-voice-strong-ink-thickness)Resolve heading alias in the themed scope

12.4 Semantic role endpoints

8 role × ~9-10 properties = 76 tokens. Each row is (token, value); tier is required for the cross-product contract because every role declares every property listed below. Special role-specific properties (font-variant-ligatures, font-variant-numeric, text-transform) appear only on the role that uses them.

Heading weight uses --cem-typography-heading-font-weight, backed by the strong voice: 800 in light/native, 750 in dark, and 850 in contrast themes. Use this endpoint for prominent headings while retaining gentle weight for ordinary UI text. The dark/contrast override tables redeclare the heading alias in the themed scope so nested theme containers resolve their local strong voice. Consumers may override heading weight independently. The loud/900 weight remains available for explicitly stronger emphasis.

cem-typography-roles
TokenValuetier
--cem-typography-reading-font-familyvar(--cem-fontography-reading-family)required
--cem-typography-reading-font-sizevar(--cem-typography-size-m)required
--cem-typography-reading-line-heightvar(--cem-typography-line-height-reading)required
--cem-typography-reading-letter-spacingvar(--cem-typography-letter-spacing-reading)required
--cem-typography-reading-font-weightvar(--cem-voice-regular-ink-thickness)required
--cem-typography-reading-speech-volumevar(--cem-voice-regular-speech-volume)required
--cem-typography-reading-speech-ratevar(--cem-voice-regular-speech-rate)required
--cem-typography-reading-speech-pitchvar(--cem-voice-regular-speech-pitch)required
--cem-typography-reading-ssml-emphasisvar(--cem-voice-regular-ssml-emphasis)required
--cem-typography-heading-font-weightvar(--cem-voice-strong-ink-thickness)required
--cem-typography-ui-font-familyvar(--cem-fontography-ui-family)required
--cem-typography-ui-font-sizevar(--cem-typography-size-m)required
--cem-typography-ui-line-heightvar(--cem-typography-line-height-ui)required
--cem-typography-ui-letter-spacingvar(--cem-typography-letter-spacing-ui)required
--cem-typography-ui-font-weightvar(--cem-voice-gentle-ink-thickness)required
--cem-typography-ui-speech-volumevar(--cem-voice-gentle-speech-volume)required
--cem-typography-ui-speech-ratevar(--cem-voice-gentle-speech-rate)required
--cem-typography-ui-speech-pitchvar(--cem-voice-gentle-speech-pitch)required
--cem-typography-ui-ssml-emphasisvar(--cem-voice-gentle-ssml-emphasis)required
--cem-typography-tag-font-familyvar(--cem-fontography-ui-family)required
--cem-typography-tag-font-sizevar(--cem-typography-size-s)required
--cem-typography-tag-line-heightvar(--cem-typography-line-height-ui)required
--cem-typography-tag-letter-spacingvar(--cem-typography-letter-spacing-ui)required
--cem-typography-tag-font-weightvar(--cem-voice-firm-ink-thickness)required
--cem-typography-tag-speech-volumevar(--cem-voice-firm-speech-volume)required
--cem-typography-tag-speech-ratevar(--cem-voice-firm-speech-rate)required
--cem-typography-tag-speech-pitchvar(--cem-voice-firm-speech-pitch)required
--cem-typography-tag-ssml-emphasisvar(--cem-voice-firm-ssml-emphasis)required
--cem-typography-script-font-familyvar(--cem-fontography-script-family)required
--cem-typography-script-font-sizevar(--cem-typography-size-s)required
--cem-typography-script-line-heightvar(--cem-typography-line-height-script)required
--cem-typography-script-letter-spacingnormalrequired
--cem-typography-script-font-weightvar(--cem-voice-regular-ink-thickness)required
--cem-typography-script-font-variant-ligaturesvar(--cem-typography-feature-ligatures-script)required
--cem-typography-script-speech-volumevar(--cem-voice-regular-speech-volume)required
--cem-typography-script-speech-ratevar(--cem-voice-gentle-speech-rate)required
--cem-typography-script-speech-pitchvar(--cem-voice-gentle-speech-pitch)required
--cem-typography-script-ssml-emphasisvar(--cem-voice-gentle-ssml-emphasis)required
--cem-typography-data-font-familyvar(--cem-fontography-ui-family)required
--cem-typography-data-font-sizevar(--cem-typography-size-m)required
--cem-typography-data-line-heightvar(--cem-typography-line-height-ui)required
--cem-typography-data-letter-spacingvar(--cem-typography-letter-spacing-ui)required
--cem-typography-data-font-weightvar(--cem-voice-regular-ink-thickness)required
--cem-typography-data-font-variant-numericvar(--cem-typography-feature-numeric-data)required
--cem-typography-data-speech-volumevar(--cem-voice-regular-speech-volume)required
--cem-typography-data-speech-ratevar(--cem-voice-firm-speech-rate)required
--cem-typography-data-speech-pitchvar(--cem-voice-regular-speech-pitch)required
--cem-typography-data-ssml-emphasisvar(--cem-voice-regular-ssml-emphasis)required
--cem-typography-initialism-font-familyvar(--cem-fontography-initialism-family)required
--cem-typography-initialism-font-sizevar(--cem-typography-size-s)required
--cem-typography-initialism-line-heightvar(--cem-typography-line-height-badge)required
--cem-typography-initialism-letter-spacingvar(--cem-typography-letter-spacing-caps)required
--cem-typography-initialism-font-weightvar(--cem-voice-firm-ink-thickness)required
--cem-typography-initialism-text-transformuppercaserequired
--cem-typography-initialism-speech-volumevar(--cem-voice-firm-speech-volume)required
--cem-typography-initialism-speech-ratevar(--cem-voice-firm-speech-rate)required
--cem-typography-initialism-speech-pitchvar(--cem-voice-firm-speech-pitch)required
--cem-typography-initialism-ssml-emphasisvar(--cem-voice-firm-ssml-emphasis)required
--cem-typography-iconized-font-familyvar(--cem-fontography-initialism-family)required
--cem-typography-iconized-font-sizecalc(var(--cem-typography-size-m) * 2)required
--cem-typography-iconized-line-heightvar(--cem-typography-line-height-badge)required
--cem-typography-iconized-letter-spacingvar(--cem-typography-letter-spacing-caps)required
--cem-typography-iconized-font-weightvar(--cem-voice-strong-ink-thickness)required
--cem-typography-iconized-text-transformuppercaserequired
--cem-typography-iconized-speech-volumevar(--cem-voice-strong-speech-volume)required
--cem-typography-iconized-speech-ratevar(--cem-voice-strong-speech-rate)required
--cem-typography-iconized-speech-pitchvar(--cem-voice-strong-speech-pitch)required
--cem-typography-iconized-ssml-emphasisvar(--cem-voice-strong-ssml-emphasis)required
--cem-typography-brand-font-familyvar(--cem-fontography-brand-family)required
--cem-typography-brand-font-sizevar(--cem-typography-size-xxl)required
--cem-typography-brand-line-height1.1required
--cem-typography-brand-letter-spacingnormalrequired
--cem-typography-brand-font-weightvar(--cem-voice-strong-ink-thickness)required
--cem-typography-brand-speech-volumevar(--cem-voice-strong-speech-volume)required
--cem-typography-brand-speech-ratevar(--cem-voice-strong-speech-rate)required
--cem-typography-brand-speech-pitchvar(--cem-voice-strong-speech-pitch)required
--cem-typography-brand-ssml-emphasisvar(--cem-voice-strong-ssml-emphasis)required

13. Token manifest index

Source tableSectionDescription
cem-typography-fontography§12.15 family stacks (reading/ui/script/initialism/brand)
cem-typography-thickness§12.17-step weight scale (--cem-thickness-*)
cem-typography-size§12.17-step rem-based size scale
cem-typography-line-height§12.14 line-height primitives (reading/ui/script/badge)
cem-typography-letter-spacing§12.13 tracking primitives (reading/ui/caps)
cem-typography-feature§12.13 feature policies (numeric-data, ligatures-script, optical-sizing)
cem-typography-reading-ergonomics§12.12 reading ergonomics (measure-max, paragraph-gap)
cem-typography-voice-ink-thickness§12.27 voice → ink-thickness (required)
cem-typography-voice-icon-stroke-multiplier§12.27 voice → icon-stroke multipliers
cem-typography-voice-speech-volume§12.27 voice → TTS volume
cem-typography-voice-speech-rate§12.27 voice → TTS rate
cem-typography-voice-speech-pitch§12.27 voice → TTS pitch
cem-typography-voice-ssml-emphasis§12.27 voice → SSML emphasis
cem-typography-voice-ink-thickness-dark§12.3Dark theme overrides for voice ink-thickness (override-only)
cem-typography-voice-ink-thickness-contrast§12.3Contrast theme overrides for voice ink-thickness (override-only)
cem-typography-roles§12.477 semantic role endpoint tokens

Generator derivation rules:


11. Summary

This revision replaces the “typography as style” mindset with typography as semantics, and introduces voice as a cross-modal axis:

The result is a token set that stays understandable to consumers of the design system, stable across brand changes, and reusable across visual and vocal presentations.