| 1 | import { readdirSync, readFileSync, statSync } from "node:fs"; |
| 2 | import { describe, expect, it } from "vitest"; |
| 3 | import { resolveWhale } from "./whale-tokens"; |
| 4 | import { siteCss } from "./site-css"; |
| 5 | |
| 6 | // The site's type follows GPUI set_theme: Shannon Sans for every role, the |
| 7 | // system monospace stack at 13px for code, no serif and no all-caps labels. |
| 8 | const CSS = siteCss(); |
| 9 | const LAYOUT = readFileSync(new URL("../app/[locale]/layout.tsx", import.meta.url), "utf8"); |
| 10 | const TAILWIND = readFileSync(new URL("../tailwind.config.ts", import.meta.url), "utf8"); |
| 11 | const FONTS = new URL("../public/brand/fonts/", import.meta.url); |
| 12 | |
| 13 | function rootVar(name: string): string { |
| 14 | const match = CSS.match(new RegExp(`--${name}:\\s*([^;]+);`)); |
| 15 | if (!match) throw new Error(`Missing --${name}`); |
| 16 | return match[1].replace(/\s+/g, " "); |
| 17 | } |
| 18 | |
| 19 | describe("typography contract", () => { |
| 20 | it("loads no serif or webfont mono and sets no all-caps", () => { |
| 21 | expect(CSS).not.toMatch(/Newsreader|IBM Plex|JetBrains|--font-serif/); |
| 22 | expect(CSS).not.toMatch(/text-transform:\s*uppercase/); |
| 23 | expect(LAYOUT).not.toMatch(/next\/font\/google/); |
| 24 | expect(TAILWIND).not.toMatch(/Newsreader|JetBrains|widest|wider/); |
| 25 | expect(TAILWIND).toMatch(/textTransform:\s*false/); |
| 26 | }); |
| 27 | |
| 28 | it("leaves no caller on the ungenerated all-caps or wide-tracking utilities", () => { |
| 29 | // Tailwind no longer emits these, so a surviving class is a silent no-op |
| 30 | // that reads as intent. Labels are sentence case at normal tracking. |
| 31 | const offenders: string[] = []; |
| 32 | for (const dir of ["../app/", "../components/"]) { |
| 33 | for (const entry of readdirSync(new URL(dir, import.meta.url), { recursive: true })) { |
| 34 | const path = String(entry); |
| 35 | if (!path.endsWith(".tsx")) continue; |
| 36 | const source = readFileSync(new URL(dir + path, import.meta.url), "utf8"); |
| 37 | if (/\b(uppercase|tracking-wider|tracking-widest)\b/.test(source)) offenders.push(dir + path); |
| 38 | } |
| 39 | } |
| 40 | expect(offenders).toEqual([]); |
| 41 | }); |
| 42 | |
| 43 | it("resolves every role to Shannon Sans and code to the system mono stack", () => { |
| 44 | expect(rootVar("font-body")).toMatch(/^var\(--font-shannon-latin\), var\(--font-shannon-ext\),/); |
| 45 | expect(rootVar("font-display")).toBe("var(--font-body)"); |
| 46 | expect(rootVar("font-mono")).toMatch(/^ui-monospace,/); |
| 47 | expect(rootVar("font-cjk")).not.toMatch(/Serif|(?<!sans-)serif/); |
| 48 | expect(resolveWhale(rootVar("text-mono"))).toBe("0.8125rem"); |
| 49 | expect(rootVar("text-prose")).toBe("1.0625rem"); |
| 50 | }); |
| 51 | |
| 52 | it("preloads only the Latin subset of Shannon Sans", () => { |
| 53 | const faces = [...LAYOUT.matchAll(/localFont\(\{([\s\S]*?)\n\}\);/g)].map((m) => m[1]); |
| 54 | expect(faces).toHaveLength(2); |
| 55 | const [latin, ext] = faces; |
| 56 | expect(latin).toContain("ShannonSans-Variable-latin.woff2"); |
| 57 | expect(latin).not.toContain("preload: false"); |
| 58 | expect(ext).toContain("ShannonSans-Variable-ext.woff2"); |
| 59 | expect(ext).toContain("preload: false"); |
| 60 | for (const face of faces) expect(face).toContain('prop: "unicode-range"'); |
| 61 | // The Latin face is the only font on the critical path; keep it small. |
| 62 | expect(statSync(new URL("ShannonSans-Variable-latin.woff2", FONTS)).size).toBeLessThan(80_000); |
| 63 | expect(statSync(new URL("ShannonSans-Variable-ext.woff2", FONTS)).size).toBeGreaterThan(0); |
| 64 | }); |
| 65 | }); |
| 66 |