import { readdirSync, readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; // docs/DESIGN.md B9. The header + fade edge is one recipe, the `fade-below` / `fade-below-panel` / // `fade-above-panel` utilities in index.css: a crisp band and a 1rem gradient of the colour under // it. The table header carried its own arbitrary-value copy of it, which a change to the recipe (its // height, its colour on a panel) would have left behind. function sources(): Array<[string, string]> { const walk = (u: URL, prefix: string): Array<[string, string]> => readdirSync(u, { withFileTypes: true }).flatMap((e) => { if (e.isDirectory()) return walk(new URL(`${e.name}/`, u), `${prefix}${e.name}/`); if (!/\.tsx?$/.test(e.name) || e.name.includes(".test.")) return []; return [ [`${prefix}${e.name}`, readFileSync(new URL(e.name, u), "utf-8")] as [string, string], ]; }); return walk(new URL("../", import.meta.url), ""); } describe("the fade edge", () => { it("is the utility, never an arbitrary gradient of a surface colour", () => { const copies = sources() .filter(([, src]) => /linear-gradient\(to_(?:bottom|top),var\(--(?:bg|surface|card)\)/.test(src), ) .map(([name]) => name); expect(copies).toEqual([]); }); }); // A sticky header's fade shows only once content has slid under it. Drawn at rest it lay over the // first row's top 16 px, dimming it and hiding the top edge of the row's keyboard ring (seen // live). jsdom runs no scroll timeline, so what is pinned is the recipe: resting opacity 0, and the // scroll-driven fade-in over the first 1rem, only where the engine has scroll timelines. describe("the sticky header's fade", () => { const css = readFileSync(new URL("../index.css", import.meta.url), "utf-8"); const at = css.indexOf("@utility fade-below {"); const body = css.slice(at, css.indexOf("\n}\n", at)); it("rests invisible and fades in with the first 1rem of scroll", () => { expect(at, "the utility").toBeGreaterThan(0); expect(body).toMatch(/opacity:\s*0;/); expect(body).toMatch(/@supports \(animation-timeline: scroll\(\)\)/); expect(body).toMatch(/animation-timeline:\s*scroll\(nearest block\)/); expect(body).toMatch(/animation-range:\s*0 1rem/); expect(css).toMatch( /@keyframes fade-edge-in \{\s*from \{\s*opacity: 0;\s*\}\s*to \{\s*opacity: 1;/, ); }); });