Colour System
Colour System
The palette is built on CSS custom properties, configured per-site via the PaletteComponent and the initializer's configure_palette block.
Colour Families
Four families, each with tones from 10 (lightest) to 90 (darkest):
| Family | Variables | Usage |
|---|---|---|
| Primary | --primary-10 to --primary-90 |
Brand colour, main CTAs, navigation |
| Secondary | --secondary-10 to --secondary-90 |
Supporting elements, alternate sections |
| Accent | --accent-10 to --accent-90 |
Highlights, alerts, accent buttons |
| Mono | --mono-0 to --mono-100 |
Text, borders, neutral backgrounds |
Semantic Aliases
Each family has five named aliases:
| Alias | Tone | Example |
|---|---|---|
light |
10 | --primary-light |
muted |
20 | --primary-muted |
medium |
40 | --primary-medium |
vibrant |
70 | --primary-vibrant |
dark |
90 | --primary-dark |
Surfaces
A container places its children and publishes context to them. It never styles their internals. A panel owns the grid, the gaps and the cell each card lands in. What is inside the cell is the card's business, and context reaches it as inherited custom properties, never as a selector aimed at it. panels/_panel.scss contains no .media-card or .text-card selector.
Whoever paints a background declares the surface with one of three mixins from utilities/_mixins.scss, and everything on that surface reads the published values:
| Token | light-surface |
dark-surface |
inherit-surface |
:root default |
|---|---|---|---|---|
--text-link |
--link-dark |
--link-light |
inherit |
--link-dark |
--surface-ink |
--text-dark |
--text-light |
inherit |
--text-dark |
--surface-headline |
--headline-dark |
--headline-light |
inherit |
--headline-dark |
--surface-accent |
--text-primary |
--text-light |
inherit |
--text-primary |
--surface-tint |
--overlay-tint-on-light |
--overlay-tint-on-dark |
inherit |
--overlay-tint-on-light |
--surface-raised |
second argument | second argument | inherit |
none |
// A panel publishes. It knows no card exists.&[data-bgc=primary-dark]{ @include dark-surface(var(--primary-dark), var(--bg-primary-muted)); }// A card reads. It knows no panel exists.&[data-layout-style=quote]{ @include inherit-surface(var(--surface-tint)); .media-card__quote-rule{ background-color: var(--surface-accent); } .media-card__quotee-name{ color: var(--surface-ink); }}// A card that paints its own background republishes for its children.&[data-layout-style=background]{ @include light-surface(var(--surface-raised, var(--bg-white))); }A null background publishes without painting, for a surface something else paints. Text over a photo is a scrim-surface: the dark contract with --surface-headline and --text-link kept at --text-light, since white over a scrim is the design and the accent tint is for painted dark panels. The hero's background media and the image-background card both @include scrim-surface. Controls that paint themselves (inputs, the combobox listbox, pagination cells, the navigation submenu) declare light-surface(var(--bg-white)) and read --surface-ink / --text-link like anything else.
--surface-raised is the one value a container chooses: the background a nested card paints to stand off the surface (light panels alternate light and muted). It is only emitted when given. inherit-surface sets every token to inherit explicitly, so a transparent variant beats a surface declared on the same element by a lower-specificity rule.
The :root defaults make a card correct on a bare light page. A host that paints a dark page wraps the card in dark-surface; no panel CSS is needed.
Panels accept layout_background_colour with the values primary-light, primary-muted and primary-dark, matching Contentful. Each publishes its surface as above.
Size is not colour. A card that must stack when its column is narrow responds to the width of its own cell with a container query (.panel__cell is container-type: inline-size), never to the panel's column count.
Semantic Tokens
Always use semantic tokens — never raw palette variables or hex values. Defined in styles/_colours.scss.
Backgrounds
| Token | Value | Use for |
|---|---|---|
--bg-primary-light |
--primary-10 |
Light tinted panel/card background |
--bg-primary-muted |
--primary-20 |
Muted tinted background |
--bg-primary-dark |
--primary-90 |
Dark primary background |
--bg-mono-light |
--mono-10 |
Light grey background |
--bg-white |
--mono-0 |
White background |
--bg-disabled |
--primary-20 |
Disabled state background |
Text
| Token | Value | Use for |
|---|---|---|
--text-dark |
--mono-90 |
Body text on light backgrounds |
--text-light |
--mono-0 |
Body text on dark backgrounds |
--text-primary |
--primary-90 |
Primary brand text |
--text-accent |
--accent-90 |
Accent-coloured text |
--text-link |
published by the surface | Link colour on the current surface (see Surfaces) |
--text-disabled |
--mono-40 |
Disabled text |
--text-watermark |
--mono-40 |
De-emphasised text |
Headlines
| Token | Value | Use for |
|---|---|---|
--headline-dark |
--secondary-90 |
Headings on light backgrounds |
--headline-light |
--accent-10 |
Headings on dark backgrounds |
Links
| Token | Value | Use for |
|---|---|---|
--link-dark |
--primary-90 |
Links on light backgrounds (light-surface publishes it as --text-link) |
--link-light |
--accent-10 |
Links on dark backgrounds |
Borders
| Token | Value | Use for |
|---|---|---|
--border-mono-medium |
--mono-40 |
Standard card/component border (@include card) |
--border-mono-dark |
--mono-90 |
Strong mono border |
--border-primary-dark |
--primary-90 |
Primary dark border |
--border-primary-medium |
--primary-40 |
Primary medium border |
--border-accent-dark |
--accent-90 |
Accent dark border |
--border-accent-medium |
--accent-40 |
Accent medium border |
Common Fixed Colours
| Token | Value |
|---|---|
--common-white |
--mono-0 |
--common-black |
--mono-100 |
Business Sector Colours
| Token | Sector |
|---|---|
--leisure |
Leisure |
--libraries |
Libraries |
--gsf |
Sport (GSF) |
--spaexp |
Spa |
--boxoffice |
Box Office |