feat(design): glass surfaces by role, the static Badge, LinkButton, user pickers on the Combobox (E28 S3d1)

This commit is contained in:
2026-09-25 00:34:35 +02:00
parent db10bfbbca
commit c47c193ad1
89 changed files with 818 additions and 478 deletions
@@ -0,0 +1,44 @@
import { fileURLToPath } from "node:url";
import { ESLint } from "eslint";
import { describe, expect, it } from "vitest";
// `local/surface-role` (eslint.config.js): a glass surface is a role from ui/surfaces.ts, which brings
// its radius (docs/DESIGN.md B5; E28 S3d). ~140 sites paired three tiers with five radii; this pins
// that the rule keeping a new one out fires, and where it stands down.
const RULE = "local/surface-role";
const FRONTEND = fileURLToPath(new URL("../../", import.meta.url));
async function hits(source: string, filePath = "src/components/__probe.tsx"): Promise<number> {
const eslint = new ESLint({ cwd: FRONTEND });
const [result] = await eslint.lintText(source, { filePath });
return (result?.messages ?? []).filter((m) => m.ruleId === RULE).length;
}
describe("a bare glass tier", () => {
it("is refused in a class string, a template fragment and a ui/ composite", async () => {
expect(await hits('export const A = () => <div className="glass rounded-2xl p-4" />;\n')).toBe(
1,
);
expect(await hits('export const B = "glass-menu w-60 rounded-xl p-2";\n')).toBe(1);
expect(await hits("export const C = (x: string) => `glass-card ${x}`;\n")).toBe(1);
expect(
await hits(
'export const D = () => <div className="glass p-3" />;\n',
"src/components/ui/Toaster.tsx",
),
).toBe(1);
});
it("passes as a lone value, through a role, and in the role table itself", async () => {
expect(await hits('export const E = { variant: "glass" };\n')).toBe(0);
expect(
await hits(
'import { SURFACE } from "./ui/surfaces";\nexport const F = () => <div className={`${SURFACE.card} p-3`} />;\n',
),
).toBe(0);
expect(
await hits('export const G = "glass rounded-2xl";\n', "src/components/ui/surfaces.ts"),
).toBe(0);
});
});