Design tokens
Colour is chosen per topic rather than picked from a catalogue, but only inside <style id="shf-theme">, and only as declarations the token grammar accepts. Everything else is hash-pinned.
The one editable block
<style id="shf-theme">
:root {
--shf-color-accent: #0067b8;
--shf-size-base: 20px;
}
</style>One :root block, custom properties only. No selectors, no at-rules, no url(, no var(, no backslash. Any property outside the table below fails, so a typo cannot quietly become dead CSS.
| Property | Grammar | Example |
|---|---|---|
--shf-color-* |
#rrggbb |
#0067b8 |
--shf-space-* |
number + px or rem |
24px |
--shf-size-* |
number + px, rem, or ch |
74ch |
--shf-scale-* |
bare number | 1.25 |
--shf-radius-* |
number + px or rem |
12px |
--shf-weight-* |
100–900 in hundreds |
700 |
--shf-ratio-* |
bare number | 1.6 |
Named colours, rgb(), and hsl() are all rejected. Hex keeps the contrast check straightforward.
Choosing a palette
If an earlier artifact in the same series already carries a theme block, reuse it and stop here. Otherwise pick a base hue from the subject, not from habit.
| Subject | Hue range |
|---|---|
| Infrastructure, security, platform | blue 200–230 |
| Data, analysis, finance | teal to green 160–200 |
| Product, launch, marketing | orange to red 10–40 |
| Research, policy, education | indigo to violet 250–280 |
| Operations, logistics, manufacturing | amber to brown 30–50 |
Then:
- One accent. Two only if the second marks a genuinely different category. A third makes the artifact look decorated rather than designed.
- Text is not pure black.
#1b1f27reads better than#000000. - Surfaces are the accent hue at very low saturation, not grey. It is what makes a palette feel deliberate.
- Body text against its background must clear 4.5:1. Large display text must clear 3:1. Check it; do not eyeball it.
- Never let colour be the only carrier of meaning. Pair it with a word or an icon.
Contrast, quickly
Relative luminance per channel: c/255, then c<=0.03928 ? c/12.92 : ((c+0.055)/1.055)**2.4, weighted 0.2126 R + 0.7152 G + 0.0722 B. Ratio is (lighter+0.05)/(darker+0.05).
The default accent #0067b8 on white is about 5.6:1, so it is safe for body text. Lighten it much and it stops being.
Reusing a look
The <style id="shf-theme"> block is the design system — everything else is hash-pinned — so carrying that block into the next artifact carries the palette and spacing with it. Keep it beside the artifacts when a series has to match, and derive the palette once rather than per file. It does not make two artifacts identical: archetype, markup, viewport and the fonts the machine resolves all still differ, and glyphs never match exactly across machines (japanese-typography.md).
Which tokens an archetype actually reads, counted from var() uses in the pinned CSS:
| Token | deck | doc | poster |
|---|---|---|---|
--shf-color-bg -fg -muted -accent -surface -line |
✓ | ✓ | ✓ |
--shf-space-gap --shf-radius-card --shf-weight-display |
✓ | ✓ | ✓ |
--shf-size-base |
— | ✓ | ✓ |
--shf-color-accent-deep -ok -warn -bad -surface-alt --shf-size-measure |
— | ✓ | — |
--shf-size-canvas-w -h |
— | — | ✓ |
--shf-scale-step |
— | — | — |
The verifier checks the token grammar by prefix, not against an archetype's list, so a token the archetype never reads still passes — it simply does nothing. That is how a token quietly stops working: deck typography is entirely cqw, so --shf-size-base does not scale a deck, and --shf-scale-step is read by nothing at all. Never take a passing verification as proof that a value took effect; change it and look. Measured on the poster skeleton: shf-theme sits before shf-css, whose :root defaults then win, so a changed accent renders unchanged while verification passes. Put shf-theme after shf-css (the verifier accepts either order) and confirm by rendering; deck and doc are untested.
Varying the look without new CSS
The template is fixed, so distinction comes from restraint, not from novel layout: how much whitespace, whether surfaces are used at all, how heavy the display weight is, whether the accent appears as a rule, a card edge, or only in links. A deck with --shf-weight-display:900, generous spacing and a single hairline accent reads nothing like one with cards everywhere, and neither needed a line of custom CSS.