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.
TokenValue
--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.
TokenValue
--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.
TokenValue
--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.
SampleTokens
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 type prop on XDSHeading to apply display styling while preserving the correct HTML element: <XDSHeading level={1} type="display-1"> gives you display-1 styling with an <h1> tag, so screen readers see the correct document outline.
Usage
XDSHeading for document structure
tsx
import {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>
// Display type for hero/marketing headings — level sets the HTML element
<XDSHeading level={1} type="display-1">Hero Title</XDSHeading>
<XDSHeading level={2} type="display-2">$1.2M Revenue</XDSHeading>
// Override the accessibility level when visual ≠ document hierarchy
<XDSHeading level={2} accessibilityLevel={3}>
Sidebar Section
</XDSHeading>
XDSText for body, label, and display text
tsx
import {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 without heading semantics (data callouts, decorative)
<XDSText type="display-2">$1.2M Revenue</XDSText>
Customizing the type scale via defineTheme
tsx
import {defineTheme} from '@xds/core';
// Adjust the entire ramp holistically with base and ratio
const editorialTheme = defineTheme({
name: 'editorial',
typography: {
scale: { base: 16, ratio: 1.25 }, // airy / article feel
body: { 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
GuidancePractices
DoUse XDSHeading for document headings and XDSText for everything else — they apply the full type scale automatically.
DoAdjust 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).
DoUse display types with as="h1" (or h2/h3) when display text is a page heading — this preserves accessibility while giving you display-level sizing. Or better, use <XDSHeading level={1} type="display-1"> which handles both semantics and styling.
DoLet line-height snap to the 4px grid via the type scale — expandTypeScale computes leading automatically from base and ratio.
DoUse the supporting type for secondary information: timestamps, helper text, metadata, captions.
DoUse accessibilityLevel on XDSHeading when the visual hierarchy doesn’t match the document outline (e.g. sidebar or card headings).
Don'tSet font-size or line-height manually — use the semantic type scale tokens so the full ramp stays consistent and 4px-grid-aligned.
Don'tSkip heading levels (e.g. h1 → h3) — screen readers rely on an unbroken hierarchy. Use accessibilityLevel to decouple visual from semantic level.
Don'tUse display types for body content or in-page sections — they’re designed for hero/marketing/data-callout contexts only.
Don'tOverride individual size tokens (--font-size-lg) to "tweak" a heading — adjust base/ratio instead so proportions remain coherent across the entire scale.
Don'tUse raw numeric font-weight values (400, 600) — reference the semantic weight tokens so themes can remap them.