The screenshots said seven dark screens differed and could not say why. Comparing COMPUTED theme variables between the old build and the new one, in the same colour mode, answered it in a single run: 47 of 53 matched exactly, and the six that did not were the `--duck-*` variables — EMPTY in the new build. The utilities were remapped to `--brand-primary`, `--ink` and friends, which is correct. What that missed is code reading the VARIABLE rather than using the utility. `prose.css` does exactly that eleven times — chat markdown links, blockquotes, inline code, checkboxes — and `landing/styles.css` does it six more. All silently resolved to nothing. Fixed by keeping the legacy names as aliases in every theme, pointing at the new source of truth rather than duplicating values. That also covers any plugin or inline style doing the same thing, which I have not enumerated. They retire when the utility rename lands. Every variable now matches. ── What the screenshots are actually worth here ── Less than I assumed. The wallpaper rotates per page load, so a control capture of the same build against itself had 4 of 16 screens differ — and a different 4 each time. Between 5 and 6 come out byte-identical to the baseline on any run; the rest is noise I mistook for signal for two rounds, including one where I removed a `color-scheme` line to test a theory it turned out not to explain. The variable diff is the instrument that settles it, and it is cheap. Added as `scripts/theme-vardiff.ts`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
137 lines
6.2 KiB
CSS
137 lines
6.2 KiB
CSS
/*
|
|
DUCK — DARK
|
|
============================================================================================
|
|
|
|
A sibling of Duck Light, not a variant of it. The owner's decision: light and dark are two themes, which
|
|
removes "colour mode" from the application as a concept.
|
|
|
|
It is written out in full rather than as a diff against light. Overriding would be shorter and would
|
|
reintroduce exactly the coupling this refactor removes — a theme that only makes sense in the presence of
|
|
another is not something a third party can author or a customer can buy. A complete file is also the only
|
|
way an author can see what they have to supply.
|
|
|
|
`applyDarkClass` is set for this theme in the registry, so the 396 `dark:` utilities still written into
|
|
components keep resolving. That is a compatibility shim with a known end date, not part of the design.
|
|
|
|
Values are the `.dark` block of the original `globals.css`, unchanged. Where light defined something that
|
|
the original dark block did not override (`--duck-yellow`, `--duck-orange`), the light value is repeated
|
|
here — that is what the browser was computing before, made explicit.
|
|
*/
|
|
|
|
[data-theme='duck-dark'] {
|
|
/* ── Brand — lightened for contrast on a dark ground ──────────────────────────────────────────
|
|
`--ink` flipping to near-white is the clearest evidence that `duck-dark` was never a brand colour. */
|
|
--brand-primary: #22d3ee;
|
|
--brand-accent: #f4c430;
|
|
--brand-warm: #ea580c;
|
|
--brand-deep: #4ade80;
|
|
--ink: #f1f5f9;
|
|
--surface-warm: #292524;
|
|
|
|
/* ── Legacy variable names, for direct `var()` consumers ──────────────────────────────────────
|
|
The `duck-*` UTILITIES are remapped in base.css, but a stylesheet reading `var(--duck-teal)` gets
|
|
nothing from that — it wants the variable itself. `prose.css` does exactly this eleven times for chat
|
|
markdown (links, blockquotes, inline code, checkboxes), and the rename silently emptied all of them.
|
|
Caught by diffing computed variables between the old and new builds, not by reading.
|
|
|
|
Kept as aliases rather than as values: there is one source of truth above, and these follow it. They
|
|
retire when nothing reads them — the same migration as the utility names. */
|
|
--duck-teal: var(--brand-primary);
|
|
--duck-dark: var(--ink);
|
|
--duck-yellow: var(--brand-accent);
|
|
--duck-forest: var(--brand-deep);
|
|
--duck-beige: var(--surface-warm);
|
|
--duck-orange: var(--brand-warm);
|
|
|
|
/* ── The page ─────────────────────────────────────────────────────────────────────────────────── */
|
|
--page-bg: hsl(222.2 84% 4.9%);
|
|
--pixel-grid-color: rgba(200, 230, 210, 0.08);
|
|
|
|
/* ── Surfaces ─────────────────────────────────────────────────────────────────────────────────── */
|
|
--glass-bg: rgba(0, 0, 0, 0.2);
|
|
--glass-border: rgba(255, 255, 255, 0.1);
|
|
|
|
--panel-bg: rgba(0, 0, 0, 0.2);
|
|
--panel-border: rgba(255, 255, 255, 0.1);
|
|
--panel-radius: 0.75rem;
|
|
--panel-header-bg: rgba(0, 0, 0, 0.15);
|
|
--panel-header-fg: hsl(var(--foreground));
|
|
--panel-accent: var(--brand-primary);
|
|
--panel-grid-color: rgba(200, 230, 210, 0.06);
|
|
|
|
--dock-bg: rgba(0, 0, 0, 0.3);
|
|
--dock-border: rgba(255, 255, 255, 0.08);
|
|
--dock-tooltip-bg: rgba(0, 0, 0, 0.85);
|
|
|
|
--card-bg: rgba(10, 20, 15, 0.9);
|
|
--card-grid-color: rgba(200, 230, 210, 0.06);
|
|
|
|
/* ── Semantic colour ──────────────────────────────────────────────────────────────────────────── */
|
|
--background: 222.2 84% 4.9%;
|
|
--foreground: 210 40% 98%;
|
|
|
|
--card: 222.2 84% 4.9%;
|
|
--card-foreground: 210 40% 98%;
|
|
|
|
--popover: 222.2 84% 4.9%;
|
|
--popover-foreground: 210 40% 98%;
|
|
|
|
--primary: 210 40% 98%;
|
|
--primary-foreground: 222.2 47.4% 11.2%;
|
|
|
|
--secondary: 217.2 32.6% 17.5%;
|
|
--secondary-foreground: 210 40% 98%;
|
|
|
|
--muted: 217.2 32.6% 17.5%;
|
|
--muted-foreground: 215 20.2% 65.1%;
|
|
|
|
--accent: 211 82% 64%;
|
|
--accent-foreground: 0 0% 100%;
|
|
|
|
--destructive: 0 62.8% 30.6%;
|
|
--destructive-foreground: 210 40% 98%;
|
|
|
|
--success: 142.1 76.2% 36.3%;
|
|
--success-foreground: 355.7 100% 97.3%;
|
|
|
|
--brand: 145 60% 45%;
|
|
--brand-foreground: 0 0% 100%;
|
|
--brand-muted: 145 30% 25%;
|
|
|
|
--warning: 43 96% 56%;
|
|
--warning-foreground: 0 0% 0%;
|
|
|
|
/* Lifted well above the light value — 50% lightness violet on a near-black background reads as muddy
|
|
grey at 12px, which is exactly the size it is used at. */
|
|
--info: 263 90% 70%;
|
|
--info-foreground: 0 0% 100%;
|
|
|
|
--border: 217.2 32.6% 17.5%;
|
|
--input: 217.2 32.6% 17.5%;
|
|
--ring: 212.7 26.8% 83.9%;
|
|
|
|
/* The original dark block never overrode these, so the light values were in force. Written out because
|
|
a theme that silently inherits from another is the thing this file exists to stop. */
|
|
--cta: 211 74.77% 45.58%;
|
|
--cta-foreground: 0 0% 100%;
|
|
--ctahover: 211 91.53% 34.82%;
|
|
|
|
--naturegreen: 145 79% 38%;
|
|
--naturegreen-foreground: 0 0% 100%;
|
|
|
|
--radius: 0.5rem;
|
|
|
|
/* ── Data visualisation and status ────────────────────────────────────────────────────────────── */
|
|
--chart-primary: 211 82% 64%;
|
|
--chart-secondary: 211 82% 84%;
|
|
--status-success: 142 76% 36%;
|
|
--status-warning: 43 96% 56%;
|
|
--status-in-progress: 211 82% 64%;
|
|
|
|
/* ── Chrome ───────────────────────────────────────────────────────────────────────────────────── */
|
|
--scrollbar-thumb: #444;
|
|
--scrollbar-thumb-hover: #555;
|
|
--scrollbar-thin-thumb: rgb(128 128 128 / 0.3);
|
|
--scrollbar-thin-thumb-hover: rgb(128 128 128 / 0.5);
|
|
}
|