Skip to content
  • System
  • Light
  • Dark
  • High contrast

Theming

Raster’s package contains:

  • Primitive scalesneutral, 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.

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.

my-app/styles/theme.css
@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.

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... */
}

A few practical guidelines:

  • Greys: pick one neutral family (neutral, slate, or sand) 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.

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.

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:

  1. 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.
  2. Replace any direct references to --color-primitive-gray-* with --color-primitive-neutral-* (the scale was renamed from gray and shifted to true R = G = B values).
  • “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).