Chapter 02

Color

Viola is a palette with a point of view: mostly violet, warmed with cream, sparked once with peach. Every value on this page is read from the stylesheet the site runs on, so it cannot go out of date.

Built with

  • CSS variables
  • color-mix()
  • Tailwind @theme

Proportion

Seventy percent violet. Twenty cream. Ten peach.

The ratio matters more than any single colour. Keep violet dominant and the page feels calm and confident. Let the accent grow past a tenth and it starts to shout.

70Violet
The primary. Actions, focus, hover fills, and the surfaces around them.
20Cream
The secondary. Warm paper, and the text colour on ink.
10Peach
The accent. One small spark, never a theme.

Layer 1

52 primitives. The only place a hex lives.

Raw palette values, independent of any theme. Nothing in a component ever references these directly. Click a swatch to copy its value.

Violet

Primary. The only colour that acts.

Lilac & ink

Neutrals. Lilac paper for light, ink for dark, and the greys that carry text.

Cream

Secondary. Warm paper, and the text colour on ink.

Peach & amber

Accent. One small spark.

Status

Success and error, tuned per theme.

Brand

Third-party colours that must stay recognisable.

Layer 2

35 semantic tokens. What the interface actually uses.

Each theme points these names at primitives. The first swatch is live, so it follows the theme toggle; the two beside it show the value each theme resolves to.

  • --background

    The page itself. Lilac paper in light, ink in dark.

    #f1edf8

    --lilac-50

    #14121a

    --ink-950

  • --background-elevated

    Cards and raised panels, one step off the page.

    #f7ead9

    --cream-100

    #1b1823

    --ink-900

  • --background-surface

    Inset wells and the deepest layer.

    #f1dec6

    --cream-200

    #221e2c

    --ink-800

  • --foreground

    Primary text. Ink on paper, cream on ink.

    #211c2e

    --ink-text

    #faebd7

    --cream-50

  • --foreground-muted

    Secondary text and supporting copy.

    #56506c

    --grey-light-muted

    #b4aec4

    --grey-dark-muted

  • --foreground-subtle

    Metadata, labels, and anything that should whisper.

    #726788

    --grey-light-subtle

    #837c98

    --grey-dark-subtle

  • --primary

    Violet. Links, focus, hover fills, the voice of the page.

    #7c6fb0

    --violet-600

    #a89cd8

    --violet-400

  • --primary-light

    The lighter violet, for gradients and soft emphasis.

    #9b8fd0

    --violet-450

    #c3b9e8

    --violet-300

  • --primary-dark

    The deeper violet, for pressed states and strong marks.

    #5f5389

    --violet-700

    #8b7fb8

    --violet-550

  • --secondary

    Cream. Warm secondary surfaces and text on ink.

    #f1dec6

    --cream-200

    #fae3c6

    --cream-300

  • --accent-warm

    Amber or peach. A single spark, never a theme.

    #c98a3d

    --amber-500

    #f5c98a

    --peach-300

  • --accent-coral

    The warmer sibling of the accent, for rare highlights.

    #d9854f

    --amber-600

    #eda673

    --peach-400

  • --border

    Hairlines between sections and around components.

    rgba(90, 70, 140, 0.14)

    direct value

    rgba(250, 227, 198, 0.12)

    direct value

  • --border-subtle

    Even quieter lines, for inside lists.

    rgba(90, 70, 140, 0.08)

    direct value

    rgba(250, 227, 198, 0.07)

    direct value

  • --success

    Healthy, online, sent.

    #4d7c5f

    --green-600

    #6fae82

    --green-400

  • --error

    Something needs attention.

    #b3402c

    --red-700

    #d17563

    --red-400

  • --offline

    Unreachable. Slightly softer than error.

    #c0525a

    --red-500

    #c0525a

    --red-500

Layer 3

Derived colour, mixed on the fly.

Component tokens are built from the layer above with color-mix(), so they follow the theme without being redefined. The activity heatmap's five levels are all just violet at different strengths over the page background.

light · heatmap

tint-strongtint-softglow

dark · heatmap

tint-strongtint-softglow
globals.css · component tokens
--heatmap-l0: rgba(90, 70, 140, 0.08);--heatmap-l1: color-mix(in srgb, var(--primary) 28%, var(--background));--heatmap-l2: color-mix(in srgb, var(--primary) 56%, var(--background));--heatmap-l3: var(--primary);--heatmap-l4: var(--primary-dark);--primary-tint-strong: color-mix(in srgb, var(--violet-600) 10%, transparent);--primary-tint-soft: color-mix(in srgb, var(--violet-600) 6%, transparent);--primary-glow: color-mix(in srgb, var(--violet-600) 40%, transparent);

Wiring

How Tailwind sees it.

Tailwind v4 reads tokens from CSS, not a config file. @theme inline exposes the semantic tokens as utilities, so bg-primary and text-muted read the live variable and follow the theme with no dark: variants.

globals.css · @theme inline
@theme inline {  --color-background: var(--background);  --color-foreground: var(--foreground);  --color-muted: var(--foreground-muted);  --color-subtle: var(--foreground-subtle);  --color-primary: var(--primary);  --color-secondary: var(--secondary);  --color-elevated: var(--background-elevated);  --color-surface: var(--background-surface);  --color-accent: var(--accent-warm);  --color-border: var(--border);}