Typography components — tokens in a convenient wrapper
Font tokens from Module 2 need to reach the product through stable components: Heading and Text (sometimes Label, Caption, Code). The goal is a shared rhythm and correct HTML semantics — not “a pretty div with font-size.”
Separation: visual style ≠ HTML level
A common mistake: <h1 className="text-sm"> for a sidebar, or <p className="text-4xl"> instead of a heading.
The right contract:
as/level— which tag is in the DOM (h1–h6,p,span).variant/size— how it looks (display, title, body, caption).
// Looks like title-md, semantically h2
<Heading as="h2" size="md">Profile settings</Heading>
// Large marketing heading, but the page already has an h1
<Heading as="h2" size="display">Skill Maps</Heading>
A page usually has one h1. Heading hierarchy should be logical (do not jump from h2 to h5 without h3).
Heading
const headingSizes = {
display: { fontSize: '3rem', lineHeight: 1.1, fontWeight: 700 },
lg: { fontSize: '2rem', lineHeight: 1.2, fontWeight: 700 },
md: { fontSize: '1.5rem', lineHeight: 1.3, fontWeight: 600 },
sm: { fontSize: '1.25rem', lineHeight: 1.4, fontWeight: 600 },
};
function Heading({ as: Comp = 'h2', size = 'md', className, children, ...props }) {
return (
<Comp className={cn(headingVariants({ size }), className)} {...props}>
{children}
</Comp>
);
}
Text
For paragraphs, captions, secondary copy:
<Text size="md">Main paragraph</Text>
<Text size="sm" tone="muted">Secondary hint</Text>
<Text as="span" size="sm" weight="medium">Inline emphasis</Text>
Useful props: tone (default / muted / danger / success), weight, align, truncate (use carefully — truncation hurts a11y if the full text is unavailable).
Responsive type
Two workable approaches:
- Fluid —
clamp():
.text-display {
font-size: clamp(1.75rem, 1.2rem + 2vw, 3rem);
}
- Stepped — different sizes at breakpoints:
.heading-lg {
font-size: var(--font-size-xl);
}
@media (min-width: 768px) {
.heading-lg {
font-size: var(--font-size-2xl);
}
}
In the DS, lock one approach and document the scale. Do not mix “magic” px on every screen.
Semantics and accessibility
- Headings structure the document — they are not “make it bolder.”
- Long copy —
p; short UI phrases — oftenspaninside buttons/badges. line-heightand measure (45–75 characters) affect readability more than picking a “trendy” font.- Users may enlarge text: avoid hard
heighton text blocks. - Body text contrast ≥ 4.5:1; large text ≥ 3:1.
Connection to tokens
Components only consume tokens:
// tokens/typography.js
export const fontSize = {
xs: '0.75rem',
sm: '0.875rem',
md: '1rem',
lg: '1.125rem',
xl: '1.25rem',
'2xl': '1.5rem',
};
Do not hardcode 14px inside fifteen components — otherwise a brand change becomes archaeology.
Practice
- Implement
HeadingandTextwithas+ visual sizes. - Build a sample page: h1 → section h2s → body + caption.
- Check the heading tree in the DevTools Accessibility panel.
- Add stories for all size/tone combinations.