Typography
Two component families: headings (H1–H6, each rendering its semantic HTML tag) and body text (<Text variant>). Use these instead of raw HTML + Tailwind classes — that’s what keeps the spec honest across surfaces.
When to use
- Always. Any user-facing string that isn’t a button label, badge, or input value should be a
<Text>or a heading.
Don’t use: <h2 className="text-lg">. That’s the smell — replace it with <H3>.
Headings
The H1–H6 components are semantic — they render <h1> through <h6>. Pick by role on the page, not by the size you want.
H1 — page title (24px)
H2 — detail-page title (20px)
H3 — section heading (16px)
H4 — card title, form section (14px)
H5 — minor heading (12px)
H6 — minor heading (12px)
<H1>Page title (24px) — rare in admin</H1><H2>Detail page title (20px)</H2><H3>Section heading (16px) — most common</H3><H4>Card title (14px)</H4><H5>Minor heading (12px)</H5>| Component | HTML | Size | Use |
|---|---|---|---|
<H1> | <h1> | 24px | Page title (rare in admin) |
<H2> | <h2> | 20px | Detail-page title |
<H3> | <h3> | 16px | Section heading, page bar title |
<H4> | <h4> | 14px | Card title, form-section title |
<H5> | <h5> | 12px | Minor heading |
<H6> | <h6> | 12px | Minor heading |
Text variants
Body text has 4 tiers. Pick by role, not by aesthetic — the muted tier is for description-style copy, not “make it lighter for visual interest.”
<Text variant="body">Body — default 14px foreground.</Text><Text variant="muted" as="p">Descriptions, timestamps, metadata.</Text><Text variant="label" as="p">Non-form labels, table headers.</Text><Text variant="caption" as="p">Small metadata, helper text.</Text>| Variant | Size | Weight | Color | Use |
|---|---|---|---|---|
body | 14px | 400 | foreground | Default body text |
muted | 14px | 400 | muted-foreground | Descriptions, timestamps |
label | 12px | 500 | foreground | Non-form labels, table headers |
caption | 12px | 400 | muted-foreground | Small metadata, helper text |
Polymorphic via as and asChild
<Text> defaults to <span>. Use as for a different tag, or asChild to project styles onto a child element (e.g., wrap an <a>).
<Text as="p">A paragraph rendered with body styles.</Text>
<Text asChild variant="muted"> <a href="/path">A link rendered with muted styles</a></Text>API
Heading components (H1–H6)
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Project heading styles onto a child element via Radix Slot. |
className | string | — | Forwarded. |
...rest | HTMLAttributes | — | Native <h1>–<h6> attributes. |
Text
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "body" | "muted" | "label" | "caption" | "body" | Type tier — see table above. |
as | "span" | "p" | "div" | "span" | Underlying tag. |
asChild | boolean | false | Project Text styles onto a child element via Radix Slot. |
className | string | — | Forwarded. |
Design guidelines
✓ Do
- Pick the heading by role on the page (H3 = section heading), not by the pixel size you want.
- Use Text variant="muted" for any descriptive paragraph next to a heading.
- Use Text asChild to wrap an <a> when the link should look like body text, not a button.
✗ Don't
- Reach for <h2 className="text-lg">. The whole point of these primitives is to remove that decision from the call site.
- Add custom font sizes via className. The system has 6 heading sizes + 2 body sizes; that is the contract.
- Use the muted variant for visual variety. It only means "this is secondary content."
Accessibility
- Heading hierarchy on a page: one H1 (or zero — many admin surfaces don’t have one), one H2 for the page topic, H3 for sections, H4 inside.
- Don’t skip levels. Screen readers expose heading nesting.
- Color contrast is enforced by the underlying tokens — both
foregroundandmuted-foregroundmeet AA in light + dark.