Tokens
Every design primitive in the Material-slim token system: brand colours, surfaces, on-surface ink, inverse surface, outlines, status families, typography, radius, motion, z-index, and focus. Each section that varies between light and dark mode renders both modes side by side using .force-light / .force-dark wrappers so a swatch reads its hex from a single token declaration.
Source of truth:
DESIGN.md
is the editable file; running task tokens
regenerates tokens.css.
Swatches read CSS variables — they can’t drift.
Brand — plum
Plum is the brand 10% — used sparingly on filled CTAs, focus rings, selected states. primary is the saturated plum for filled buttons and the focus ring. primary-container is the only sanctioned soft brand surface — for selected radio cards, brand-themed pills, active-but-not-CTA backgrounds. Pair bg-primary with text-on-primary; pair bg-primary-container with text-on-primary-container. Brand colours are full-opacity only — alpha-modulated utilities (bg-primary/10, text-primary/40, etc.) are forbidden everywhere.
Light
#6B3D5A
Dark
#B984A6
primary
Filled CTAs, focus rings
--color-primary
Light
#FFFFFF
Dark
#1B0F18
on-primary
Text / icons on bg-primary
--color-on-primary
Light
#ECDBE5
Dark
#2D262F
primary-container
Soft plum surface (selected, pills)
--color-primary-container
Light
#44243A
Dark
#D2A9C2
on-primary-container
Text on bg-primary-container
--color-on-primary-container
Light
#44243A
Dark
#D2A9C2
primary-hover
Filled-button hover (token internal)
--color-primary-hover
The -hover token is consumed internally by koala-btn for filled-button hover states — don’t reach for it directly in markup.
Surfaces
Four container levels — page → sidebar / banded → inset row → card. The brand is intentionally surface-rich in the warm range; hierarchy is rendered with hairlines and tone shifts rather than shadows. Light mode pivots around warm cream (#F7F3EC); dark mode around midnight (#131418).
Light
#F7F3EC
Dark
#131418
surface
Page background
--color-surface
Light
#F1ECE2
Dark
#0D0E11
surface-dim
Sidebar, banded sections, hover backdrop
--color-surface-dim
Light
#FBF8F2
Dark
#16181C
surface-container-low
Inset rows inside cards, list dividers
--color-surface-container-low
Light
#FFFFFF
Dark
#1C1E23
surface-container
Cards, modals, side trays, dropdown panels
--color-surface-container
On-surface — ink
Slate-neutral text ramp. Four steps from headings down to placeholders. Dark-mode on-surface is warm cream by design — the warm heading on midnight is the brand’s signature pairing and ties the two modes together.
The quick brown fox jumps over the lazy dog.
Light #14171B
The quick brown fox jumps over the lazy dog.
Dark #F1ECE2
on-surface
Headings, primary emphasis
--color-on-surface
The quick brown fox jumps over the lazy dog.
Light #3F4651
The quick brown fox jumps over the lazy dog.
Dark #B6BBC3
on-surface-variant
Body copy, link text
--color-on-surface-variant
The quick brown fox jumps over the lazy dog.
Light #6E7682
The quick brown fox jumps over the lazy dog.
Dark #8A8F97
on-surface-muted
Captions, timestamps, helper text
--color-on-surface-muted
The quick brown fox jumps over the lazy dog.
Light #9FA5AE
The quick brown fox jumps over the lazy dog.
Dark #5C6168
on-surface-hint
Placeholders, disabled hints
--color-on-surface-hint
Inverse surface
The dark fill in light mode that flips to a light fill in dark mode. One semantic role covers tooltips, snackbars, dark-style secondary buttons, and high-contrast banded sections. Pair bg-inverse-surface with text-on-inverse-surface.
Light #1F2329
Dark #DAD3C5
inverse-surface
Tooltip / snackbar fill
--color-inverse-surface
Light #F7F3EC
Dark #14171B
on-inverse-surface
Text on inverse fill
--color-on-inverse-surface
Canvas
Forced-white surface for content whose contrast budget is set outside the design system — quiet zones around generated QR codes, printed document previews, embedded code samples with locked-foreground ink. Identical in light and dark mode by design. Reach for bg-canvas only when the foreground is fixed; for everything else, use the surface tokens above.
Light #FFFFFF
Dark #FFFFFF
canvas
QR quiet zone, document preview
--color-canvas
Light #14171B
Dark #14171B
on-canvas
Ink on canvas
--color-on-canvas
Outlines
Containers separate via borders, not shadows. outline is the visible border on cards, inputs, and section dividers. outline-variant is the lighter hairline used for table row separators and inset row dividers.
Light
#D9CFBC
Dark
#353941
outline
Cards, inputs, section dividers
--color-outline
Light
#E8E1D3
Dark
#272A30
outline-variant
Table rows, inset dividers
--color-outline-variant
Status
Five warm-aligned status families: success (sage), danger (warm red), warning (amber), info (marine), neutral. Each is a four-token bundle: the saturated accent (success) paired with on-success for the filled-fill foreground, plus the soft surface (success-container) paired with on-success-container. Use the bundle through koala-badge and the equivalent component helpers — never raw Tailwind palette classes.
Light
accent #5B8F6E on-accent #FFFFFF
container #DDE8DF on-container #2E5340
Dark
accent #6FA587 on-accent #131418
container #1F2D26 on-container #B7D4C0
Light
accent #C0413A on-accent #FFFFFF
container #F4DAD8 on-container #7A2722
Dark
accent #E07269 on-accent #1F0F0E
container #3A1B19 on-container #F2BFBB
Light
accent #C4861F on-accent #1F1306
container #F5E4C4 on-container #7D5410
Dark
accent #E0A547 on-accent #1F1306
container #3D2C0E on-container #F2D49B
Light
accent #3B6F8C on-accent #FFFFFF
container #D4E1EA on-container #234560
Dark
accent #7DA4CC on-accent #0F1A22
container #1A2630 on-container #B3CCE3
Light
accent #7A8A80 on-accent #FFFFFF
container #EDE8DD on-container #4A5A50
Dark
accent #8A8F97 on-accent #131418
container #232529 on-container #B5BBC3
Type scale
Tailwind defaults — not redeclared. Strict usage rules: 16px (default) for primary content (body, names, descriptions, timestamps, links), text-sm for <dt> labels, secondary metadata, and table headers, text-xs only for tiny badges. Never shrink primary content. Display headings (text-3xl and up) take tracking-tight (≈ -0.025em).
The quick brown fox
Tiny badges onlyThe quick brown fox
Labels, metadata, table headersThe quick brown fox
Primary content (default)The quick brown fox
Subheadings, standout textThe quick brown fox
Card titles, section headingsThe quick brown fox
Page headings (compact)The quick brown fox
Page headings (full)Font weights
Plus Jakarta Sans for everything. Tailwind defaults — only font-medium, font-semibold, and font-bold appear in normal use. Headings are font-semibold, sentence case.
The quick brown fox
Body copyThe quick brown fox
Form labels, nav itemsThe quick brown fox
Section headings, card titlesThe quick brown fox
Page headings, table headersEyebrow
The single sanctioned uppercase style. 11px / 600 / 0.16em letter-spacing, coloured text-on-surface-muted. Used for category labels above section headings, navigation group titles, and contextual badges that label content. Never on headings, body, badges, or anywhere else. Apply via <span class="koala-eyebrow">.
Sentence-case heading
Body copy follows the heading, with the eyebrow tucked above as a quiet category cue.
Radius
A single 5px radius for every UI surface. Buttons, badges, inputs, cards, modals, side trays, dropdowns, switches, segmented controls — every shape shares the same corner. Every Tailwind radius utility (rounded-sm, rounded-md, rounded-lg, rounded-xl, rounded-2xl) resolves to 5px. The only exception is rounded-full, reserved for genuinely circular elements: avatars, spinners, icon-in-circle containers, notification dots. Segmented controls and pills render with the standard 5px corner — not rounded-full.
rounded-md
5px (every size resolves to this)
rounded-full
9999px (sanctioned circles only)
Shadows
Never on containers.
Reserved for floating UI: dropdowns, autocomplete panels, popovers, tooltips, modals on desktop. Containers (cards, page sections, settings panels) separate via border-outline.
shadow-xs
Subtle separators
shadow-sm
Lifted action chips
shadow-md
Tooltips, toasts
shadow-lg
Dropdowns, modals
Shadows are opt-in.
Static containers never shadow — only floating UI (dropdowns, popovers, the pinned
sticky bar) applies an elevation utility. The system uses the generic
shadow-sm /
shadow-md /
shadow-lg
scale with no use-case-specific shadow tokens, so the elevation scale stays cleanly
re-themeable for white-label builds.
Motion
One duration token plus the project’s two easing curves. Used in @layer components rules and Alpine x-transition. Never write raw 0.2s ease-out in new code. See /motion for the full reference. Hover the box to preview.
--motion-default
0.2s · All UI motion
--easing-default: ease-out · --easing-emphasis: cubic-bezier(0.1, 0.5, 0.1, 1)
Z-index
Stacking is named, not numeric. Reach for the role token, not z-50. Modals and side trays share the same stacking level deliberately — they never coexist.
| Token | Value | Role |
|---|---|---|
| --z-base | 0 | Default flow |
| --z-raised | 10 | Stat tile hover, sticky table headers |
| --z-sticky | 20 | Sticky page headers, sub-nav bars |
| --z-dropdown | 30 | Autocomplete panels, action menus |
| --z-modal-backdrop | 40 | Modal / side-tray backdrop |
| --z-modal | 50 | Modals |
| --z-side-tray | 50 | Side tray content (modals and trays never coexist) |
| --z-toast | 60 | Notifications, toasts |
Focus
Focus rings are brand colour (plum), 2px solid, no offset. Validation-error focus swaps in a rose ring (--ring-color-focus-error). Values are tokenised so every component picks the same look. See /focus for the full reference. Click into a field to preview.
Light · --ring-color-focus
Dark · --ring-color-focus
--ring-color-focus · --ring-color-focus-error · --ring-width-focus: 2px · --ring-offset-focus: 0px
Edit DESIGN.md
to change tokens; run task tokens to regenerate
tokens.css. The narrative around each token (rules, do’s and don’ts) lives in DESIGN.md.