TypographyFont families, geometric type scale, weight, line-height, and semantic text tokens for consistent, accessible text styling.
OverviewXDS typography is built on a geometric type scale: base size × ratio^step, with 14px and 1.2 as defaults. Every text style is a semantic token that composes font size, weight, and line-height — so components express intent (heading, body, label) rather than raw values.Two layers work together: raw size tokens (--font-size-xs … --font-size-5xl) form the geometric scale, and semantic type scale tokens (--text-heading-1-size, --text-body-leading, etc.) reference them by var(). Themes override the entire scale by adjusting base and ratio in defineTheme — all semantic tokens recompute automatically.
Font FamiliesThree font roles: body (UI text), heading (titles and headings), and code (monospace). By default, body and heading share the same system font stack; code uses a monospace stack. Custom themes can assign different families per role — heading inherits from body when not explicitly set.
| Token | Value |
|---|---|
| --font-family-body | -apple-systemThe quick brown fox jumps over the lazy dog |
| --font-family-code | "SF Mono"The quick brown fox jumps over the lazy dog |
| --font-family-heading | -apple-systemThe quick brown fox jumps over the lazy dog |
Font SizesGeometric scale: round(base × ratio^step), expressed in rem. The default scale is 14px × 1.2, producing 12 steps from 4xs (6px) to 5xl (42px). Adjusting base and ratio in defineTheme regenerates every size token while preserving proportional relationships.
| Token | Value |
|---|---|
| --font-size-4xs | 0.375remThe quick brown fox jumps over the lazy dog |
| --font-size-3xs | 0.4375remThe quick brown fox jumps over the lazy dog |
| --font-size-2xs | 0.5remThe quick brown fox jumps over the lazy dog |
| --font-size-xs | 0.625remThe quick brown fox jumps over the lazy dog |
| --font-size-sm | 0.75remThe quick brown fox jumps over the lazy dog |
| --font-size-base | 0.875remThe quick brown fox jumps over the lazy dog |
| --font-size-lg | 1.0625remThe quick brown fox jumps over the lazy dog |
| --font-size-xl | 1.25remThe quick brown fox jumps over the lazy dog |
| --font-size-2xl | 1.5remThe quick brown fox jumps over the lazy dog |
| --font-size-3xl | 1.8125remThe quick brown fox jumps over the lazy dog |
| --font-size-4xl | 2.1875remThe quick brown fox jumps over the lazy dog |
| --font-size-5xl | 2.625remThe quick brown fox jumps over the lazy dog |
Font WeightsFour semantic weights: normal (400, body/code), medium (500, labels/data), semibold (600, headings/titles), bold (700, strong emphasis). Type scale tokens reference these by var() so themes can remap numeric values.
| Token | Value |
|---|---|
| --font-weight-normal | 400The quick brown fox jumps over the lazy dog |
| --font-weight-medium | 500The quick brown fox jumps over the lazy dog |
| --font-weight-semibold | 600The quick brown fox jumps over the lazy dog |
| --font-weight-bold | 700The quick brown fox jumps over the lazy dog |
Line HeightLine heights are computed from a tiered target ratio and snapped to a 4px vertical grid. Small text (<20px) targets 1.5, medium text (20–31px) targets 1.4, and large text (≥32px) targets 1.25. A minimum gap of fontSize + 4px is enforced. The result is a unitless ratio stored in each --text-*-leading token.The 4px grid is critical: every line box aligns to 4px increments, which keeps baselines, spacing, and component heights predictable. The expandTypeScale utility computes these automatically when you provide a base and ratio — you should never need to set line-height manually.
Type ScaleSemantic tokens that combine size, weight, and line-height into a single type style. Each token triplet (--text-*-size, --text-*-weight, --text-*-leading) is consumed by XDSText and XDSHeading. Use the component props rather than composing raw font tokens.
| Sample | Tokens |
|---|---|
| H1 | var(--font-size-2xl) · -apple-systemvar(--font-weight-semibold) · 1.3333 |
| H2 | var(--font-size-xl) · -apple-systemvar(--font-weight-semibold) · 1.4 |
| H3 | var(--font-size-lg) · -apple-systemvar(--font-weight-semibold) · 1.4118 |
| H4 | var(--font-size-base) · -apple-systemvar(--font-weight-semibold) · 1.4286 |
| H5 | var(--font-size-sm) · -apple-systemvar(--font-weight-semibold) · 1.6667 |
| H6 | var(--font-size-xs) · -apple-systemvar(--font-weight-semibold) · 1.6 |
| Display 1 | var(--font-size-5xl) · -apple-systemvar(--font-weight-normal) · 1.2381 |
| Display 2 | var(--font-size-4xl) · -apple-systemvar(--font-weight-normal) · 1.2571 |
| Display 3 | var(--font-size-3xl) · -apple-systemvar(--font-weight-normal) · 1.3793 |
| Large | var(--font-size-lg) · -apple-systemvar(--font-weight-semibold) · 1.4118 |
| Body | var(--font-size-base) · -apple-systemvar(--font-weight-normal) · 1.4286 |
| Label | var(--font-size-base) · -apple-systemvar(--font-weight-medium) · 1.4286 |
| Code | var(--font-size-base) · "SF Mono"var(--font-weight-normal) · 1.4286 |
| Supporting | var(--font-size-sm) · -apple-systemvar(--font-weight-normal) · 1.6667 |
Display TextDisplay variants (display-1, display-2, display-3) continue the geometric progression above heading-1, at steps +6, +5, and +4. They use normal weight (400) instead of semibold, and tighter line-heights (~1.2) — large text reads better with less leading. Use display types for hero banners, marketing headlines, and data callouts — not for document headings.Display text often needs heading semantics for accessibility. Use the as prop on XDSText to render the correct HTML element: <XDSText type="display-1" as="h1"> gives you display-1 styling with an <h1> tag, so screen readers see the correct document outline.
Usage
XDSHeading for document structure
tsximport {XDSHeading} from '@xds/core';// Heading levels map to semantic tokens: level 1 → --text-heading-1-*<XDSHeading level={1}>Page Title</XDSHeading><XDSHeading level={2}>Section</XDSHeading><XDSHeading level={3}>Subsection</XDSHeading>// Override the accessibility level when visual ≠ document hierarchy<XDSHeading level={2} accessibilityLevel={3}>Sidebar Section</XDSHeading>
XDSText for body, label, and display text
tsximport {XDSText} from '@xds/core';<XDSText type="body">Body text at the base scale.</XDSText><XDSText type="large">Emphasized body text.</XDSText><XDSText type="label">Form label</XDSText><XDSText type="supporting">Helper text, timestamps, metadata.</XDSText><XDSText type="code">{'const x = 1;'}</XDSText>// Display with heading semantics for accessibility<XDSText type="display-1" as="h1">Hero Title</XDSText><XDSText type="display-2" as="h2">$1.2M Revenue</XDSText>
Customizing the type scale via defineTheme
tsximport {defineTheme} from '@xds/core';// Adjust the entire ramp holistically with base and ratioconst editorialTheme = defineTheme({name: 'editorial',typography: {scale: { base: 16, ratio: 1.25 }, // airy / article feelbody: { family: 'Geist', fallbacks: '-apple-system, sans-serif' },heading: { weight: 'bold' },code: { family: 'Geist Mono', fallbacks: '"SF Mono", monospace' },},});const denseTheme = defineTheme({name: 'dense',typography: {scale: { base: 12, ratio: 1.125 }, // compact / data-dense UI},});
Best Practices
| Guidance | Practices |
|---|---|
| Do | Use XDSHeading for document headings and XDSText for everything else — they apply the full type scale automatically. |
| Do | Adjust typography holistically: change base and ratio in defineTheme to shift the entire ramp (e.g. { base: 16, ratio: 1.25 } for editorial, { base: 12, ratio: 1.125 } for dense UI). |
| Do | Use display types with as="h1" (or h2/h3) when display text is a page heading — this preserves accessibility while giving you display-level sizing. |
| Do | Let line-height snap to the 4px grid via the type scale — expandTypeScale computes leading automatically from base and ratio. |
| Do | Use the supporting type for secondary information: timestamps, helper text, metadata, captions. |
| Do | Use accessibilityLevel on XDSHeading when the visual hierarchy doesn’t match the document outline (e.g. sidebar or card headings). |
| Don't | Set font-size or line-height manually — use the semantic type scale tokens so the full ramp stays consistent and 4px-grid-aligned. |
| Don't | Skip heading levels (e.g. h1 → h3) — screen readers rely on an unbroken hierarchy. Use accessibilityLevel to decouple visual from semantic level. |
| Don't | Use display types for body content or in-page sections — they’re designed for hero/marketing/data-callout contexts only. |
| Don't | Override individual size tokens (--font-size-lg) to "tweak" a heading — adjust base/ratio instead so proportions remain coherent across the entire scale. |
| Don't | Use raw numeric font-weight values (400, 600) — reference the semantic weight tokens so themes can remap them. |