From b9ef2ccf9558e9113f7fab34bd01b06a27720954 Mon Sep 17 00:00:00 2001 From: Marco Beretta <81851188+berry-13@users.noreply.github.com> Date: Tue, 29 Sep 2026 08:02:34 +0200 Subject: [PATCH 1/6] fix: Draw Library Colours From Theme Roles and Record the Exceptions PixelCard resolves its variant palettes from theme channel variables when it lays out pixels, so the image generation placeholder follows the active theme in dark mode instead of drawing a fixed light slate palette. The SAML glyph and Codey mark take the text colour. The new theme/allowlist.md records every colour that deliberately sits outside the theme (brand marks, artwork, theme definitions, library elevation ink) and why. --- eslint-suppressions.json | 5 -- packages/client/src/components/PixelCard.tsx | 49 +++++++++++++++++--- packages/client/src/svgs/CodeyIcon.tsx | 2 +- packages/client/src/svgs/SamlIcon.tsx | 2 +- packages/client/src/theme/allowlist.md | 41 ++++++++++++++++ 5 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 packages/client/src/theme/allowlist.md diff --git a/eslint-suppressions.json b/eslint-suppressions.json index c356e3cb194..27d7fcfa3a8 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -2385,11 +2385,6 @@ "count": 7 } }, - "packages/client/src/svgs/SamlIcon.tsx": { - "shadcn/no-raw-colors": { - "count": 1 - } - }, "packages/client/src/svgs/SendMessageIcon.tsx": { "shadcn/no-inline-styles": { "count": 1 diff --git a/packages/client/src/components/PixelCard.tsx b/packages/client/src/components/PixelCard.tsx index 904e2fc05b5..a539da3f680 100644 --- a/packages/client/src/components/PixelCard.tsx +++ b/packages/client/src/components/PixelCard.tsx @@ -139,13 +139,50 @@ const getEffectiveSpeed = (value: number, reducedMotion: boolean) => { const clamp = (n: number, min = 0, max = 1) => Math.min(Math.max(n, min), max); +/** Variant palettes name theme channel variables, read off the card when the pixels are laid + * out, so the canvas draws the active theme instead of a fixed light palette. */ const VARIANTS = { - default: { gap: 5, speed: 35, colors: '#f8fafc,#f1f5f9,#cbd5e1', noFocus: false }, - blue: { gap: 10, speed: 25, colors: '#e0f2fe,#7dd3fc,#0ea5e9', noFocus: false }, - yellow: { gap: 3, speed: 20, colors: '#fef08a,#fde047,#eab308', noFocus: false }, - pink: { gap: 6, speed: 80, colors: '#fecdd3,#fda4af,#e11d48', noFocus: true }, + default: { + gap: 5, + speed: 35, + colors: '--surface-primary-alt,--surface-tertiary,--border-medium', + noFocus: false, + }, + blue: { + gap: 10, + speed: 25, + colors: '--status-info-subtle,--status-info-border,--status-info', + noFocus: false, + }, + yellow: { + gap: 3, + speed: 20, + colors: '--status-warning-subtle,--status-warning-border,--status-warning', + noFocus: false, + }, + pink: { + gap: 6, + speed: 80, + colors: '--status-error-subtle,--status-error-border,--status-error', + noFocus: true, + }, } as const; +/** Resolves `--token` entries to the element's channel triplet; any other entry is a CSS color + * the caller supplied and passes through. A token the page does not define falls back to the + * canvas ink rather than drawing nothing. */ +const resolvePalette = (palette: string, element: Element): string[] => { + const style = getComputedStyle(element); + return palette.split(',').map((entry) => { + const color = entry.trim(); + if (!color.startsWith('--')) { + return color; + } + const channels = style.getPropertyValue(color).trim(); + return channels ? `rgb(${channels})` : 'currentColor'; + }); +}; + interface PixelCardProps { variant?: keyof typeof VARIANTS; gap?: number; @@ -262,7 +299,7 @@ export default function PixelCard({ canvasRef.current.width = Math.floor(cw); canvasRef.current.height = Math.floor(ch); - const cols = palette.split(','); + const cols = resolvePalette(palette, containerRef.current); const px: Pixel[] = []; const cx = cw / 2; @@ -355,7 +392,7 @@ export default function PixelCard({ >