Theme colours
Editors change the theme at runtime in Site settings → Theme (see Theme and design tokens). The defaults, and how the theme becomes CSS, live in code.
Change the defaults #
Edit
DEFAULT_THEMEinsrc/theme/tokens.ts: the seven base colours and the two tints, as hex values.export const DEFAULT_THEME: Theme = { colors: { brand: '#2f54c8', ink: '#0f1115', highlight: '#6f97f2', signal: '#d9603f', surface: '#d6d3ce', success: '#4f9d69', error: '#cf5d5d', }, tints: { 'tint-20': '#f9fafb', 'tint-50': '#f1f3f6' }, }Regenerate the CSS:
pnpm generate:themeThis rewrites
src/theme/theme.css: every variable, and the list of classes built at runtime. Commit it; a test fails when it is not exactly what the generator makes.Run
pnpm test:unit. The contrast tests check that text on every background reaches 4.5:1.
The defaults reach every site, running ones included, on its next deploy. Site settings store only the colours an admin set in Site settings → Theme: those win, and every colour left empty follows DEFAULT_THEME. The Theme fields show the default in grey while they are empty.
A site whose settings were saved before the kit worked this way stored every colour, so it keeps its old ones. To let a colour follow the defaults again, press Use default beside it in Site settings → Theme, or empty the field, and save.
How a colour becomes a scale #
generateScale() mixes the base colour with white for the light steps and with black for the dark ones: 50 is 5 % colour, 400 is 50 %, 500 is the colour itself, and 900 is 20 % colour with black. Every step becomes a variable (--color-brand-500) and a utility (bg-brand-500, text-brand-500/60).
Readable text #
For every background token the theme also writes:
text-on-<token>: black or white, whichever has more contrast.text-on-<token>-accent: the eyebrow and link colour, a brand step where it reaches 4.5:1 on that background, else black or white.
Use them through getSurface(token) (src/components/sections/surface.ts) and getPanelRecipe(token). Never put text-brand-500 on a background you do not control: a light brand colour, a yellow say, would be unreadable on white.
Rules the code keeps #
- Semantic names only. Colours are
brand,ink,signaland so on, never a company's colour names. A test checks. - Editors store tokens, such as
brand-500, never hex values, so a theme change reaches every block. - No capitals by CSS. Eyebrows get their feel from small type and letter-spacing, never
text-transform.