Colors
Four palettes — Iron Night, Birch Storm, Forest, Bloom — organised by role. A two-layer token architecture separates raw palette values from semantic aliases, so components never need to know which theme mode they are in.
Palette Tokens
Raw palette tokens are defined once and never change. Use them directly only for brand moments and design showcases — all production components reference the semantic layer instead.
Iron Night — Dark Surfaces
--kn0-void#0D1117--kn1-iron#374151--kn2-slate#4B5563--kn3-ash#6B7280| Token | Hex | Role |
|---|---|---|
--kn0-void | #0D1117 | Primary background (dark mode) |
--kn1-iron | #374151 | Elevated surfaces — panels, cards on dark |
--kn2-slate | #4B5563 | Active lines, selections, highlights |
--kn3-ash | #6B7280 | Secondary text, placeholders (AA-large only) |
Birch Storm — Light Surfaces
--bs0-sand#E4E2DA--bs1-linen#EEEDE9--bs2-parchment#F5F4F0| Token | Hex | Role |
|---|---|---|
--bs0-sand | #E4E2DA | Elevated UI (light), input fills, sunken surfaces |
--bs1-linen | #EEEDE9 | Card fill, hover states, tinted sections |
--bs2-parchment | #F5F4F0 | Primary background (light) / primary text (dark) |
Forest — Accent
--fr0-sage#5C9E86--fr1-fern#3E7A62--fr2-forest#2D5E4A--fr3-deepwater#254D5A| Token | Hex | Role |
|---|---|---|
--fr0-sage | #5C9E86 | Supporting accent, dark-mode primary accent |
--fr1-fern | #3E7A62 | Primary accent — buttons, links, CTAs (light mode) |
--fr2-forest | #2D5E4A | Accent hover, secondary emphasis |
--fr3-deepwater | #254D5A | Code block backgrounds (light pages), blockquote accent |
Bloom — Semantic States
--bl0-ember#C5414C--bl1-ochre#AB5A2B--bl2-grain#8D6B20--bl3-moss#567A37--bl4-heather#885DB4| Token | Hex | Role |
|---|---|---|
--bl0-ember | #C5414C | Error, destructive actions |
--bl1-ochre | #AB5A2B | Annotation, decorator-weight info |
--bl2-grain | #8D6B20 | Warning, draft, pending |
--bl3-moss | #567A37 | Success, published, active |
--bl4-heather | #885DB4 | Special states, beta, research |
Semantic Token Layer
Components always reference semantic tokens — never palette tokens directly. The semantic layer resolves to the correct palette value per theme mode automatically.
Light Mode (default)
:root,
[data-theme="light"] {
--color-bg: var(--bs2-parchment); /* Page background */
--color-bg-elevated: var(--bs1-linen); /* Cards, panels */
--color-bg-sunken: var(--bs0-sand); /* Input fills */
--color-text: var(--kn0-void); /* Primary text */
--color-text-secondary: var(--kn2-slate); /* Supporting text */
--color-text-tertiary: var(--kn3-ash); /* Placeholders, captions */
--color-border: var(--kn1-iron); /* Default border */
--color-accent: var(--fr1-fern); /* Primary CTA, links */
--color-accent-hover: var(--fr2-forest); /* Accent hover */
--color-accent-text: var(--bs2-parchment); /* Text on accent bg */
} Dark Mode
[data-theme="dark"] {
--color-bg: var(--kn0-void);
--color-bg-elevated: var(--kn1-iron);
--color-bg-sunken: var(--kn2-slate);
--color-text: var(--bs2-parchment);
--color-text-secondary: var(--bs0-sand);
--color-text-tertiary: var(--kn3-ash);
--color-border: var(--kn2-slate);
--color-accent: var(--fr0-sage); /* Sage reads better on dark */
--color-accent-hover: var(--fr1-fern);
--color-accent-text: var(--bs2-parchment);
} Surface Overrides
Apply data-surface to any element to force a surface context regardless of the page-level theme. All child components inherit the re-scoped tokens automatically.
<section data-surface="dark"> … </section>
<section data-surface="light"> … </section>
<section data-surface="tinted"> … </section> Accessibility
| Pair | Ratio | Result |
|---|---|---|
| Parchment on Void | 13.07:1 | ✓ AA |
| Sand on Void | 10.96:1 | ✓ AA |
| Fern on Parchment | 4.80:1 | ✓ AA |
| Fern on Void | 4.68:1 | ✓ AA |
| Sage on Void | 5.19:1 | ✓ AA |
| Ember on Parchment | 4.51:1 | ✓ AA |
| Parchment on Fern (button text) | 4.80:1 | ✓ AA |
| Ash on Void | 3.35:1 | AA large only |
--kn3-ash is AA-large only — use at 18px or bold weight only, never for small body text. All Bloom tokens meet WCAG AA (4.5:1) against --bs2-parchment in light mode.
Usage Rules
- Never use Bloom colours as decorative accents — reserve them for semantic states only (error, warning, success, annotation).
- Iron Night tones must not appear as foreground text on light backgrounds — contrast is too low.
- Primary CTA:
--fr1-fernbackground with--bs2-parchmenttext (4.80:1 ✓). - Dark mode accent shifts Fern → Sage automatically. Both are the same palette family — the shift is imperceptible as a brand change but improves legibility.
- Components reference semantic tokens only. Palette tokens are for brand moments and one-off elements.