fix(ui): theme the onboarding wizard decorative panel instead of hardcoding dark (#11379)
The onboarding wizard's decorative right-hand panel, which holds the ASCII paperclip illustration, hardcoded a near-black surface. `ThemeContext` supports light and dark and follows `prefers-color-scheme`, so in light mode a new customer met the product as a pale form beside a solid black rectangle — on the one screen meant to introduce it. The glyphs inside already used `text-muted-foreground`, so the panel was the only part ignoring the theme. It now uses the paired `bg-muted` surface, which is defined in both themes (`oklch(0.97 0 0)` light, `oklch(0.269 0 0)` dark), so the illustration reads as ink on a surface either way and follows any future theme without another fix. The guard for it asserts the complete set of `bg-` classes on the panel is `["bg-muted"]`, anchored to the `<AsciiArtAnimation />` wrapper rather than scanning the file. Forbidding specific spellings is what failed here originally: the first version checked `bg-[#rrggbb]` and silently stopped guarding anything once master migrated the class to `bg-(--hex-1d1d1d)`. Naming what is allowed cannot decay that way, and it catches named colours like `bg-black` that no spelling list covered. Lands the work from #8982 by @stubbi, whose two commits are included unchanged with their authorship. The rebase and the guard are mine. Tested: ui typecheck clean; both theme cases fail against four spellings of the regression — `bg-(--hex-1d1d1d)`, `bg-[#1d1d1d]`, `bg-black`, `bg-zinc-900` — where the original caught only one and my first widening caught two. Full ui suite 3958 pass, with one timezone-dependent IssueProperties failure present on master. All CI gates green; Greptile 5/5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
a53cc8819b
commit
35a9b98733
|
|
@ -2155,7 +2155,7 @@ function OnboardingWizardInner({
|
|||
name + mission steps) */}
|
||||
<div
|
||||
className={cn(
|
||||
"hidden md:block overflow-hidden bg-(--hex-1d1d1d) transition-(--tp-width-opacity) duration-500 ease-in-out",
|
||||
"hidden md:block overflow-hidden bg-muted text-muted-foreground transition-(--tp-width-opacity) duration-500 ease-in-out",
|
||||
step === 1 || step === 2 ? "w-1/2 opacity-100" : "w-0 opacity-0"
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
// @vitest-environment node
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
// The onboarding wizard's decorative right-hand panel (which renders the
|
||||
// ASCII paperclip illustration) must follow the active shadcn theme instead of
|
||||
// hardcoding a dark surface. Otherwise a light/cream deployer theme (set via
|
||||
// the PAPERCLIP_DEFAULT_THEME bootstrap) renders a jarring cream form next to a
|
||||
// solid dark panel. The illustration glyphs already use `text-muted-foreground`,
|
||||
// so the panel must sit on the paired `bg-muted` surface to read as an
|
||||
// intentional ink-on-surface texture in every theme.
|
||||
//
|
||||
// Asserting against the source keeps this guard cheap: the panel className is a
|
||||
// static string literal, and the full wizard dialog is too heavy to mount here.
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
describe("OnboardingWizard decorative panel theming", () => {
|
||||
const source = readFileSync(
|
||||
path.join(here, "OnboardingWizard.tsx"),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
/**
|
||||
* The className expression on the wrapper around <AsciiArtAnimation />.
|
||||
* Anchoring here keeps both guards on the decorative panel itself - the same
|
||||
* tokens appear elsewhere in the wizard. `[^<>]` stops the match spanning
|
||||
* into other JSX elements.
|
||||
*/
|
||||
function panelClassNames(): string | null {
|
||||
return (
|
||||
source.match(/className=\{cn\(([^<>]*)\)\}\s*>\s*<AsciiArtAnimation\s*\/>/)?.[1] ??
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
it("gives the decorative panel no background but the muted token", () => {
|
||||
// Scoped to the panel, not the file. A file-wide scan would fail this
|
||||
// case for a hardcoded colour anywhere else in the wizard, reporting a
|
||||
// panel regression that had not happened.
|
||||
//
|
||||
// Asserted as the complete set of `bg-` classes rather than a list of
|
||||
// spellings to forbid. The previous version scanned for `bg-[#rrggbb]`
|
||||
// alone, and passed unchanged once master migrated this class to
|
||||
// `bg-(--hex-1d1d1d)` - so it guarded nothing at all for a while. A named
|
||||
// colour like `bg-black` or `bg-zinc-900` would have slipped through the
|
||||
// same way. Naming what is allowed cannot rot like that.
|
||||
const panel = panelClassNames();
|
||||
expect(panel).not.toBeNull();
|
||||
const backgrounds = panel!.match(/\bbg-[^\s"'`,]+/g) ?? [];
|
||||
expect(backgrounds).toEqual(["bg-muted"]);
|
||||
});
|
||||
|
||||
it("themes the decorative panel with shadcn surface tokens", () => {
|
||||
// Anchor to the wrapper around <AsciiArtAnimation /> so the guard checks
|
||||
// the decorative panel itself (the same tokens appear elsewhere in the
|
||||
// wizard), then assert each token independently so a class reorder or a
|
||||
// utility inserted between them cannot false-fail the test. `[^<>]`
|
||||
// keeps the match from spanning across other JSX elements.
|
||||
const panel = source.match(
|
||||
/className=\{cn\(([^<>]*)\)\}\s*>\s*<AsciiArtAnimation\s*\/>/
|
||||
);
|
||||
expect(panel).not.toBeNull();
|
||||
expect(panel?.[1]).toMatch(/\bbg-muted\b(?!-)/);
|
||||
expect(panel?.[1]).toMatch(/\btext-muted-foreground\b/);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue