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
dark · heatmap
--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.
@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);}