diff --git a/apps/web/src/panel-metric-tokens.test.ts b/apps/web/src/panel-metric-tokens.test.ts new file mode 100644 index 0000000..cbe7b45 --- /dev/null +++ b/apps/web/src/panel-metric-tokens.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from "vitest"; +import { readFileSync } from "node:fs"; + +const styles = readFileSync(new URL("./styles.css", import.meta.url), "utf8"); + +function cssBlock(selector: string) { + const start = styles.indexOf(`${selector} {`); + if (start < 0) throw new Error(`Missing CSS rule for ${selector}`); + const end = styles.indexOf("}", start); + if (end < 0) throw new Error(`Unclosed CSS rule for ${selector}`); + return styles.slice(start, end + 1); +} + +function customProperty(block: string, name: string) { + const match = block.match(new RegExp(`\\s${name}:\\s*([^;]+);`)); + if (!match) throw new Error(`Missing ${name}`); + return match[1].trim(); +} + +describe("shared panel and metric typography", () => { + it("uses defined Hearth-aligned geometry and type aliases", () => { + const root = cssBlock(":root"); + + expect(customProperty(root, "--s1")).toBe("4px"); + expect(customProperty(root, "--type-xs")).toBe("11px"); + expect(customProperty(root, "--type-sm")).toBe("12px"); + expect(customProperty(root, "--type-body")).toBe("14px"); + expect(customProperty(root, "--type-title")).toBe("26px"); + expect(customProperty(root, "--r-md")).toBe("11px"); + + expect(cssBlock(".metric")).toContain("gap: var(--s1)"); + expect(cssBlock(".metric")).toContain("border-radius: var(--r-md)"); + expect(cssBlock(".metric-label")).toContain("font-size: var(--type-xs)"); + expect(cssBlock(".metric-value")).toContain("font-size: var(--type-title)"); + expect(cssBlock(".metric-sub")).toContain("font-size: var(--type-sm)"); + expect(cssBlock(".panel-title")).toContain("font-size: var(--type-body)"); + expect(cssBlock(".panel-hint")).toContain("font-size: var(--type-xs)"); + }); + + it("leaves operational and topology styling separate", () => { + expect(cssBlock(".state-pill")).toContain("color: var(--c, var(--s-unknown))"); + expect(cssBlock(".map")).toContain("--map-panel"); + expect(cssBlock(".suggest-chip")).toContain("border-radius: 999px"); + }); +}); diff --git a/apps/web/src/styles.css b/apps/web/src/styles.css index 0af1dd8..47d0fd3 100644 --- a/apps/web/src/styles.css +++ b/apps/web/src/styles.css @@ -47,6 +47,9 @@ /* Hearth's compact metadata type role. Geometry continues through the established --s* and --r-* aliases until #222's wider export lands. */ --type-xs: 11px; + --type-sm: 12px; + --type-body: 14px; + --type-title: 26px; --shadow: var(--hearth-shadow); --t: 140ms cubic-bezier(0.22, 0.61, 0.36, 1); @@ -487,7 +490,7 @@ kbd { .metric { display: flex; flex-direction: column; - gap: 2px; + gap: var(--s1); padding: var(--s4); background: var(--surface); border: 1px solid var(--border); @@ -495,20 +498,20 @@ kbd { } .metric-label { - font-size: 11px; + font-size: var(--type-xs); letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted-deep); } .metric-value { - font-size: 26px; + font-size: var(--type-title); font-weight: 700; letter-spacing: -0.02em; } .metric-sub { - font-size: 12px; + font-size: var(--type-sm); color: var(--muted); } @@ -541,7 +544,7 @@ kbd { display: flex; align-items: center; gap: var(--s2); - font-size: 14px; + font-size: var(--type-body); } .panel-title svg { @@ -549,7 +552,7 @@ kbd { } .panel-hint { - font-size: 11.5px; + font-size: var(--type-xs); color: var(--muted-deep); margin-left: var(--s2); }