Resolved: light

Color

Save the Dogs colors flow through three layers: raw primitive ramps (--p-*, theme-independent) feed semantic tokens (--color-*, re-pointed per skin) which feed the Tailwind utilities (bg-accent, text-muted…). Components read semantic tokens only, so flipping :root ↔ .dark re-skins everything. Dark is the default; :root holds the LIGHT values and .dark overrides them. Flip the theme in the top bar to watch the semantic swatches move while the primitive ramps stay put.

Primitives (--p-*)
Theme-independent raw palette: the full beryl accent ramp, the cool neutral ramp, status ramps, data-viz series, and hairline/glow alphas. Never change between light and dark.
Semantic (--color-*)
Purpose-named tokens (accent, surface, text, border, status…) defined on :root for LIGHT and overridden in .dark for DARK. This is what you should reach for in app code.
Tailwind utilities
@theme inline maps the semantic tokens to classes: bg-accent / text-accent / bg-surface / text-muted / bg-good etc. The gray-* ramp is remapped to the neutrals and indigo-* to beryl 1:1.
No bg-bg / text-text
There is no bg-bg or text-text utility. For page background / primary text use bg-background / text-foreground (legacy bridges) or the arbitrary form bg-[var(--color-bg)] / text-[var(--color-text)].

Beryl accent ramp

The Save the Dogs teal — the brand accent primitive (--p-beryl-50 … --p-beryl-950), 50 lightest → 950 darkest. Theme-independent: identical in light and dark. The semantic accent tokens pick stops out of this ramp (light leans darker for AA contrast, dark leans brighter). Also re-exposed 1:1 as the Tailwind indigo-* ramp.

--p-beryl-50#e6f8f5
--p-beryl-100#c4efe8
--p-beryl-200#90dfd4
--p-beryl-300#50cbbf
--p-beryl-400#1bb6ab
--p-beryl-500#13a89f
--p-beryl-600#00948d
--p-beryl-700#007671
--p-beryl-800#005654
--p-beryl-900#053b3a
--p-beryl-950#072928
--p-beryl-50 … --p-beryl-950 (= indigo-50 … indigo-950)
Light accent stops
--color-accent = beryl-700, accent-strong = beryl-800, accent-hover = beryl-600, accent-soft = beryl-50. Shifted one stop darker so text-accent on accent-soft clears WCAG AA.
Dark accent stops
--color-accent = beryl-500, accent-strong = beryl-600, accent-hover = beryl-400, accent-soft = beryl-950.
indigo-* alias
@theme inline maps --color-indigo-50…950 to these exact beryl values, so every legacy indigo-* utility renders Save the Dogs teal with no edits.

Neutral ramp

The cool neutral primitive (--p-neutral-0 … --p-neutral-950). Note the Save the Dogs ordering: 0 is DARKEST and 950 is LIGHTEST — the reverse of Tailwind's default gray. These drive every surface, text and border in the dark skin, and are re-exposed (reversed, so gray-50 = lightest) as the Tailwind gray-* ramp.

--p-neutral-0#080a0e
--p-neutral-50#0c0e13
--p-neutral-100#12151a
--p-neutral-150#191c22
--p-neutral-200#21242b
--p-neutral-250#2a2e35
--p-neutral-300#373b42
--p-neutral-400#4f5359
--p-neutral-500#71747c
--p-neutral-600#8e929a
--p-neutral-700#adb1b9
--p-neutral-800#d1d4da
--p-neutral-900#e2e5e9
--p-neutral-950#f1f4f6
--p-neutral-0 … --p-neutral-950 (0 = darkest, 950 = lightest)
Dark surfaces
bg = neutral-0, surface = neutral-100, surface-2 = neutral-150, surface-3 = neutral-200, overlay = neutral-250, tooltip = neutral-300 — a tight near-black stack that gives subtle elevation.
Dark text
text = neutral-900, muted = neutral-700, faint = neutral-500; on-accent / on-danger = neutral-0.
gray-* alias
@theme inline maps gray-50…950 to these REVERSED (gray-50 = neutral-950 = lightest … gray-950 = neutral-0 = darkest), matching Tailwind's light→dark convention.

Semantic — accent

The purpose-named accent tokens. Every one is re-pointed between skins (light pulls darker beryl stops for contrast; dark pulls brighter ones). Use these — not the raw beryl ramp — for interactive accent surfaces and text. The swatches resolve live to the current theme.

--color-accent · bg-accent / text-accentlight beryl-700 · dark beryl-500
--color-accent-hover · hover:bg-accent-hoverlight beryl-600 · dark beryl-400
--color-accent-strong · text-accent-stronglight beryl-800 · dark beryl-600
--color-accent-soft · bg-accent-softlight beryl-50 · dark beryl-950
resolved value shown for current theme
--color-on-accentlight #f7fbfa · dark neutral-0
--color-linklight beryl-700 · dark beryl-300
--color-link-hoverlight beryl-800 · dark beryl-200
--color-accent-ringlight #00948d73 · dark #13a89f99
accent support tokens
differs per skin
ALL accent tokens are re-pointed in .dark. Light chooses darker beryl stops (beryl-700/800) for WCAG AA; dark chooses brighter ones (beryl-500/400/300).
--color-on-accent
Foreground (label) color to sit ON an accent fill. on-accent on beryl-700 = 5.26:1 (light); neutral-0 on beryl-500 in dark.
text-accent on accent-soft
The canonical accent chip pairing — beryl-700 on beryl-50 = 4.99:1, clears AA. Used by .chip-husky / .chip-lead.
--color-accent-ring
The translucent outer focus-ring color (fed into --fx-focus-ring). Not a Tailwind utility — read via the var.

Semantic — surfaces & background

The elevation stack. In light these are near-white tints; in dark they are a tight near-black neutral stack. bg is the page canvas; surface → surface-2 → surface-3 step up in elevation; overlay/tooltip sit above. The scrim/seat are translucent.

--color-bg · bg-backgroundlight #f9fafc · dark neutral-0
--color-surface · bg-surfacelight #ffffff · dark neutral-100
--color-surface-2 · bg-surface-2light #f5f7f9 · dark neutral-150
--color-surface-3 · bg-surface-3light #f0f2f5 · dark neutral-200
elevation stack
--color-overlaylight #ffffff · dark neutral-250
--color-tooltiplight #1b1e25 · dark neutral-300
--color-scrimlight #0f121a66 · dark #07090d9e
--p-seat-30#0000004d (primitive)
overlays & scrims
differs per skin
Every surface token is re-pointed. Light = white/near-white tints; dark = neutral-0…300. Overlay/tooltip invert most dramatically (light overlay is white, dark is neutral-250).
Utilities
bg-surface / bg-surface-2 / bg-surface-3 are generated. For the page canvas use bg-background (bridge) or bg-[var(--color-bg)] — there is no bg-bg.
--color-overlay / --color-tooltip
Backgrounds for popovers/menus (overlay) and tooltips. No dedicated utility — read via the var or rely on the components that consume them.
--color-scrim
Translucent backdrop behind modals/drawers. --p-seat-30 is the inner-glow seat shadow alpha used in the dark inner-glow recipe.

Semantic — text

The three-tier text hierarchy. Primary body text is inherited from <body> (= --color-text); use text-muted for secondary and text-faint for the quietest captions/placeholders. The two foreground-on-fill tokens (on-accent / on-danger) pair with their fills.

--color-text · text-foregroundlight #1b1e25 · dark neutral-900
--color-muted · text-mutedlight #494d55 · dark neutral-700
--color-faint · text-faintlight #71747c · dark neutral-500
text tiers
--color-on-accentlight #f7fbfa · dark neutral-0
--color-on-dangerlight #fdf9f8 · dark neutral-0
foreground-on-fill

Primary — text-[var(--color-text)] / inherited body

Secondary — text-muted

Faint — text-faint (captions, placeholders)

legibility over bg-surface
Primary
Inherited from <body>; for an explicit primary use text-[var(--color-text)] or the text-foreground bridge. There is no text-text utility.
text-muted / text-faint
Generated utilities for the secondary and tertiary tiers. faint is the quietest pass-AA tier (placeholders, timestamps, the swatch captions in this very page).
differs per skin
All text tokens re-point. on-accent / on-danger flip to near-black neutral-0 in dark so labels on the mid-tone fills clear AA (neutral-0 on bad-500 = 5.87:1).

Semantic — borders & rules

Four hairline tiers from barely-there to a defined edge. In light these are tinted-black alphas; in dark they are white alphas (so lines READ as light-on-dark). Shown as fills here to expose the alpha, plus a live border preview.

--color-hairlinelight #0f121a0d · dark #ffffff0b
--color-rulelight #0f121a14 · dark #ffffff14
--color-border · border-border-softlight #0f121a1f · dark #ffffff1a
--color-border-strong · border-border-stronglight #0f121a33 · dark #ffffff24
hairline tiers (shown as fills to reveal alpha)
border-hairline
border-rule
border-border-soft
border-border-strong
live borders over bg-surface
Utilities
Only border (--color-border) and border-strong are exposed as Tailwind classes: border-border-soft and border-border-strong. hairline and rule are var-only.
Ladder
hairline (faintest, internal dividers) → rule (table/list rules) → border (default card/control edge) → border-strong (emphasised / overlay edges).
differs per skin
All four flip sign: light = black alphas (#0f121a…), dark = white alphas (#ffffff…). They are translucent, so they only render against a surface.

Semantic — status

Good / warn / bad, each a text token paired with a soft -bg fill. In light the text tokens were darkened so text-good/warn/bad clear WCAG AA on their soft backgrounds; the -bg surfaces are unchanged. In dark they map onto the status ramp primitives. These tokens also drive the .chip-* helpers.

--color-good · text-good / bg-goodlight #1f7a44 · dark good-500 #46b86e
--color-warn · text-warn / bg-warnlight #7a5300 · dark warn-500 #ebae42
--color-bad · text-bad / bg-badlight #b3251f · dark bad-500 #ec5c52
--color-good-bglight #dffae5 · dark good-950 #0e2b18
--color-warn-bglight #fff0d1 · dark warn-950 #37290f
--color-bad-bglight #ffe9e5 · dark bad-950 #3f1916
status text + soft background pairs
Good — text-good on bg-good-bg
Warn — text-warn on bg-warn-bg
Bad — text-bad on bg-bad-bg
live text-on--bg pairings (the AA-tuned combination)
--p-good-300#78d694
--p-good-500#46b86e
--p-good-600#329d5a
--p-good-950#0e2b18
--p-warn-300#facc7e
--p-warn-500#ebae42
--p-warn-600#d69727
--p-warn-950#37290f
--p-bad-300#fe8c80
--p-bad-500#ec5c52
--p-bad-600#d1433c
--p-bad-950#3f1916
status ramp primitives (--p-good/warn/bad-*)
Utilities
text-good / text-warn / text-bad and bg-good / bg-warn / bg-bad are generated from the TEXT tokens. The soft -bg surfaces are var-only (bg-[var(--color-good-bg)] …).
Pairing rule
Always pair the text token with its matching -bg fill — that combination is the one tuned to pass AA (good 4.83:1, warn 6.08:1, bad 5.64:1 in light).
differs per skin
Light darkens the text tokens for AA; dark maps text→ramp-500 and -bg→ramp-950. The light -bg surfaces are bespoke tints, not the -950 primitives.
Never color alone
Status is conveyed by a label/icon too, never by the hue alone (the chips below carry text).

Data-viz series

A six-color categorical series (--p-viz-1 … --p-viz-6) for charts. Theme-independent primitives — the same six hues in light and dark — ordered for maximum adjacent-hue separation. viz-1 is the brand teal so single-series charts read on-brand.

--p-viz-1#0fb6ac (teal)
--p-viz-2#7ba3f6 (blue)
--p-viz-3#e1a536 (amber)
--p-viz-4#d179ca (orchid)
--p-viz-5#6abf6e (green)
--p-viz-6#ea856b (coral)
--p-viz-1 … --p-viz-6
Usage
Assign in order (viz-1, viz-2, …) so categorical series stay consistent across charts. Read via the var: fill='var(--p-viz-3)' or bg-[var(--p-viz-3)].
viz-1 = brand
viz-1 is the brand teal; a single-series or primary metric chart lands on-brand automatically.
does NOT differ per skin
These are primitives — identical in light and dark. They are chosen mid-tone so they read on both the near-white and near-black canvases.

Chip helpers (.chip-*)

The .chip base + .chip-* color classes from globals.css — pill labels built from the semantic tokens (so they re-skin automatically). Risk chips keep red/amber/green because the hue carries meaning; team and role chips map onto accent/status families. Add .chip-sm for the compact size.

chip-redchip-yellowchip-green
risk-level chips — hue is meaningful, but text is always present
chip-huskychip-bulldogchip-terrier
team chips
chip-leadchip-directorchip-advise-againstchip-neutral
role / advisory / neutral chips
chip-sm chip-redchip-sm chip-huskychip-sm chip-leadchip-sm chip-neutral
compact size — append .chip-sm
.chip
Base pill: inline-flex, pill radius, 12px / weight 540, 2px 10px padding, transparent border. .chip-sm shrinks to 11px and 1px 8px.
chip-red / -yellow / -green
Risk levels. Built from bad / warn / good token families (bg = -bg, border = token @35%, text = token). Kept hue-coded because risk is meaningful — but always render the label too.
chip-husky / -bulldog / -terrier
Team chips. husky → accent (beryl), bulldog → bad family, terrier → good family. The hue is decorative team identity, not status.
chip-lead / -director
Roles → brand accent. lead uses --color-accent; director uses the deeper --color-accent-strong with a stronger border.
chip-advise-against
Advisory flag → warn family (amber). chip-neutral → surface-3 fill + muted text for non-semantic labels.
differs per skin
Every chip re-skins automatically — they reference only --color-* semantic tokens (and color-mix on them), so light/dark is handled by the token layer.