Design System
Typography is the voice of the interface
Good type is invisible. It serves the reader without asking for attention. This is how the system composes at reading length.
Every interface is a sequence of typographic decisions. Size, weight, tracking, and rhythm do most of the work before color or layout ever get a turn. A system that gets type right feels calm; a system that gets it wrong feels noisy no matter how many other details are polished.
The scale here spans eleven sizes, from text-2xs for dense metadata up to text-jumbo for marketing moments. Most of your UI will live between text-xs and text-lg.
Hierarchy, not decoration
Headings exist to help the eye skim, not to announce importance. Use them sparingly, and let weight and size carry the structure. When every heading shouts, none of them do.
A few rules worth internalizing:
- Never skip levels: an
h2follows anh1, not anh3. - Keep measure tight: aim for 60 to 75 characters per line for body copy.
- Trust the scale: if a size doesn’t exist in the system, extend the scale rather than reach for an arbitrary value.
A typical workflow
When composing a new page, start from the outside in:
- Pick the page-level heading size first: this anchors everything else.
- Set the body size and line height to match the reading context.
- Fill in supporting sizes last: overlines, captions, metadata.
For inline emphasis, prefer semantic weight over italics. Links should feel like links: we use underlined accent color so they read clearly in both paragraphs and lists.
Typography has one plain duty before it and that is to convey information in writing.
Code in prose
Inline tokens like font-semibold or leading-reading sit comfortably mid-sentence. Longer examples get their own block:
export function Button({ children }: { children: React.ReactNode }) {
return (
<button className="h-8 px-4 rounded-lg bg-accent text-accent-foreground">
{children}
</button>
)
}The body font is Geist Sans and code uses Geist Mono. Both are variable fonts, so weights blend cleanly across the whole scale.
font-sans is a chain: your override, then Geist Sans, then the system UI font. Install nothing and you get Geist, free via the geistnpm package. That is the kit’s default and no component ships or requires a licensed typeface. This site sets no override at all, which is the point: every specimen on this page is the default, so what you are reading is exactly what a fresh install renders. Nothing here flatters the kit with a face you would not get. The kit also sets Geist’s letterforms for you, on body: single-storey a, straight l, straight-leg R, alternate i and j, and tabular figures so a number that ticks does not reflow the text beside it. Because font-feature-settings replaces rather than merges, re-declaring it on an element switches off everything you leave out. Two catches if you do swap the face. A font package built from Google’s sources is stripped of its OpenType features, so a face chosen for its alternates will quietly arrive without them. And those set numbers name specific alternates in Geist and mean something else in another family, so re-declare the list against your own font.
This specimen is itself rendered with the system’s tokens: no custom typography CSS. If a line looks off here, it’ll look off everywhere.