diff --git a/src/components/ui/Theme/stories/TailwindBlackWhite.stories.tsx b/src/components/ui/Theme/stories/TailwindBlackWhite.stories.tsx new file mode 100644 index 000000000..664bbddf0 --- /dev/null +++ b/src/components/ui/Theme/stories/TailwindBlackWhite.stories.tsx @@ -0,0 +1,87 @@ +import React, { useEffect, useRef, useState } from 'react'; +import Theme from '../Theme'; + +// Checks the Tailwind preset's `black` and `white`: they must stay literal (#000 / #fff) in both +// appearances, while the Rad UI ramps (gray-50 and so on) follow the theme. Each sample reads the +// colour the browser actually paints and marks it pass or fail. + +type Expect = { prop: 'color' | 'backgroundColor' | 'borderTopColor'; rgb: [number, number, number] | null }; + +/** The painted colour of any CSS colour string (rgb, oklch, color-mix...), as 0–255 RGB. */ +function toRgb(css: string): [number, number, number] { + const canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 1; + const ctx = canvas.getContext('2d')!; + ctx.fillStyle = '#123456'; + ctx.fillStyle = css; + ctx.fillRect(0, 0, 1, 1); + const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data; + return [r, g, b]; +} + +const close = (a: number[], b: number[]) => a.every((v, i) => Math.abs(v - b[i]) <= 2); + +const Sample = ({ className, label, expect }: { className: string; label: string; expect: Expect }) => { + const ref = useRef(null); + const [painted, setPainted] = useState<[number, number, number] | null>(null); + + useEffect(() => { + const el = ref.current; + if (!el) return; + // Wait a frame so the theme's appearance class is applied. + const id = requestAnimationFrame(() => setPainted(toRgb(getComputedStyle(el)[expect.prop]))); + return () => cancelAnimationFrame(id); + }); + + const ok = painted && expect.rgb ? close(painted, expect.rgb) : null; + return ( +
+
+ {label} +
+ + {painted ? `rgb(${painted.join(', ')})` : '…'} + + {ok === null + ? ( + follows the theme + ) + : ( + + {ok ? 'PASS' : 'FAIL'} + + )} +
+ ); +}; + +const WHITE: [number, number, number] = [255, 255, 255]; +const BLACK: [number, number, number] = [0, 0, 0]; + +const Panel = ({ appearance }: { appearance: 'light' | 'dark' }) => ( + +
+

{appearance} appearance

+ + + + + +
+ +
+
+); + +export default { + title: 'Components/Theme/Tailwind black and white', + render: () => ( +
+ + +
+ ) +}; + +export const BothAppearances = {};