Design tokens
Every color, font, and typography size this site uses — rendered live so the whole system stays visible to itself.
Design tokens are the atoms of a theme. Change one, and every component referencing it shifts together. This post renders every token defined in src/styles/global.css so the whole system is visible at once — and so I catch anything that drifts in either light or dark mode when I tune it.
Toggle the theme using the button in the nav (or let it follow your OS) and every swatch on this page flips with you. That’s the whole point of semantic tokens: the same names, different values per mode.
Colors
Every color is a CSS variable scoped to :root for light mode and .dark for dark mode. Components reference tokens by purpose, not by value, so the mode can swap underneath without touching a single component file.
Surfaces
Backgrounds and card panels. The primary background is what holds the page together; everything else is a layered variation on it.
—bg—bg-secondary—card-bg—tag-bgText
Three levels of reading emphasis. Primary for the content itself, secondary for captions and supporting copy, muted for mono meta labels and dates.
—textThe quick brown fox jumps over the lazy dog
—text-secondary
The quick brown fox jumps over the lazy dog
—text-mutedThe quick brown fox jumps over the lazy dog
—tag-textThe quick brown fox jumps over the lazy dog
Accent
The warm gold that defines the brand. --accent is the base; --accent-hover is the interactive state; --accent-light is a desaturated variant for underlines and subtle highlights.
—accent—accent-hover—accent-lightA sample of the accent in context:
Borders
Two border weights, from primary (for section dividers) to subtle (for card separators).
—border—border-lightTypography
Eight semantic text tokens. Each maps to both a font-size and a matching line-height via Tailwind 4’s @theme directive, so tuning the scale is a single-file change.
The rule: name by purpose, never by size. When I replaced every text-[Npx] in the codebase with these tokens, the home page got easier to reason about overnight.
text-lead
text-name
text-heading
The quick brown fox jumps over
text-card
The quick brown fox jumps over
text-body
The quick brown fox jumps over the lazy dog
text-caption
The quick brown fox jumps over the lazy dog
text-meta
text-micro
The quick brown fox jumps over the lazy dog
| Token | Size | Line height | Used for |
|---|---|---|---|
text-micro | 10px | 1.4 | Tag pills, type badges |
text-meta | 11px | 1.5 | Dates, mono meta labels |
text-caption | 14px | 1.55 | Card descriptions |
text-body | 16px | 1.65 | Main reading register |
text-card | 17px | 1.35 | Post and project card titles |
text-heading | 18px | 1.3 | Section headings |
text-name | 20px | 1.15 | Hero name |
text-lead | 22px | 1.5 | Hero tagline |
Font families
Two families, no more: a sans for reading and a mono for metadata, code, and anything that wants to feel “system-native.”
—font-sans
Geist — A clean, modern sans designed for interfaces. The quick brown fox jumps over the lazy dog. 0123456789
—font-mono
Geist Mono — A monospace for code and meta. The quick brown fox jumps over the lazy dog. 0123456789
Where these live
Every token in this post is defined in a single file:
src/styles/global.css
The typography scale lives in the @theme block at the top. The color palette lives in @layer base with two blocks — :root for light mode and .dark for dark mode. Components reference tokens via Tailwind utilities (text-body, bg-[var(--bg)], border-[var(--border)], etc.) so there are no hardcoded values anywhere in the component tree.
If a token looks wrong on this page in either mode, the fix goes into that one file and every component updates on the next reload.
This post intentionally has featured: false. It’s a reference, not a showcase — it shouldn’t pressure its way onto the home page’s featured list. Find it through /content or the design-system tag.