hex everywhere, because the palettes were half broken
Reported as "the ellipsis doesn't show in the streaming bubble". It was the visible corner of something much larger: every Tailwind semantic utility was broken under all eleven ported palettes. `@theme` mapped `--color-muted-foreground: hsl(var(--muted-foreground))`, which needs an unwrapped triplet. A ported palette publishes `#6e6a86`, so the mapping produced `hsl(#6e6a86)` — invalid — and the utility fell back to white or transparent: duck-dark text-muted-foreground=rgb(148,163,184) bg-muted=rgb(30,41,59) rose-pine text-muted-foreground=rgb(255,255,255) bg-muted=rgba(0,0,0,0) The themes looked convincing because panels, the page and the header read their variables DIRECTLY rather than through a utility class. Everything that went through Tailwind was quietly wrong, and the thinking dots — `bg-muted-foreground/60` — were simply invisible. So: 123 HSL triplets converted to hex across the three hand-written themes, and 37 `hsl(var(--x))` mappings unwrapped. One format everywhere, which is also the format every published palette ships in. The triplets only ever existed so `bg-card/50` could get opacity. That was tested earlier tonight and turned out unnecessary — Tailwind v4 emits `color-mix(in oklab, var(--card) 50%, transparent)` for any colour format. Having proved it, I then left the mappings wrapped, which is what broke the palettes. Verified across six themes: `bg-muted` and `bg-muted-foreground/60` resolve in every one of them, and Duck's values are unchanged through the conversion — `bg-muted` is `rgb(30,41,59)` before and after. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
── How a value reaches a component ──
|
||||
|
||||
component .bg-surface
|
||||
@theme (here) --color-surface: hsl(var(--surface))
|
||||
@theme (here) --color-surface: var(--surface)
|
||||
theme file --surface: 0 0% 100%
|
||||
|
||||
The indirection through `var()` is load-bearing: Tailwind resolves `@theme` at BUILD time, so a theme
|
||||
@@ -57,64 +57,77 @@
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Values are HEX, not unwrapped HSL triplets.
|
||||
============================================================================================
|
||||
|
||||
This block used to read `--color-card: hsl(var(--card))`, which requires every theme to publish an
|
||||
unwrapped triplet (`0 0% 100%`). That was done so `bg-card/50` could get opacity — and it turned out to
|
||||
be unnecessary: Tailwind v4 emits `color-mix(in oklab, var(--card) 50%, transparent)` for any colour
|
||||
format, with a `@supports` fallback. Tested before changing it.
|
||||
|
||||
It was also actively wrong the moment a theme arrived in hex: `hsl(#6e6a86)` is invalid, so every
|
||||
semantic utility silently fell back to white or transparent. Eleven ported palettes looked correct only
|
||||
because panels and the page read their variables directly rather than through a utility class.
|
||||
*/
|
||||
@theme {
|
||||
/* ── Semantic colour, the whole vocabulary a component is allowed to use ──────────────────────── */
|
||||
|
||||
--color-border: hsl(var(--border));
|
||||
--color-input: hsl(var(--input));
|
||||
--color-ring: hsl(var(--ring));
|
||||
--color-background: hsl(var(--background));
|
||||
--color-foreground: hsl(var(--foreground));
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
|
||||
--color-brand: hsl(var(--brand));
|
||||
--color-brand-foreground: hsl(var(--brand-foreground));
|
||||
--color-brand-muted: hsl(var(--brand-muted));
|
||||
--color-brand: var(--brand);
|
||||
--color-brand-foreground: var(--brand-foreground);
|
||||
--color-brand-muted: var(--brand-muted);
|
||||
|
||||
--color-primary: hsl(var(--primary));
|
||||
--color-primary-foreground: hsl(var(--primary-foreground));
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
|
||||
--color-secondary: hsl(var(--secondary));
|
||||
--color-secondary-foreground: hsl(var(--secondary-foreground));
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
|
||||
--color-destructive: hsl(var(--destructive));
|
||||
--color-destructive-foreground: hsl(var(--destructive-foreground));
|
||||
--color-destructive: var(--destructive);
|
||||
--color-destructive-foreground: var(--destructive-foreground);
|
||||
|
||||
--color-success: hsl(var(--success));
|
||||
--color-success-foreground: hsl(var(--success-foreground));
|
||||
--color-success: var(--success);
|
||||
--color-success-foreground: var(--success-foreground);
|
||||
|
||||
--color-warning: hsl(var(--warning));
|
||||
--color-warning-foreground: hsl(var(--warning-foreground));
|
||||
--color-warning: var(--warning);
|
||||
--color-warning-foreground: var(--warning-foreground);
|
||||
|
||||
--color-info: hsl(var(--info));
|
||||
--color-info-foreground: hsl(var(--info-foreground));
|
||||
--color-info: var(--info);
|
||||
--color-info-foreground: var(--info-foreground);
|
||||
|
||||
/* The sixth state, and the reason it exists: `running` is neither good news nor a warning. The chat
|
||||
tray, the send button and the queue all wore amber for it, and the token set had nothing to offer —
|
||||
`--status-in-progress` is blue, and `warning` would make a working job look like a failing one.
|
||||
`-ink` is the readable-on-a-tint variant: the fill is bright enough to sit behind black text, which
|
||||
makes it far too light to BE text. Every tone will want this eventually; busy needed it first. */
|
||||
--color-busy: hsl(var(--busy));
|
||||
--color-busy-foreground: hsl(var(--busy-foreground));
|
||||
--color-busy-ink: hsl(var(--busy-ink));
|
||||
--color-busy: var(--busy);
|
||||
--color-busy-foreground: var(--busy-foreground);
|
||||
--color-busy-ink: var(--busy-ink);
|
||||
|
||||
--color-muted: hsl(var(--muted));
|
||||
--color-muted-foreground: hsl(var(--muted-foreground));
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
|
||||
--color-accent: hsl(var(--accent));
|
||||
--color-accent-foreground: hsl(var(--accent-foreground));
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
|
||||
--color-popover: hsl(var(--popover));
|
||||
--color-popover-foreground: hsl(var(--popover-foreground));
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
|
||||
--color-card: hsl(var(--card));
|
||||
--color-card-foreground: hsl(var(--card-foreground));
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
|
||||
--color-cta: hsl(var(--cta));
|
||||
--color-cta-foreground: hsl(var(--cta-foreground));
|
||||
--color-ctahover: hsl(var(--ctahover));
|
||||
--color-cta: var(--cta);
|
||||
--color-cta-foreground: var(--cta-foreground);
|
||||
--color-ctahover: var(--ctahover);
|
||||
|
||||
--color-naturegreen: hsl(var(--naturegreen));
|
||||
--color-naturegreen-foreground: hsl(var(--naturegreen-foreground));
|
||||
--color-naturegreen: var(--naturegreen);
|
||||
--color-naturegreen-foreground: var(--naturegreen-foreground);
|
||||
|
||||
/*
|
||||
── The brand slots ────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user