Theming
Raster’s package contains:
- Primitive scales —
neutral,sand,slate,red,orange,amber,yellow,green,teal,cyan,blue,indigo,purple,pink,rose. Each is a 12-step scale (1 = lightest, 12 = darkest). Available as CSS custom properties via@eekodigital/raster/primitives.css. - Components — every component reads semantic tokens (
--color-surface,--color-text,--color-interactive, etc.) so they adapt to whatever theme the consuming app applies.
What raster does not ship: a default theme. Mapping primitives to semantic tokens is a brand decision and lives with each consumer.
A theme is a brand statement. Two products built on raster will pick different greys, different accent colours, different surface contrasts — that’s the point. If the design system bakes one theme into every consumer, every consumer drags the same brand. Splitting primitives from theme keeps raster generic and lets each brand own its look.
The minimum viable theme
Section titled “The minimum viable theme”Import primitives, then declare semantic tokens for at least one theme. Every component reads these tokens, so once they’re defined the whole system has colour.
@import "@eekodigital/raster/primitives.css";
:root,[data-theme="light"] { --color-bg: var(--color-primitive-neutral-2); --color-surface: var(--color-primitive-neutral-1); --color-surface-raised: var(--color-primitive-neutral-3); --color-surface-overlay: var(--color-primitive-neutral-5);
--color-border: var(--color-primitive-neutral-6); --color-border-strong: var(--color-primitive-neutral-8);
--color-text: var(--color-primitive-neutral-12); --color-text-subtle: var(--color-primitive-neutral-11); --color-text-placeholder: var(--color-primitive-neutral-9); --color-text-disabled: var(--color-primitive-neutral-7); --color-text-inverse: var(--color-primitive-neutral-1);
--color-interactive: var(--color-primitive-blue-9); --color-interactive-hover: var(--color-primitive-blue-10); --color-interactive-active: var(--color-primitive-blue-11); --color-interactive-subtle: var(--color-primitive-blue-2); --color-interactive-text: var(--color-primitive-neutral-1);
--color-focus-ring: var(--color-primitive-blue-9);
--color-success: var(--color-primitive-green-11); --color-success-bg: var(--color-primitive-green-2); --color-success-border: var(--color-primitive-green-9);
--color-danger: var(--color-primitive-red-11); --color-danger-bg: var(--color-primitive-red-2); --color-danger-border: var(--color-primitive-red-9);
--color-warning: var(--color-primitive-amber-11); --color-warning-bg: var(--color-primitive-amber-2); --color-warning-border: var(--color-primitive-amber-9);
--color-inactive: var(--color-primitive-neutral-9); --color-inactive-bg: var(--color-primitive-neutral-2); --color-inactive-border: var(--color-primitive-neutral-7);}Every semantic token raster components consume is listed in the Colours reference. Pick a primitive for each.
Multiple themes
Section titled “Multiple themes”Repeat the block under [data-theme="dark"], [data-theme="high-contrast"], etc. Toggle the theme by setting data-theme on the root element (typically <html>). The same components flip automatically because they only read the semantic tokens.
[data-theme="dark"] { --color-bg: var(--color-primitive-neutral-12); --color-surface: var(--color-primitive-neutral-11); /* ...same tokens, mapped to darker primitives... */}Picking primitives for a theme
Section titled “Picking primitives for a theme”A few practical guidelines:
- Greys: pick one neutral family (
neutral,slate, orsand) and stick to it across surfaces, borders, and text-greys. Mixing families produces subtle colour clashes you’ll keep noticing. - Light theme surfaces: tend to live around steps 1–3 of your chosen grey scale.
- Dark theme surfaces: steps 11–12 for
bg, steps 9–11 for surfaces above it. - Accents: pick step 9–11 for the strong accent (button background) and step 1–2 for the soft accent (subtle highlight). Keep accent and grey families stable across themes.
- High-contrast theme: lean on pure black, pure white, and the highest-contrast primitive steps. Aim for WCAG AAA (≥ 7:1) on body text rather than the AA target (≥ 4.5:1) used for light/dark.
Verifying contrast
Section titled “Verifying contrast”Every theme should hit WCAG AA on body text (--color-text on --color-bg). The high-contrast theme should hit AAA. The Colours page renders live swatches so you can spot-check; an automated checker (Axe, WebAIM contrast tool, or vitest-axe against rendered components) will catch regressions before they ship.
Migration from raster ≤ 0.4.x
Section titled “Migration from raster ≤ 0.4.x”Previously, importing @eekodigital/raster/tokens.css brought primitives and raster’s own light / dark / high-contrast theme. From 0.5.0 onward tokens.css (and its alias primitives.css) ship primitives only.
If you depended on the old default theme:
- Copy raster’s prior theme blocks into your app (search the changelog for the 0.4.x theme files), or write your own using the template above.
- Replace any direct references to
--color-primitive-gray-*with--color-primitive-neutral-*(the scale was renamed fromgrayand shifted to true R = G = B values).
Why the rename from gray to neutral
Section titled “Why the rename from gray to neutral”- “Gray” excludes white and black; the scale spans
#fcfcfc–#212121. “Neutral” is the right word for that range. - Sidesteps the
gray(US) /grey(UK) spelling fork. - Reinforces that the scale is achromatic — every step now has equal R, G, and B channels (no warm or cool tilt).