/** * Font loading utility for the grain design system — the brand faces. * * Self-hosted OFL variable fonts (Archivo, Source Serif 4), embedded as * base64 woff2 with `font-display: block`. No ``, no Google Fonts * network request, no `display=swap` FOUT-by-design. * * `document.fonts.check()` ALONE IS NOT PROOF a face is actually loaded * and painting. The CSS font-matching algorithm it consults can be * satisfied by any registered face, including one that has merely been * *declared*, not successfully downloaded and parsed — so it can report * `true` for a face that never actually renders. loadFonts() therefore * runs a proof per face and reports it, rather than asserting it * silently: * * 1. FontFace status — `await face.load()`, then `face.status === 'loaded'`. * 2. `document.fonts.check()` for the face — required, but never * trusted alone (see above). * 3. Laid-out width divergence — identical text measured once with the * face in the stack and once forced onto the bare fallback generic. * Identical widths mean the fallback painted and steps 1/2 lied. * * A face is `proved` only when all three agree. See * foundry-business-hearth, proposal 20260802T2107, foundation/design-system.md §3.1. */ interface FontFaceSpec { family: string; base64: string; /** CSS font-weight range for the variable font. */ weightRange: string; /** Bare generic fallback used for the width-divergence probe. */ fallbackGeneric: string; } declare const FONT_FACES: FontFaceSpec[]; declare const PROOF_TEXT = "The quick brown fox jumps over the lazy dog 0123456789"; declare const PROOF_SIZE_PX = 48; declare const WIDTH_DIVERGENCE_EPSILON_PX = 1; export interface FontLoadFaceReport { family: string; /** Step 1: the FontFace object itself reports 'loaded'. */ faceLoaded: boolean; /** Step 2: document.fonts.check() for this family. Required, but see module doc — not trusted alone. */ checkPasses: boolean; /** Step 3: a laid-out width measured with this face in the stack differs from the * same text laid out with the bare fallback generic. */ widthDivergent: boolean; /** faceLoaded && checkPasses && widthDivergent — all three, none alone. */ proved: boolean; error?: string; } export interface FontLoadReport { faces: FontLoadFaceReport[]; ok: boolean; } /** * `display: 'block'` per `migration.md` §7's contract — mirrors * `scripts/build-grain-css.mjs`'s `fontFace()` CSS output (`font-display: block`) * so the JS runtime path `loadFonts()` actually calls, and the static * `dist/grain.css` `@font-face` rule, agree. The FontFace API defaults * `display` to `'auto'`, not `'block'`, when the descriptor omits it — * exported standalone so it is unit-testable without a real `FontFace` * implementation (vitest's node environment has neither, see load.test.ts). */ export declare function faceDescriptor(weightRange: string): FontFaceDescriptors; /** * Loads the self-hosted brand faces and returns a structured proof * report. Never throws — a failed face is reported, not thrown, so a * font regression degrades to the CSS fallback stack rather than * crashing app startup. Failures are logged loudly via console.error; * callers that need to gate on the result should inspect `report.ok`. */ declare function loadFonts(): Promise; export { loadFonts, FONT_FACES, PROOF_TEXT, PROOF_SIZE_PX, WIDTH_DIVERGENCE_EPSILON_PX };