The theme is the set of design choices every page draws from: colours, fonts, spacing, how round corners are, how wide content may grow, button styles, form fields and the colours of code samples. You change it in one place and the whole site follows.
The choices are stored as tokens. A token is a named value, such as the colour brand or the spacing step lg. A block never holds a colour or a gap as a raw number. Its fields offer the theme's tokens by name, so pages stay consistent and one edit restyles them all.
After this chapter you can change colours and fonts, adjust spacing and buttons, restyle form fields and pick a colour scheme for code. Admins can do all of it in the admin. Developers can do the same in theme.ts.
Until the first save, the site uses the theme in theme.ts, and the Save card says so. After a save, the saved theme is used instead, and changes to theme.ts have no effect until you press Reset to theme.ts. Reset deletes the saved theme. The History card at the bottom of the screen keeps each value that a save replaced, with a Restore button, so a change can be undone.
Every colour has a name and a value. The name is the token that blocks refer to. brand-dark shows as Brand Dark in every colour field in the page editor.
venue.#0f766e or rgb(15 118 110).You cannot remove a colour that the theme's buttons, text styles or form fields still use. Its Remove button is greyed out and says what uses it. Removing a colour that page blocks use leaves those blocks without it, so change them first.
A new site names the roles that blocks default to: brand, brand-dark, surface, surface-alt, text, muted, accent and border. Keep those names and change their values. Add your own colours beside them, such as a dark band and the text that sits on it.
If you write your own CSS, each colour is also a variable, var(--color-brand), and a pair of classes, wn-bg-brand for a background and wn-text-brand for text.
The Typography card has a Body font, a Heading font and, if you press Add a monospace font, a Monospace font that code samples use. For each you give the Family name, a Fallback stack that the browser uses until the font loads, and where it is Loaded from:
Under Text you set the Base size (other sizes in em scale from it), the Line height and the Heading weight.
Text styles are named treatments that a Text block or a menu can wear: default, lead, small, eyebrow, display, title and wordmark. Each has a Size, Weight, Line height, Letter spacing, Case and Font, and sets the colour and underline of links inside it. Change one by name, or add your own. The Default row sets how every link on the site looks, and a blank link cell in another row keeps the Default's.
none to 2xl. Every padding, margin and gap field offers them. A step can be any CSS length. Write the largest steps as clamp() values so that generous desktop spacing shrinks on a phone without editors setting anything per screen.none, sm, md, lg and full.md, lg and xl. These are the screen widths where responsive fields change value and where columns stop stacking. It also sets the three container widths, narrow, default and wide, which are how wide a section's content can grow.The Buttons card has two tables. Styles are named combinations of colours: a background, a text colour, a border and a hover background and text colour. Sizes combine a horizontal and vertical padding, a font size and a radius. Every Button block and every hero call to action picks one style and one size by name.
The Form fields card styles every text input, select, textarea, checkbox and radio on the public site: forms, sign-in, checkout and the quantity stepper. Anything left on Default keeps the standard look, which is a 1px border in the border colour, sm corners, a surface background, a height of 2.75rem and a brand focus ring.
The card has a Resting group (Background, Text, Placeholder, Border, Border width, Corners, Padding across, Padding down, Font size and Height) and a Focus and states group (the focus colours, the Focus ring, the Error colour for an invalid field and the Ticks colour for checkboxes and radios). Setting the focus ring to None removes the outline, which needs a focus border or a focus background so a focused field still looks different.
In theme.ts the same choices live under components.field. Colours are token names. radius, paddingX and paddingY are radius and spacing steps. borderWidth, fontSize and height are CSS values. Add a field entry inside components, next to button:
components: {
// Keep the button styles you already have here.
field: { border: 'ink', radius: 'full', paddingX: 'lg', focusBorder: 'brand', focusRing: 'none' },
},The Code block (Pages and blocks) colours code with one of eight colour schemes:
| Scheme | Looks like |
|---|---|
| GitHub Light | Light |
| GitHub Dark | Dark |
| One Light | Light |
| One Dark | Dark |
| Dracula | Dark |
| Nord | Dark |
| Solarized Light | Light |
| Solarized Dark | Dark |
The theme holds the site's default. To set it, open Settings → Theme and use the Code card. Choose the scheme, the corner radius and the font size, and the card shows a preview of a short snippet. Press Save theme. Every Code block that follows the site default changes at once, with no need to republish a page. If you leave the card alone, code is drawn in GitHub Dark, with md corners and a font size of 0.875rem.
A single block can override the default. Select the Code block and set its Colours field to one of the eight schemes instead of Site default. The font of the code is the theme's Monospace font, if you have added one.
In theme.ts the same default is components.code. Add a code entry inside components, next to button. Every key is optional. The radius is one of the radius steps and the font size is a CSS value:
components: {
// Keep the button styles you already have here.
code: { scheme: 'one-dark', radius: 'md', fontSize: '0.875rem' },
},The scheme names in theme.ts are github-light, github-dark, one-light, one-dark, dracula, nord, solarized-light and solarized-dark.
theme.ts is the theme a developer keeps with the site's code. A new site's file is a complete theme. The example below is a small one that starts from the plain starter theme and changes two colours and the fonts:
import { defineTheme, starterTheme } from '@jamiemcconnell/cms/theme';
export default defineTheme({
...starterTheme,
colors: {
...starterTheme.colors,
brand: '#0f766e',
ink: '#14213d',
},
fonts: {
body: {
family: 'Figtree',
fallback: 'system-ui, sans-serif',
source: { google: { family: 'Figtree', weights: [400, 600] } },
},
heading: {
family: 'Bricolage Grotesque',
fallback: 'system-ui, sans-serif',
source: { google: { family: 'Bricolage Grotesque', weights: [700] } },
},
},
});defineTheme checks the theme when the site starts and names every problem it finds, such as a button style that uses a colour that does not exist. Remember that a theme saved in Settings → Theme takes the place of this file until it is reset.
The theme cannot say everything. Bordered cards, a pill behind a hero's eyebrow or a hairline under the header are the site's own CSS. Put it in the local block package's styles in blocks/index.ts. It is a function of the theme that returns CSS, and it is added after the theme's CSS on every page, so it can use every variable:
import { defineBlockPackage } from '@jamiemcconnell/cms/blocks';
export default defineBlockPackage({
name: 'site',
blocks: [
// Your own blocks stay here.
],
styles: () => `
.wn-card-grid .wn-card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
}
`,
});The starter site's own look is a stylesheet of this kind over its theme. Keep your blocks, collections and icons in the file as they are, and change only the styles entry.