diff --git a/tickets/032-greyscale-theme-rework.md b/tickets/032-greyscale-theme-rework.md deleted file mode 100644 index 3bc7d5b..0000000 --- a/tickets/032-greyscale-theme-rework.md +++ /dev/null @@ -1,47 +0,0 @@ -# 032 — Rework colour themes as greyscale equivalents with accent-only colour - -## Summary - -Today's colour themes (`webapp/src/index.css`) only vary `--c-accent`/ -`--c-accent-soft`/`--c-accent-text` per theme (`html.red`, `.green`, `.blue`, -`.yellow`, `.pink`, `.orange` at lines 97-130) — every theme shares the same -dark `:root` surfaces/borders/text. `html.grey` (lines 75-95) is a Tailwind -slate palette, which reads blue-tinted rather than neutral. The wishlist -wants: `light`/`dark` as true black-and-white inverses of each other, `grey` -as true neutral greyscale, and every other named theme (`red`/`green`/etc.) -to desaturate its named hue to the matching greyscale lightness for -surfaces/borders/text, reserving actual saturated colour for accents only. - -## Scope - -- `:root` (dark) and `html.light`: convert to a true black/white greyscale - scale (surfaces, borders, text all `#000`–`#fff` greys, inverted between - the two), replacing the current warm/cool-tinted neutrals. -- `html.grey`: replace the slate-derived palette (lines 76-90) with the same - neutral greyscale scale as dark/light (i.e. `grey` becomes the canonical - greyscale theme other themes derive from), keeping only its own accent - distinct if one is still wanted. -- `red`/`green`/`blue`/`yellow`/`pink`/`orange`: for each, desaturate that - hue to compute a matching greyscale lightness scale for - `--c-surface-*`/`--c-border-*`/`--c-text-*` (e.g. convert the hue to HSL, - zero the saturation, keep the lightness steps), then keep that theme's - actual saturated colour only on `--c-accent*`. -- Document the derivation (e.g. a short comment or a small script/table) so a - new themed variant can be added consistently later. - -## Acceptance criteria - -- `light` and `dark` are literal greyscale inverses of each other (every - `--c-*` value in one is the other's lightness mirrored, no hue). -- `grey` uses a neutral (zero-saturation) scale, not a blue-tinted one. -- Each named colour theme's surfaces/borders/text render as neutral greys at - a lightness matching that colour's own luminance, with the named colour - visible only via `--c-accent*`. - -## Depends on - -None. - -## Depended on by - -- [033 — Downloadable/uploadable custom themes](033-custom-theme-download-upload.md) diff --git a/webapp/src/index.css b/webapp/src/index.css index 60be19b..a486bf3 100644 --- a/webapp/src/index.css +++ b/webapp/src/index.css @@ -9,12 +9,16 @@ * so re-declaring the `--c-*` variables under a `html.` selector * re-skins the whole app with no per-component changes. * - * `dark` is the baseline defined on :root; every other theme only needs to - * override what differs. `light`/`grey` swap the full surface+text scale; - * the seven colour themes (red/green/blue/yellow/pink/orange/grey... ) keep - * `dark`'s neutral surfaces and swap only the accent trio, since accent is - * the only place the app uses a non-neutral colour (selection states, the - * timeline histogram, focus/active highlights). + * Derivation: surfaces/borders/text are always neutral (S=0) greys, never + * hue-tinted. `dark`/`grey` share one true black-based grey scale, `light` + * is its exact lightness mirror. Each named theme (red/green/blue/yellow/ + * pink/orange) takes its hue's real Tailwind swatch at each scale step, + * converts it to HSL, zeroes the saturation, and keeps that lightness as + * its grey — so e.g. red's surface-0 is grey at red-950's lightness, not + * neutral-950. Colour survives only on --c-accent*. To add a new themed + * variant: pick its hue's Tailwind swatches at 950/900/800/700/600/500/ + * 400/300/100, desaturate each (HSL S->0), and slot the greys into the + * same --c-surface-*/--c-border-*/--c-text-* roles used below. */ :root { color-scheme: light dark; @@ -45,22 +49,24 @@ --c-accent-text: #7dd3fc; } +/* Exact lightness mirror of :root's neutral-950..50 scale (Tailwind neutral, + steps flipped: 950<->50, 900<->100, 800<->200, 700<->300, 600<->400). */ html.light { - --c-surface-0: #fafaf9; - --c-surface-1: #f5f5f4; - --c-surface-2: #e7e5e4; + --c-surface-0: #fafafa; + --c-surface-1: #f5f5f5; + --c-surface-2: #e5e5e5; - --c-border: #d6d3d1; - --c-border-soft: #a8a29e; - --c-border-faint: #e7e5e4; - --c-border-strong: #57534e; + --c-border: #e5e5e5; + --c-border-soft: #d4d4d4; + --c-border-faint: #f5f5f5; + --c-border-strong: #525252; - --c-text: #1c1917; - --c-text-secondary: #44403c; - --c-text-muted: #78716c; - --c-text-subtle: #a8a29e; - --c-text-faint: #d6d3d1; - --c-text-faintest: #d6d3d1; + --c-text: #171717; + --c-text-secondary: #404040; + --c-text-muted: #525252; + --c-text-subtle: #737373; + --c-text-faint: #a3a3a3; + --c-text-faintest: #d4d4d4; --c-danger-bg: #fee2e2; --c-danger-border: #fca5a5; @@ -72,59 +78,163 @@ html.light { --c-accent-text: #0369a1; } +/* Canonical greyscale theme: identical neutral scale to :root (dark), so + every other theme's derivation above is measured against this one. */ html.grey { - --c-surface-0: #020617; - --c-surface-1: #0f172a; - --c-surface-2: #1e293b; + --c-surface-0: #0a0a0a; + --c-surface-1: #171717; + --c-surface-2: #262626; - --c-border: #1e293b; - --c-border-soft: #334155; - --c-border-faint: #0f172a; - --c-border-strong: #94a3b8; + --c-border: #262626; + --c-border-soft: #404040; + --c-border-faint: #171717; + --c-border-strong: #a3a3a3; - --c-text: #f1f5f9; - --c-text-secondary: #cbd5e1; - --c-text-muted: #94a3b8; - --c-text-subtle: #64748b; - --c-text-faint: #475569; - --c-text-faintest: #334155; + --c-text: #f5f5f5; + --c-text-secondary: #d4d4d4; + --c-text-muted: #a3a3a3; + --c-text-subtle: #737373; + --c-text-faint: #525252; + --c-text-faintest: #404040; --c-accent: #38bdf8; --c-accent-soft: #0369a1; --c-accent-text: #7dd3fc; } +/* red-950..300/100 desaturated (S->0), lightness kept */ html.red { + --c-surface-0: #282828; + --c-surface-1: #4e4e4e; + --c-surface-2: #5a5a5a; + + --c-border: #5a5a5a; + --c-border-soft: #6a6a6a; + --c-border-faint: #4e4e4e; + --c-border-strong: #b4b4b4; + + --c-text: #f0f0f0; + --c-text-secondary: #d0d0d0; + --c-text-muted: #b4b4b4; + --c-text-subtle: #9a9a9a; + --c-text-faint: #818181; + --c-text-faintest: #6a6a6a; + --c-accent: #f87171; --c-accent-soft: #b91c1c; --c-accent-text: #fca5a5; } +/* green-950..300/100 desaturated (S->0), lightness kept */ html.green { + --c-surface-0: #1a1a1a; + --c-surface-1: #343434; + --c-surface-2: #3d3d3d; + + --c-border: #3d3d3d; + --c-border-soft: #4a4a4a; + --c-border-faint: #343434; + --c-border-strong: #949494; + + --c-text: #ececec; + --c-text-secondary: #bbbbbb; + --c-text-muted: #949494; + --c-text-subtle: #747474; + --c-text-faint: #5c5c5c; + --c-text-faintest: #4a4a4a; + --c-accent: #4ade80; --c-accent-soft: #15803d; --c-accent-text: #86efac; } +/* blue-950..300/100 desaturated (S->0), lightness kept */ html.blue { + --c-surface-0: #363636; + --c-surface-1: #545454; + --c-surface-2: #666666; + + --c-border: #666666; + --c-border-soft: #7a7a7a; + --c-border-faint: #545454; + --c-border-strong: #adadad; + + --c-text: #ececec; + --c-text-secondary: #c8c8c8; + --c-text-muted: #adadad; + --c-text-subtle: #989898; + --c-text-faint: #888888; + --c-text-faintest: #7a7a7a; + --c-accent: #60a5fa; --c-accent-soft: #1d4ed8; --c-accent-text: #93c5fd; } +/* yellow-950..300/100 desaturated (S->0), lightness kept */ html.yellow { + --c-surface-0: #242424; + --c-surface-1: #424242; + --c-surface-2: #4a4a4a; + + --c-border: #4a4a4a; + --c-border-soft: #545454; + --c-border-faint: #424242; + --c-border-strong: #888888; + + --c-text: #e0e0e0; + --c-text-secondary: #a2a2a2; + --c-text-muted: #888888; + --c-text-subtle: #797979; + --c-text-faint: #676767; + --c-text-faintest: #545454; + --c-accent: #facc15; --c-accent-soft: #a16207; --c-accent-text: #fde047; } +/* pink-950..300/100 desaturated (S->0), lightness kept */ html.pink { + --c-surface-0: #2c2c2c; + --c-surface-1: #4e4e4e; + --c-surface-2: #5a5a5a; + + --c-border: #5a5a5a; + --c-border-soft: #6b6b6b; + --c-border-faint: #4e4e4e; + --c-border-strong: #b3b3b3; + + --c-text: #f2f2f2; + --c-text-secondary: #d0d0d0; + --c-text-muted: #b3b3b3; + --c-text-subtle: #9a9a9a; + --c-text-faint: #818181; + --c-text-faintest: #6b6b6b; + --c-accent: #f472b6; --c-accent-soft: #be185d; --c-accent-text: #f9a8d4; } +/* orange-950..300/100 desaturated (S->0), lightness kept */ html.orange { + --c-surface-0: #252525; + --c-surface-1: #474747; + --c-surface-2: #565656; + + --c-border: #565656; + --c-border-soft: #676767; + --c-border-faint: #474747; + --c-border-strong: #9b9b9b; + + --c-text: #eaeaea; + --c-text-secondary: #b8b8b8; + --c-text-muted: #9b9b9b; + --c-text-subtle: #888888; + --c-text-faint: #7b7b7b; + --c-text-faintest: #676767; + --c-accent: #fb923c; --c-accent-soft: #c2410c; --c-accent-text: #fdba74;