For AI agents and crawlers: a structured index of this site is available at https://danny.is/llms.txt.

Foundations

Site styleguide

This page is the primary reference for the site's foundational design system. It includes the colour system, the design tokens for spacing, type, borders, radii and shadows, and the global utility classes.

Colour System

The colour system is rooted in a set of foundational colours, some of which are adaptive and appear differently in light and dark modes (via light-dark()), and some of which are absolute and appear the same in both modes. These are used to create a set of semantic role-based tokens that are primarily used across the site. All colours are defined in OKLCH. The adaptive colours use lower lightness and higher chroma in light mode and have increased lightness and lower chroma in dark mode.

White
Black
Ink
Charcoal
Beige

light mode

Coral
Pink
Orange
Purple
Yellow
Green
Blue

dark mode

Coral
Pink
Orange
Purple
Yellow
Green
Blue

The table below shows the semantic colours currently in use. Components should use these (or derivatives of them via CSS functions like color-mix()) wherever their use case fits their semantic role. In situations where a foundational colour is more semantic (eg a "blue callout") then use the foundational colour instead. I deliberately try to keep these semantic colours to a minimum to avoid ending up with a large unwieldy palette of very specific colours that are only used in one place.

Token Light Dark Variable Usage
Accent & interaction
Accent
--color-accent The primary brand colour, used in many places including for links
Visited
--color-visited Visited links, because purple is an affordance people are used to
Highlight
--color-highlight Highlighted text and occasionally other forms of highlight
Focus ring
--color-focus-ring The focus ring for interactive elements
Backgrounds & surfaces
Primary background
--color-background The primary background colour
Secondary background
--color-background-secondary A secondary background colour for things like cards and panels
Code background
--color-background-code Background for inline code and some other code-like elements
Text & borders
Text
--color-text Body text
Text secondary
--color-text-secondary Muted or secondary text
Border
--color-border Subtle borders and dividers

Design tokens

As with colours, I've tried to keep the number of generic design tokens to a minimum, while still providing a wide enough set of options for most use cases.

Borders, radii and shadows

Five CSS custom properties are available for setting border widths and radii. Border widths are set in pixels so they don't scale with the base font size while radii are mostly set in rems so they do.

Variable Value Preview Notes
--border-width-hairline max(0.0625rem, 1px)
1px at the default root size but never falls to a sub-pixel when scaling up with the root font size
--border-width-base 2px
Standard border width for most elements
--border-width-thick 4px
Usually only applied to one edge of an element
--border-width-heavy 6px
Usually only applied to one edge of an element
--border-width-accent 1rem
Used as a design element, so scales with the root font size
Variable Value Preview Notes
--radius-xs 0.125rem
Very subtle radius for softening corners slightly
--radius-sm 0.25rem
Standard radius for most rounded elements
--radius-md 0.5rem
--radius-lg 0.75rem
--radius-full calc(infinity * 1px)
Infinite radius for fully rounding into a pill or circle regardless of size.

I don't use shadows heavily but there are two custom properties available if I need to create depth and elevation.

Variable Preview
--shadow-small
--shadow-medium

Typography & Spacing Tokens

Typography and spacing design tokens provide consistent rhythm and visual hierarchy across the site.

Font Families

Typography on this site serves five distinct usage contexts, covered by four font stacks. Short-form prose and UI elements use Figtree (--font-ui) which works well for both use cases and is the default face. Long-form prose uses Literata (--font-prose), while "display" type uses Geist (--font-display). Code examples and other monospace fonts use a version of Fira Code.

Display --font-display

Geist

For large, presentational type, especially when used as a design element. Almost always used in all caps.

The quick brown fox jumps over the lazy dog

Mollit amet velit reprehenderit. Proident aliqua officia nisi officia sint sint elit commodo pariatur voluptate nisi duis occaecat mollit. Dolor sint id velit ipsum excepteur pariatur adipisicing. Irure laborum ea nulla esse aute aliquip dolor reprehenderit sit laborum consequat esse ad. Duis pariatur laborum ut veniam exercitation in ut. Ullamco irure Lorem sit consequat adipisicing do fugiat occaecat consectetur commodo pariatur amet ut amet.

Long-form prose --font-prose

Literata

Used for 'bookish' long-form prose, mostly in articles.

The quick brown fox jumps over the lazy dog

Mollit amet velit reprehenderit. Proident aliqua officia nisi officia sint sint elit commodo pariatur voluptate nisi duis occaecat mollit. Dolor sint id velit ipsum excepteur pariatur adipisicing. Irure laborum ea nulla esse aute aliquip dolor reprehenderit sit laborum consequat esse ad. Duis pariatur laborum ut veniam exercitation in ut. Ullamco irure Lorem sit consequat adipisicing do fugiat occaecat consectetur commodo pariatur amet ut amet.

Short-form prose & UI --font-ui

Figtree

The default face for UI and standard prose.

The quick brown fox jumps over the lazy dog

Mollit amet velit reprehenderit. Proident aliqua officia nisi officia sint sint elit commodo pariatur voluptate nisi duis occaecat mollit. Dolor sint id velit ipsum excepteur pariatur adipisicing. Irure laborum ea nulla esse aute aliquip dolor reprehenderit sit laborum consequat esse ad. Duis pariatur laborum ut veniam exercitation in ut. Ullamco irure Lorem sit consequat adipisicing do fugiat occaecat consectetur commodo pariatur amet ut amet.

Code --font-code

Fira Code

Code and occasional stylistic monospace.

The quick brown fox jumps over the lazy dog

Mollit amet velit reprehenderit. Proident aliqua officia nisi officia sint sint elit commodo pariatur voluptate nisi duis occaecat mollit. Dolor sint id velit ipsum excepteur pariatur adipisicing. Irure laborum ea nulla esse aute aliquip dolor reprehenderit sit laborum consequat esse ad. Duis pariatur laborum ut veniam exercitation in ut. Ullamco irure Lorem sit consequat adipisicing do fugiat occaecat consectetur commodo pariatur amet ut amet.

Typographic contexts

Most of the site renders with prose-style typography by default, set in Figtree. Individual areas can opt in or out of that baseline. These four contexts show up as tabs throughout the rest of the styleguide, so you can preview an element or component in each:

  • Default (flow) — the global prose-style defaults plus the .flow utility for vertical rhythm between block-level elements.
  • Long form Prose — wrapped in <LongFormProseTypography>, which opts in to Literata and the bookish enhancements (oldstyle numerals, tighter optical sizing) used in articles.
  • Default (no flow) — the global prose-style defaults on their own, with no wrapper.
  • UI Style — the .ui-style utility, which opts out of prose styling (no underlines, no heading borders, no list markers) for navigation, cards and other interface areas.

Font Sizes

The base type is generated with Utopia and uses clamp() to appropriately scale with the viewport width. While the CSS variables below are used in most places, I also use ems in certain contexts where I want text sized relative to that of its parent context.

Preview Variable Notes
Aa --font-size-xs Occasional use in captions, labels, fine print etc
Aa --font-size-sm Used for secondary text, metadata and anywhere else I want smaller copy
Aa --font-size-base Default for all body text and H4–6
Aa --font-size-md Anywhere we want slightly larger copy. Default for H3
Aa --font-size-lg Default for H2
Aa --font-size-xl Default for H1
Aa --font-size-2xl Used for large display copy and some page titles
Aa --font-size-3xl Used for large display copy

Spacing Scale

The spacing scale is generated with Utopia using the same base values as the type scale. Using the custom properties below to set margins, padding and the like will ensure they scale in concert with the fluid type.

Variable Preview
--space-3xs
--space-2xs
--space-xs
--space-s
--space-m
--space-l
--space-xl
--space-2xl
--space-3xl

Font Weights

All four families are variable fonts and therefore not limited to the weights defined below (Geist: 100-900, Figtree: 300-900, Literata: 200-900, Fira Code: 300-700). These design tokens provide common stops to help keep the weight scale consistent across the site, but it's fine to use intermediate weights when needed.

Literata Geist Figtree Variable Value
Aa Aa Aa --font-weight-light 300
Aa Aa Aa --font-weight-normal 350
Aa Aa Aa --font-weight-regular 400
Aa Aa Aa --font-weight-medium 500
Aa Aa Aa --font-weight-semibold 600
Aa Aa Aa --font-weight-bold 700
Aa Aa Aa --font-weight-extrabold 800
Aa Aa Aa --font-weight-heavy 900

Leading & Tracking

Line height and letter-spacing can be controlled via --leading-* and --tracking-* CSS custom properties.

Variable Value Preview
--leading-none 0.9

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.

--leading-tight 1.1

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.

--leading-snug 1.2

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.

--leading-normal 1.55

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.

--leading-loose 1.7

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.

Variable Value Preview
--tracking-tight -0.02ch Hello there
--tracking-normal 0 Hello there
--tracking-wide 0.05ch Hello there
--tracking-wider 0.1ch Hello there

Measure

A single token, --measure-standard (70ch), sets the standard measure — the maximum line length for comfortable reading, around 70 characters. I use it to cap the width of prose columns and most content areas.

Utility Classes

In addition to the foundational custom properties above, there are a few global utility classes available.

Class Purpose
.flow Adds consistent vertical rhythm between block-level children — catch-all margins for elements not covered globally, and tighter spacing between adjacent headings. Applied to most prose containers like articles and notes. Margins are em-based, so they scale with font size.
.ui-style Opt-out of default styles meant for prose-like content. Will remove margins, padding etc. Think of this like a mini "reset".
.dark-surface Forces dark background with light text regardless of theme. Used for always-dark areas like the footer.
.surface-white Sets a white surface context in light mode (or dark surface in dark mode) and redefines --color-background-secondary for children to ensure proper contrast. Used for some card-like components.
.cq Establishes container query context (container-type: inline-size).
.all-caps Uppercase text with wide letter-spacing.
.center, .right, .left Text alignment utilities. Useful for table cells and other contexts.
.top, .middle, .bottom Vertical alignment utilities. Useful for table cells and inline elements.
.content-trim Removes top margin from first child and bottom margin from last child. Use inside padded containers with slotted content.
.img-cover Makes image fill container with object-fit: cover.
.sr-only Visually hidden but accessible to screen readers. Also available as .hidden-microformat for hiding microformat metadata.
.external-arrow Subtle arrow indicator for external/offsite links in UI contexts.
.list-reset Removes list styling for navigation/UI lists.