Skip to content

Presets

A preset is a complete visual identity: colour for light and dark mode, a font pairing, a corner radius rule and the range of the signature width-axis effect. Components never hard-code any of these, so switching preset restyles every page at once.

src/config/site.ts
theme: {
preset: 'forest',
switcher: false,
},

That is the whole change. With switcher: false only the active preset’s CSS ships, so unused presets cost nothing.

The samples below are drawn from src/config/presets.ts when these docs are built, and the contrast ratios are calculated from the same values. They cannot drift from the code.

signal

Paper, ink and a signal orange. Square corners, expanded grotesk.

Display
Archivo
Body
Archivo
Radius
0 / 0
Width
100% to 125%

light

Work that ships.

Body copy in Archivo, muted notes below.

Start a projectBrand
signal light text colours and contrast
ink#11111316.2:1AAA
ink-muted#54555A6.4:1AA
signal-text#B835105.1:1AA
signal-ink#1111134.9:1 on signalAA
danger#B020205.9:1AA

dark

Work that ships.

Body copy in Archivo, muted notes below.

Start a projectBrand
signal dark text colours and contrast
ink#ECECE816.3:1AAA
ink-muted#9E9FA47.3:1AAA
signal-text#FF5E336.3:1AA
signal-ink#0E0E106.3:1 on signalAA
danger#FF786E7.5:1AAA

cobalt

Cool white and cobalt blue. Pill buttons, square images.

Display
Instrument Sans
Body
Instrument Sans
Radius
999px / 0
Width
84% to 100%

light

Work that ships.

Body copy in Instrument Sans, muted notes below.

Start a projectBrand
cobalt light text colours and contrast
ink#0B102417.1:1AAA
ink-muted#4A50687.2:1AAA
signal-text#2447F55.8:1AA
signal-ink#F3F4F65.8:1 on signalAA
danger#B4212E6.0:1AA

dark

Work that ships.

Body copy in Instrument Sans, muted notes below.

Start a projectBrand
cobalt dark text colours and contrast
ink#ECEEF516.9:1AAA
ink-muted#9AA0B67.5:1AAA
signal-text#708AFF6.3:1AA
signal-ink#090B166.3:1 on signalAA
danger#FF80808.1:1AAA

forest

Deep green and bone with an amber accent. Soft 12px corners.

Display
Bricolage Grotesque
Body
Instrument Sans
Radius
12px / 12px
Width
84% to 100%

light

Work that ships.

Body copy in Instrument Sans, muted notes below.

Start a projectBrand
forest light text colours and contrast
ink#14241C13.6:1AAA
ink-muted#4A5A506.1:1AA
signal-text#8A57085.1:1AA
signal-ink#14241C7.4:1 on signalAAA
danger#A8261E6.0:1AA

dark

Work that ships.

Body copy in Instrument Sans, muted notes below.

Start a projectBrand
forest dark text colours and contrast
ink#E6EAE215.3:1AAA
ink-muted#9CA89F7.6:1AAA
signal-text#ECB0489.7:1AAA
signal-ink#0C14109.7:1 on signalAAA
danger#FF8A7A8.2:1AAA

mono

Strict black and white, monospaced body. No accent at all.

Display
Archivo
Body
JetBrains Mono
Radius
0 / 0
Width
100% to 125%

light

Work that ships.

Body copy in JetBrains Mono, muted notes below.

Start a projectBrand
mono light text colours and contrast
ink#0C0C0D17.4:1AAA
ink-muted#5555586.6:1AA
signal-text#0C0C0D17.4:1AAA
signal-ink#F2F2F017.4:1 on signalAAA
danger#B020206.1:1AA

dark

Work that ships.

Body copy in JetBrains Mono, muted notes below.

Start a projectBrand
mono dark text colours and contrast
ink#EDEDEB16.7:1AAA
ink-muted#A0A0A37.5:1AAA
signal-text#EDEDEB16.7:1AAA
signal-ink#0C0C0D16.7:1 on signalAAA
danger#FF786E7.6:1AAA

Every preset defines the same ten roles for light and for dark. Components only ever use the roles, never raw colours.

Role Tailwind Use
paper bg-paper Page background
paperRaised bg-paper-raised Alternate sections, the footer
paperSunk bg-paper-sunk Image placeholders, skeletons
ink text-ink Text and primary buttons
inkMuted text-ink-muted Secondary text. Must pass 4.5:1 on paper
inkFaint text-ink-faint Decoration only, never text
signal bg-signal The accent as a fill
signalInk text-signal-ink Text on a signal fill. Must pass 4.5:1 on signal
signalText text-accent The accent as text, icons, rules and focus rings
danger text-danger Form errors

signal and signalText are separate because a bright fill is often too light to read as text. Forest’s amber is the clearest case: #E3A33B as a button, #8A5708 as a link.

  1. Open src/config/presets.ts, copy the preset closest to what you want and give the copy a new key, for example studio.

  2. Change the colours for light and dark. Keep every text role at 4.5:1 or better against the surface it sits on; the comments in the file show the ratios of the built-in presets. WebAIM’s contrast checker is a quick way to test a pair.

  3. Set the fonts. display should be a variable font with a width (wdth) axis if you want the stretch effect. Add the font as described in Typography.

  4. Set radius.ui (buttons, chips) and radius.media (images, cards). '0' gives hairline-divided tile grids, anything else gives separated rounded tiles.

  5. Set stretch: the width at rest, on hover, and the two extremes the footer wordmark animates between. Keep them inside your display font’s width range.

  6. Select it in site.ts with preset: 'studio'. The TypeScript type updates automatically, so a typo in the name is an error.

presetCss() turns a preset into CSS custom properties scoped to html[data-preset='name']. Dark values apply under data-theme='dark', and under prefers-color-scheme: dark for visitors without JavaScript. Tailwind reads the properties through tailwind.config.mjs, which is why bg-ink/10 and every other alpha modifier works in every preset.

The demo switcher stores a visitor’s choice in localStorage under swift-preset. It is applied before first paint, so there is no flash of the wrong preset.