The dark palette lives as a single :root[data-theme='dark'] block in tokens.css; theme.ts and the pre-paint public/theme-init.js (external file because the prod CSP forbids inline scripts) always resolve the stored ui.theme.mode to a concrete data-theme, so 'system' needs no @media duplicate and follows live OS changes via matchMedia. color-scheme flips per theme (native controls/scrollbars), paired theme-color metas track the effective theme, and the new Appearance settings section offers the three-way choice as native radios (device-local, like #170). Label chips gain a chip-outline ring so arbitrary user colors stay separated on the dark canvas; useEffectiveTheme() is exported for the later pond-scoped theming stage (ADR 0018). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QRtCnB3uLdQtFmvp9HXcRX
106 lines
4.0 KiB
CSS
106 lines
4.0 KiB
CSS
/*
|
|
* Design tokens. Components consume only these variables — never raw
|
|
* values — so pond-level font settings (ADR 0016) and future theming stay
|
|
* one-line changes. The look is deliberately plain and professional.
|
|
*/
|
|
:root {
|
|
/* Native controls/scrollbars follow the active theme; the dark block
|
|
below flips this together with the palette (issue #180). */
|
|
color-scheme: light;
|
|
|
|
/* Font slots per ADR 0016; the real self-hosted fonts arrive with issue #66. */
|
|
--font-heading: 'Roboto', system-ui, -apple-system, 'Segoe UI', sans-serif;
|
|
--font-body: 'Roboto', system-ui, -apple-system, 'Segoe UI', sans-serif;
|
|
--font-mono: 'Fira Code', ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
--font-weight-heading: 400;
|
|
--font-weight-body: 300;
|
|
--font-weight-mono: 400;
|
|
|
|
/* Color palette: calm neutrals with one pond-green accent. */
|
|
--color-text: #1f2933;
|
|
--color-text-muted: #616e7c;
|
|
--color-bg: #ffffff;
|
|
--color-bg-subtle: #f5f7fa;
|
|
/* Elevated panels (modals, cards) — several rules already referenced this
|
|
* token with a #fff fallback; the fallback-less ones dropped their
|
|
* background entirely (#120). */
|
|
--color-surface: #ffffff;
|
|
/* Muted pill/badge surfaces (member seats, count pills, neutral badges).
|
|
Was a fallback-only value scattered through base.css before #180. */
|
|
--color-surface-muted: #e2e8f0;
|
|
--color-border: #d9e2ec;
|
|
/* Boundaries of interactive controls (inputs, selects) need 3:1 against
|
|
both backgrounds (WCAG 1.4.11) — #d9e2ec is only ~1.3:1. */
|
|
--color-border-input: #7d8a97;
|
|
--color-accent: #2f6f4f;
|
|
--color-accent-contrast: #ffffff;
|
|
--color-danger: #ab091e;
|
|
/* Text sitting ON a danger background (buttons, notification badge) —
|
|
paired with --color-danger the same way --color-accent-contrast is
|
|
paired with --color-accent; the two must flip together per theme. */
|
|
--color-danger-contrast: #ffffff;
|
|
--color-danger-strong: #8a0718;
|
|
--color-ok: #14803c;
|
|
/* Favorite stars and tree icons (issue #132) — a readable gold. ICON
|
|
use only: 3.25:1 passes the 3:1 non-text minimum but NOT the 4.5:1
|
|
required for text (audit A11Y-024c). */
|
|
--color-favorite: #b8860b;
|
|
/* Status badges (admin system page): pastel surface + strong text, each
|
|
pair ≥ 4.5:1 in both themes (asserted by theme-contrast.test.ts). */
|
|
--color-badge-ok-bg: #d6f2df;
|
|
--color-badge-ok-text: #1d6f42;
|
|
--color-badge-error-bg: #fbe3e0;
|
|
--color-badge-error-text: #a02818;
|
|
--color-badge-warn-bg: #fdf0d4;
|
|
--color-badge-warn-text: #8a5a00;
|
|
/* Separation ring for user-colored label chips/swatches — invisible on
|
|
light, a faint light outline on dark where arbitrary label colors can
|
|
melt into the background. */
|
|
--color-chip-outline: transparent;
|
|
|
|
/* Spacing scale (rem-based). */
|
|
--space-1: 0.25rem;
|
|
--space-2: 0.5rem;
|
|
--space-3: 0.75rem;
|
|
--space-4: 1rem;
|
|
--space-6: 1.5rem;
|
|
--space-8: 2rem;
|
|
|
|
--radius: 6px;
|
|
--topbar-height: 3rem;
|
|
--sidebar-width: 16rem;
|
|
}
|
|
|
|
/*
|
|
* Dark palette (issue #180). public/theme-init.js and theme/theme.ts always
|
|
* resolve the user's Light/Dark/System choice to a CONCRETE data-theme on
|
|
* <html> — there is deliberately no @media fallback here, so this single
|
|
* selector is the whole contract for later theming stages (ADR 0018).
|
|
* Every pair is contrast-asserted by theme-contrast.test.ts; border-input
|
|
* and favorite pass ≥ 3:1 against both palettes and stay unchanged.
|
|
*/
|
|
:root[data-theme='dark'] {
|
|
color-scheme: dark;
|
|
|
|
--color-text: #e2e8f0;
|
|
--color-text-muted: #a7b3c0;
|
|
--color-bg: #10161d;
|
|
--color-bg-subtle: #161d26;
|
|
--color-surface: #1b2430;
|
|
--color-surface-muted: #26303c;
|
|
--color-border: #323e4d;
|
|
--color-accent: #5cb88a;
|
|
--color-accent-contrast: #10161d;
|
|
--color-danger: #f87171;
|
|
--color-danger-contrast: #10161d;
|
|
--color-danger-strong: #fa8f8f;
|
|
--color-ok: #4ade80;
|
|
--color-badge-ok-bg: #1b3d2b;
|
|
--color-badge-ok-text: #8fdcb0;
|
|
--color-badge-error-bg: #43181d;
|
|
--color-badge-error-text: #f3a8a8;
|
|
--color-badge-warn-bg: #3c3113;
|
|
--color-badge-warn-text: #e3c06b;
|
|
--color-chip-outline: rgba(255, 255, 255, 0.18);
|
|
}
|