Typography
Text in one of the type scale's looks, with the heading level chosen separately.
On this page
A design system built from tokens up.
Body text sets the reading size. Every size, weight and line height comes from a token.
Last updated October 2026.
import { Typography } from "@/components/Typography";
export function TypographyExample() {
return (
<>
<Typography variant="lead">A design system built from tokens up.</Typography>
<Typography>
Body text sets the reading size. Every size, weight and line height comes from a token.
</Typography>
<Typography variant="small" tone="muted">
Last updated October 2026.
</Typography>
</>
);
}Examples
Type scale
Display, 48px
Heading 1, 40px
Heading 2, 32px
Heading 3, 24px
Heading 4, 20px
Heading 5, 16px
Heading 6, 14px
Lead, 20px
Body, 16px
Small, 14px
Label, 12px
Code, inherits size
Level and look are separate
An h4 with the h2 look
An h5 with the label look
Look from the element
An h4, with the h4 look
A code element, with the code lookA small element, with the small lookLead text with strong text inside, at the lead's size
Tones
Default
Muted
Subtle
Accent
Accent 2
Success: saved
Warning: unsaved changes
Error: the email is missing
Measure
With measure: A comfortable line holds about 45 to 75 characters, so the eye finds the start of the next line without effort. Longer lines tire readers, and shorter ones break the rhythm of reading. The measure caps this paragraph at about 65 characters, whatever its size.
Without measure: A comfortable line holds about 45 to 75 characters, so the eye finds the start of the next line without effort. Longer lines tire readers, and shorter ones break the rhythm of reading. The measure caps this paragraph at about 65 characters, whatever its size.
Inline text
Press Tab to reach main. Web Content Accessibility Guidelines (WCAG) 2.2 was published on . Its reflow criterion asks for content without loss of information or functionality
.
Customized with component tokens
Custom label
.customLabel {
--component-typography-label-tracking: var(--type-tracking-body);
--component-typography-label-font-family: var(--type-family-body);
}Props
Any native prop not listed here goes to the root element unchanged.
Without variant, the look comes from the element: h1–h6 take the variant of the same name; code, kbd and samp take code; small takes small; span, strong, em, mark, s, abbr, cite, q and time take inherit; every other element takes body. An explicit variant wins. The heading level, and so the role, comes only from as. inherit and code set no size, so on h1–h6 the plain heading's size for that level shows through.
| Prop | Type | Default | Description |
|---|---|---|---|
as | "p" | "span" | "div" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "label" | "legend" | "strong" | "em" | "small" | "mark" | "s" | "code" | "kbd" | "samp" | "abbr" | "cite" | "q" | "time" | "blockquote" | "figcaption" | "caption" | "li" | "dt" | "dd" | "p" | The element, and so the heading level and role. |
variant | "display" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "lead" | "body" | "small" | "label" | "code" | "inherit" | from the element | The look only: family, size, weight, line height and tracking. |
tone | "default" | "muted" | "subtle" | "accent" | "accent-2" | "success" | "warning" | "error" | inherited | Text color, each from its own component token. |
measure | boolean | false | Caps the line length at about 65 characters. |
Component tokens
| Token | Copy | Description | Aliases | Light | Dark | Preview |
|---|---|---|---|---|---|---|
--component-typography-display-font-family | Display text font. | type.family.heading | 'Space Grotesk Variable', 'Space Grotesk Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.display | 'Space Grotesk Variable', 'Space Grotesk Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.display | ||
--component-typography-display-font-size | Display text size. | type.size.display | 3remfrom type.font-size.4xl | 3remfrom type.font-size.4xl | ||
--component-typography-display-font-weight | Display text weight. | type.weight.heading | 700from type.font-weight.700 | 700from type.font-weight.700 | ||
--component-typography-display-line-height | Display text line height. | type.leading.heading | 1.2from type.line-height.tight | 1.2from type.line-height.tight | ||
--component-typography-display-tracking | Display text letter spacing. | type.tracking.display | -0.02emfrom type.letter-spacing.neg-2 | -0.02emfrom type.letter-spacing.neg-2 | ||
--component-typography-h1-font-family | Heading 1 look font. | type.family.heading | 'Space Grotesk Variable', 'Space Grotesk Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.display | 'Space Grotesk Variable', 'Space Grotesk Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.display | ||
--component-typography-h1-font-size | Heading 1 look size. | type.size.h1 | 2.5remfrom type.font-size.3xl | 2.5remfrom type.font-size.3xl | ||
--component-typography-h1-font-weight | Heading 1 look weight. | type.weight.heading | 700from type.font-weight.700 | 700from type.font-weight.700 | ||
--component-typography-h1-line-height | Heading 1 look line height. | type.leading.heading | 1.2from type.line-height.tight | 1.2from type.line-height.tight | ||
--component-typography-h1-tracking | Heading 1 look letter spacing. | type.tracking.display | -0.02emfrom type.letter-spacing.neg-2 | -0.02emfrom type.letter-spacing.neg-2 | ||
--component-typography-h2-font-family | Heading 2 look font. | type.family.heading | 'Space Grotesk Variable', 'Space Grotesk Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.display | 'Space Grotesk Variable', 'Space Grotesk Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.display | ||
--component-typography-h2-font-size | Heading 2 look size. | type.size.h2 | 2remfrom type.font-size.2xl | 2remfrom type.font-size.2xl | ||
--component-typography-h2-font-weight | Heading 2 look weight. | type.weight.heading | 700from type.font-weight.700 | 700from type.font-weight.700 | ||
--component-typography-h2-line-height | Heading 2 look line height. | type.leading.heading | 1.2from type.line-height.tight | 1.2from type.line-height.tight | ||
--component-typography-h2-tracking | Heading 2 look letter spacing. | type.tracking.display | -0.02emfrom type.letter-spacing.neg-2 | -0.02emfrom type.letter-spacing.neg-2 | ||
--component-typography-h3-font-family | Heading 3 look font. | type.family.heading | 'Space Grotesk Variable', 'Space Grotesk Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.display | 'Space Grotesk Variable', 'Space Grotesk Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.display | ||
--component-typography-h3-font-size | Heading 3 look size. | type.size.h3 | 1.5remfrom type.font-size.xl | 1.5remfrom type.font-size.xl | ||
--component-typography-h3-font-weight | Heading 3 look weight. | type.weight.heading | 700from type.font-weight.700 | 700from type.font-weight.700 | ||
--component-typography-h3-line-height | Heading 3 look line height. | type.leading.heading | 1.2from type.line-height.tight | 1.2from type.line-height.tight | ||
--component-typography-h3-tracking | Heading 3 look letter spacing. | type.tracking.body | 0emfrom type.letter-spacing.0 | 0emfrom type.letter-spacing.0 | ||
--component-typography-h4-font-family | Heading 4 look font. | type.family.heading | 'Space Grotesk Variable', 'Space Grotesk Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.display | 'Space Grotesk Variable', 'Space Grotesk Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.display | ||
--component-typography-h4-font-size | Heading 4 look size. | type.size.h4 | 1.25remfrom type.font-size.lg | 1.25remfrom type.font-size.lg | ||
--component-typography-h4-font-weight | Heading 4 look weight. | type.weight.heading | 700from type.font-weight.700 | 700from type.font-weight.700 | ||
--component-typography-h4-line-height | Heading 4 look line height. | type.leading.heading | 1.2from type.line-height.tight | 1.2from type.line-height.tight | ||
--component-typography-h4-tracking | Heading 4 look letter spacing. | type.tracking.body | 0emfrom type.letter-spacing.0 | 0emfrom type.letter-spacing.0 | ||
--component-typography-h5-font-family | Heading 5 look font. | type.family.heading | 'Space Grotesk Variable', 'Space Grotesk Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.display | 'Space Grotesk Variable', 'Space Grotesk Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.display | ||
--component-typography-h5-font-size | Heading 5 look size. | type.size.h5 | 1remfrom type.font-size.md | 1remfrom type.font-size.md | ||
--component-typography-h5-font-weight | Heading 5 look weight. | type.weight.heading | 700from type.font-weight.700 | 700from type.font-weight.700 | ||
--component-typography-h5-line-height | Heading 5 look line height. | type.leading.heading | 1.2from type.line-height.tight | 1.2from type.line-height.tight | ||
--component-typography-h5-tracking | Heading 5 look letter spacing. | type.tracking.body | 0emfrom type.letter-spacing.0 | 0emfrom type.letter-spacing.0 | ||
--component-typography-h6-font-family | Heading 6 look font. | type.family.heading | 'Space Grotesk Variable', 'Space Grotesk Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.display | 'Space Grotesk Variable', 'Space Grotesk Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.display | ||
--component-typography-h6-font-size | Heading 6 look size. | type.size.h6 | 0.875remfrom type.font-size.sm | 0.875remfrom type.font-size.sm | ||
--component-typography-h6-font-weight | Heading 6 look weight. | type.weight.heading | 700from type.font-weight.700 | 700from type.font-weight.700 | ||
--component-typography-h6-line-height | Heading 6 look line height. | type.leading.heading | 1.2from type.line-height.tight | 1.2from type.line-height.tight | ||
--component-typography-h6-tracking | Heading 6 look letter spacing. | type.tracking.body | 0emfrom type.letter-spacing.0 | 0emfrom type.letter-spacing.0 | ||
--component-typography-lead-font-family | Lead text font. | type.family.body | 'Inter Variable', 'Inter Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.sans | 'Inter Variable', 'Inter Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.sans | ||
--component-typography-lead-font-size | Lead text size. | type.size.lead | 1.25remfrom type.font-size.lg | 1.25remfrom type.font-size.lg | ||
--component-typography-lead-font-weight | Lead text weight. | type.weight.body | 400from type.font-weight.400 | 400from type.font-weight.400 | ||
--component-typography-lead-line-height | Lead text line height. | type.leading.body | 1.6from type.line-height.normal | 1.6from type.line-height.normal | ||
--component-typography-lead-tracking | Lead text letter spacing. | type.tracking.body | 0emfrom type.letter-spacing.0 | 0emfrom type.letter-spacing.0 | ||
--component-typography-body-font-family | Body text font. | type.family.body | 'Inter Variable', 'Inter Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.sans | 'Inter Variable', 'Inter Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.sans | ||
--component-typography-body-font-size | Body text size. | type.size.body | 1remfrom type.font-size.md | 1remfrom type.font-size.md | ||
--component-typography-body-font-weight | Body text weight. | type.weight.body | 400from type.font-weight.400 | 400from type.font-weight.400 | ||
--component-typography-body-line-height | Body text line height. | type.leading.body | 1.6from type.line-height.normal | 1.6from type.line-height.normal | ||
--component-typography-body-tracking | Body text letter spacing. | type.tracking.body | 0emfrom type.letter-spacing.0 | 0emfrom type.letter-spacing.0 | ||
--component-typography-small-font-family | Small text font. | type.family.body | 'Inter Variable', 'Inter Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.sans | 'Inter Variable', 'Inter Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-seriffrom type.font-stack.sans | ||
--component-typography-small-font-size | Small text size. | type.size.small | 0.875remfrom type.font-size.sm | 0.875remfrom type.font-size.sm | ||
--component-typography-small-font-weight | Small text weight. | type.weight.body | 400from type.font-weight.400 | 400from type.font-weight.400 | ||
--component-typography-small-line-height | Small text line height. | type.leading.body | 1.6from type.line-height.normal | 1.6from type.line-height.normal | ||
--component-typography-small-tracking | Small text letter spacing. | type.tracking.body | 0emfrom type.letter-spacing.0 | 0emfrom type.letter-spacing.0 | ||
--component-typography-label-font-family | Label text font. | type.family.label | 'Fira Code Variable', 'Fira Code Fallback', ui-monospace, SFMono-Regular, Menlo, Consolas, monospacefrom type.font-stack.mono | 'Fira Code Variable', 'Fira Code Fallback', ui-monospace, SFMono-Regular, Menlo, Consolas, monospacefrom type.font-stack.mono | ||
--component-typography-label-font-size | Label text size. | type.size.label | 0.75remfrom type.font-size.xs | 0.75remfrom type.font-size.xs | ||
--component-typography-label-font-weight | Label text weight. | type.weight.body | 400from type.font-weight.400 | 400from type.font-weight.400 | ||
--component-typography-label-line-height | Label text line height. | type.leading.body | 1.6from type.line-height.normal | 1.6from type.line-height.normal | ||
--component-typography-label-tracking | Label text letter spacing. | type.tracking.label | 0.16emfrom type.letter-spacing.16 | 0.16emfrom type.letter-spacing.16 | ||
--component-typography-code-font-family | Code font. Size, weight and line height come from the surrounding text. | type.family.code | 'Fira Code Variable', 'Fira Code Fallback', ui-monospace, SFMono-Regular, Menlo, Consolas, monospacefrom type.font-stack.mono | 'Fira Code Variable', 'Fira Code Fallback', ui-monospace, SFMono-Regular, Menlo, Consolas, monospacefrom type.font-stack.mono | ||
--component-typography-tone-default-color | Default text color. | color.text.default | #17202bfrom color.gray.900 | #fffffffrom color.gray.0 | ||
--component-typography-tone-muted-color | Muted text color. | color.text.muted | #455464from color.gray.700 | #e6ecf3from color.slate.100 | ||
--component-typography-tone-subtle-color | Subtle text color. Not checked against glass. | color.text.subtle | #5d6876from color.gray.600 | #d3dce6from color.slate.200 | ||
--component-typography-tone-accent-color | Accent text color. Not checked against glass. | color.accent.text | #005f7bfrom color.cyan.700 | #54d8f4from color.cyan.300 | ||
--component-typography-tone-accent-2-color | Second accent (violet) text color. Not checked against glass. | color.accent-2.text | #5b3fa6from color.violet.600 | #c9b8f5from color.violet.300 | ||
--component-typography-tone-success-color | Success text color. Never the only cue: the wording must state the status (WCAG 1.4.1). Not checked against glass. | color.success.text | #1e6b45from color.green.700 | #8fd6a8from color.green.300 | ||
--component-typography-tone-warning-color | Warning text color. Never the only cue: the wording must state the status (WCAG 1.4.1). Not checked against glass. | color.warning.text | #94440afrom color.orange.700 | #f7ad7cfrom color.orange.300 | ||
--component-typography-tone-error-color | Error text color. Never the only cue: the wording must state the status (WCAG 1.4.1). Not checked against glass. | color.error.text | #b3261efrom color.red.700 | #f4a097from color.red.300 | ||
--component-typography-measure | Maximum line length with `measure`: about 65 characters in the element's own font. | type.measure.text | 65chfrom type.line-length.65 | 65chfrom type.line-length.65 | No preview |
Accessibility
- The level comes from as and the look from variant, so keep the page outline logical whatever the size.
- success, warning and error carry meaning, so the wording must state the status on its own (WCAG 1.4.1); the color is an extra.
- On glass, use only the default or muted tone: the others aren't contrast-checked there.
- Don't set tone on text inside a Link, or on mark: those pairings aren't contrast-checked, and a recolored link may lose what sets it apart.
- The label look's uppercase is CSS only, so write the source text in normal case: some screen readers spell out words typed in capitals.
- abbr's title isn't reliably announced or shown, so spell the abbreviation out on first use.
- Sizes are in rem, so text follows the browser's text size and zoom.
- Long words break instead of overflowing in block layout, so nothing scrolls sideways at 320 CSS px (WCAG 1.4.10). In a flex or grid item, also let it shrink (min-inline-size: 0).