Typography
StableGeist for the interface and Geist Mono for labels, code and the wordmark, in the sizes the site and the kit really use.
Both fonts are variable (weights 100–900), loaded with font-display: swap, and free under the SIL Open Font License. Pages read at 15px, app interfaces at 13px, and mono labels at 11px with wide tracking. Headings tighten their tracking as they grow; numbers that line up use tabular figures.
Type scale
/** * The type scale as the site and the product kit use it, largest first. Each row is the real * class list; the size labels are pixels / line height (in pixels, or as a multiple of the size). * (The two largest steps grow with the preview's width here; on pages they use sm: instead of @3xl:.) */const scale = [ { name: 'Display', spec: '36 → 56 · 600 · −0.035em', where: 'Page heroes', className: 'text-4xl font-semibold tracking-[-0.035em] text-balance @3xl:text-[3.5rem] @3xl:leading-[1.02]', sample: 'Tools for AI coding agents', }, { name: 'Title', spec: '32 → 40 · 600 · −0.03em', where: 'Library and project titles', className: 'text-[2rem] leading-[1.1] font-semibold tracking-[-0.03em] @3xl:text-[2.5rem]', sample: 'Colour and type', },Usage
import '@rchursin/product.css' // brand tokens, fonts, the finishing layer and the app tokens<p className="font-mono text-[11px] font-medium tracking-[0.12em] text-fg-3 uppercase">On this page</p><h2 className="text-xl font-semibold tracking-[-0.02em] text-balance">Keyboard and focus</h2><p className="text-[15px]/7 text-pretty text-fg-2">Every control can be reached with Tab.</p><span className="tabular-nums">1:11:11</span>Examples
Two cuts
Geist for reading and interface; Geist Mono for code, labels, ids and the wordmark.
/** * One family in two cuts. Geist sets the interface; Geist Mono sets labels, code, numbers that * line up, and the wordmark (drawn from Geist Mono Medium). Both are variable fonts, 100–900. */const families = [ { name: 'Geist', utility: 'font-sans', use: 'Interface, headings, body text', className: 'font-sans', weights: [ { w: 400, cls: 'font-normal', label: 'Regular · body' }, { w: 500, cls: 'font-medium', label: 'Medium · labels, buttons' }, { w: 600, cls: 'font-semibold', label: 'Semibold · headings' }, ], }, { name: 'Geist Mono', utility: 'font-mono', use: 'Code, mono labels, ids, the wordmark',Mono labels and tabular numbers
Geist’s digits are proportional by default. Add tabular-nums wherever numbers stack or tick.
/** * Geist's digits are proportional by default (a 1 is about 60% as wide as a 0), so columns of * numbers wobble. tabular-nums switches on the font's tabular figures and the columns line up. * The column heads are the mono label: Geist Mono, 11px, +0.12em, uppercase, fg-3. */const sessions = [ { day: 'Mon', time: '1:11:11' }, { day: 'Tue', time: '0:48:08' }, { day: 'Wed', time: '2:01:17' }, { day: 'Thu', time: '0:30:00' },]
function Column({ title, numbers }: { title: string; numbers: string }) { return ( <table className="w-full text-[13px]"> <caption className="pb-2 text-left font-mono text-[11px] font-medium tracking-[0.12em] text-fg-3 uppercase">{title}</caption> <thead className="sr-only"> <tr> <th scope="col">Day</th> <th scope="col">Focus time</th>API
CSS
| Class or variable | What it does |
|---|---|
font-sans · --font-sans | Geist, then ui-sans-serif and system-ui. Set on <html>, so it is the default everywhere. |
font-mono · --font-mono | Geist Mono, then ui-monospace, SFMono-Regular and Menlo. Ligatures are off in code blocks. |
tabular-nums | Switches on Geist’s tabular figures (tnum). Use for times, counts, prices and table columns. |
text-balance · text-pretty | Balance headings so lines are even; pretty paragraphs so they never end on one word. |
Mono label | font-mono text-[11px] font-medium tracking-[0.12em] uppercase text-fg-3: eyebrows, table heads, group titles. |
Accessibility
- Body text on pages is 15px with a 28px line height; app text is 13px, the smallest size used for content people read in full.
- Uppercase is kept to short mono labels; tracking opens them up so letters stay distinct.
- The viewport never blocks pinch zoom, so any size can be enlarged.
- Fields use 16px text on phones (text-base sm:text-[13px]) so iOS does not zoom the page when they are focused.
Guidelines
Do
- Use semibold (600) for headings and medium (500) for labels and buttons.
- Tighten tracking on large headings (−0.02em to −0.035em) and widen it on small caps (+0.12em).
- Use tabular-nums anywhere numbers line up or change in place.
Don’t
- Don’t use Geist Mono for paragraphs; keep it to code, ids and labels.
- Don’t set body text below 13px or meta text below 11px.
- Don’t fake italics: Geist Mono has none, so code comments differ by tone instead.
In use
Related
- ColourTwelve semantic greys that switch with the theme, and six fixed brand values for the logo and brand panels.
- Code blockHighlighted code in five grey roles, with line numbers, picked-out lines, a Wrap toggle, Copy, and long files folded.
- Key capsKeyboard keys cut from the same metal as the buttons, and shortcuts written as key caps.
- SectionA page band with an eyebrow, an h2 and a lead, on a plain or sunken ground. SectionHeading is the heading block alone.
Source
The component’s own files, exactly as they run on this site.
/* RChursin_ brand tokens (from the brand kit) for Tailwind CSS v4. Every token becomes a utility: bg-ink, text-paper, bg-graphite, text-fog, rounded-tile, shadow-lift, drop-shadow-lift … Fonts are loaded with next/font in src/styles/fonts.ts and wired up in globals.css.
Brand rules: black and white only — greys are for UI surfaces and text. The logo is one colour at a time, never with gradients, glows or effects.*/
@theme { /* Colour */ --color-ink: #0a0a0a; /* primary black: logo, text on light, dark backgrounds */ --color-paper: #ffffff; /* primary white: logo on dark, light backgrounds */ --color-graphite: #171716; /* raised dark surface: cards, sidebars */ --color-board: #1f1f1e; /* presentation / canvas background */ --color-fog: #a3a3a3; /* secondary text on dark (≥ 4.5:1 on ink) */ --color-stone: #737373; /* secondary text on light (≥ 4.5:1 on paper) */
/* Shape */