Colors for charts and diagrams
A pie chart needs one color per slice. A flowchart needs one per stage. A timeline needs one per period. Lattice calls these categorical colors, and a theme supplies twelve of them.
They are the hardest colors in a palette, because each one has to do three jobs at once.
Each slot is a pair
Section titled “Each slot is a pair”A slot is not one color. It is two, and they are tiers of the same hue:
--cat-1-fill: #BCD5EC; /* the area: a pie slice, a node's inside */--cat-1-mark: #2E608A; /* the edge: its border, a line, a dot */The fill is pale. The mark is deep. Together they make a box that reads as one thing: a soft area with a definite edge.
There is a third, generated value per slot — --cat-1-ink — for when that
category has to appear as small text on the slide rather than as a
shape. You do not pick these by hand; node tools/derive-cat-ink.js
computes them from your marks and writes the block into your file.
The three layers
Section titled “The three layers”Every slot has to satisfy all three at once, in both canvases.
① The edge reads against the page. --cat-N-mark against --bg, at
least 3:1. This is the layer that stops boxes from floating: without a
definite border, a pale fill on a pale page has no shape.
② The fill is deliberately quiet. --cat-N-fill against --bg sits
low on purpose: the edge from layer ① is what defines the shape, so the
fill is free to be a whisper. This is the one layer no checker measures,
because “quiet enough” is a judgment.
③ The label reads on the fill. Whatever text sits inside that pie slice
uses --cat-on-fill, and it needs 4.5:1 against the fill it lands on.
And one anti-rule: fill and mark must not be the same color. Set them equal and the box collapses into a flat blob with no edge. That was a real bug, and there is now a gate that fails the build for it.
The pairs swap in dark mode
Section titled “The pairs swap in dark mode”On a light page, the fill is the pale tier and the mark is the deep one. On a dark page they trade places: the fill becomes a jewel tone and the mark becomes a pale tint. Both tiers are written as a light/dark pair, so one line covers both:
--cat-1-fill: light-dark(#BCD5EC, #006398); /* pale ↔ jewel */--cat-1-mark: light-dark(#2E608A, #D4DFE8); /* deep ↔ pale */Because the fill flips from pale to deep, the label ink has to flip too:
--cat-on-fill: var(--text-heading); /* flips with the canvas */--cat-on-mark: light-dark(#FFFFFF, #0A1628); /* also flips */Set --cat-on-fill to a fixed dark color and it works beautifully in light
mode, then paints dark text on a dark jewel fill the moment someone opens
the deck in dark mode. That is the single most common mistake in a new
theme, and the build refuses it.
Try this. Each box takes the next slot in the cycle — four of the twelve are set in the file below. Press Dark and watch every box flip tiers at once: nothing in the markdown changed, and no second palette exists. Then push --cat-3-fill toward #8A5A12 and watch the label on that one box stop being readable while the other three are fine — layer ③, failing in one slot.
Notice that the flowchart carries no styling instructions at all. The author wrote the shape; the theme supplied every color. A roadmap or a kanban board on the same deck reads the same twelve slots — same cycle, different shape.
One thing this particular lab does not show is layer ①. A Mermaid
flowchart draws every node’s border from a single token, --diagram-stroke,
rather than from each slot’s own --cat-N-mark — so the edges here are all
one color by design. The per-slot mark does the edge job where the shapes
are genuinely per-category: a pie slice, a chart series, a timeline dot. If
your diagram boxes look like they are floating, --diagram-stroke is the
token to darken.
Author for six, not twelve
Section titled “Author for six, not twelve”Twelve slots exist so that a long timeline does not run out. They are not an invitation to use twelve.
Past roughly six categories, people stop telling hues apart — this is a
limit of perception, not of the palette, and no amount of care in choosing
the colors gets around it. If you genuinely have more than six categories,
the answer is not a thirteenth color. It is either fewer categories or a
palette that separates by pattern instead of hue: onyx, concrete
and the a11y-* family fill each category with a texture, and a theme opts
in by declaring twelve --cat-N-texture tokens.
The shortcut you should take
Section titled “The shortcut you should take”Do not invent twelve hues from nothing. npm run new:theme copies the
default palette’s categorical block, which already satisfies all three
layers in both canvases — re-hue it toward your brand rather than starting
from a blank list. Then run:
node tools/derive-cat-ink.js # regenerate the twelve --cat-N-ink valuesnpm run build:check # the three-layer gateYou have all the pieces. Now go and finish the file: Ship your theme.