Skip to content

Native fonts package, token font variables, and radius/type/motion/density roles (#64, #65, #66) - #75

Merged
joeblau merged 4 commits into
mainfrom
claude/hig-64-65-66
Oct 15, 2026
Merged

joeblau merged 4 commits into
mainfrom
claude/hig-64-65-66

Conversation

@joeblau

@joeblau joeblau commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Closes #64. Closes #65. Refs #66: shipped here. The Chart follow-up that deletes hig-roles.generated.css is bloxwap/chart#55.

#64: native TTFs leave the web tokens package

  • New package @bloxwap/hig-native-fonts (packages/native-fonts). It contains fonts/*.ttf (still generated by npm run generate:fonts, now written here), fonts/manifest.json, fonts/OFL.txt, a typed fontManifest export (src/index.ts, generated from the manifest and drift-checked) and ./manifest.
  • @bloxwap/hig-tokens drops ./fonts/* and assets/fonts, so the tarball now holds only CSS, JS and the mark.
  • Bytes, names and hashes are unchanged: the generator rewrote an identical manifest, and the quality gate still hashes every face.
  • release:dry-run fails if any non-native-fonts tarball contains .ttf/.otf/.woff*. It also records packedSizes.webTokens / packedSizes.nativeFonts in the release report.
  • smoke:packed-native checks that the installed @bloxwap/hig-native-fonts hashes match the generator manifest, that Metro export bundles the three registered faces (by SHA-256), and that the installed tokens package has no font files.
  • The gallery, gesture-sheet entry and Metro baseline now require('@bloxwap/hig-native-fonts/fonts/…').
  • Migration notes are in docs/migration/native.md#native-fonts-moved-out-of-hig-tokens, including Satori/OG users and monorepo#4540. docs/installation.md and the font budget rows in docs/governance/quality.md are updated.

#65: control CSS reads token font variables

  • @bloxwap/hig-tokens/css defines --hig-font-sans: var(--font-bloxwap-sans, 'Bloxwap Sans'), system-ui, sans-serif and the matching --hig-font-mono. Next hosts that put the @bloxwap/font loader .variable classes on <html> get the loaded faces with no bridge. Plain @font-face hosts fall back to the literal families.
  • .hig-button / .hig-number use var(--hig-font-sans|mono, <literal fallbacks>).
  • smoke:packed-next now serves the packed Next consumer and runs next-browser-smoke against it. It asserts that the computed first family of .hig-button / .hig-number passes document.fonts.check and that a FontFace of that family has status === 'loaded', since check alone also returns true when no face matches. The workflow now installs Playwright before this step.
  • browser-smoke (Chromium/Firefox/WebKit) runs the same check on the plain @font-face Vite fixture and expects exactly Bloxwap Sans / Bloxwap Mono.
  • The SFX bridge removal is a follow-up in bloxwap/sfx after this candidate is vendored.

#66: radius, type, motion and density roles

  • --hig-radius-{sm,md,lg,xl,pill}.
  • --hig-type-{caption,label,body,title,display}-{size,line-height,weight}, plus a --hig-type-<role> font shorthand.
  • --hig-motion-duration-{fast,base,slow} and --hig-motion-easing-standard. Durations are 0ms under prefers-reduced-motion: reduce and [data-hig-motion='reduced'].
  • --hig-density-{comfortable,compact}-{control,target,gap}, plus active --hig-density-{control,target,gap} that switch under [data-hig-density='compact'].
  • All of these are generated from packages/tokens/source.json, which gains easing and font. nativeMetrics is unchanged, so native bundles are unaffected.
  • A new negative test changes one role in the generated CSS and asserts that check-drift.mjs fails.
  • The roles are documented in docs/foundations/platform.md and in docs/installation.md (Tokens only / plain CSS table and example).
  • The docs CSS budget goes from 110,000 to 115,000 B (measured 110,618 B with the roles).

Proof (local, Node 22.22.0 / npm 11.6.1)

Command Result
npm run generate:fonts (fonttools 4.61.1, brotli 1.2.0) 15 faces; manifest byte-identical to the committed one
npm run check (build, lint + drift, typecheck, 94 vitest + native jest) pass
npm run examples:check 212 modules pass
npm run quality pass (docsCSS 110,618 ≤ 115,000; native face hashes verified from packages/native-fonts)
smoke:ssr, smoke:packed, smoke:packed-boundaries, smoke:packed-examples pass
smoke:packed-native pass: "Packed native fonts: hashes match the generator manifest and Metro bundles the registered faces"
smoke:packed-next pass. In the packed consumer, .hig-button → BloxwapSans, "Bloxwap Sans Scripts", … with check: true, loaded: true, and .hig-number → BloxwapMono, … with check: true, loaded: true (evidence/packed-next-browser.json). Before this change the literal 'Bloxwap Sans' could never match the loader's BloxwapSans family
browser-smoke m5 ×3 engines (plain @font-face font check added) pass
feedback and docs browser smokes; expo-web gallery (registers BloxwapSansRegular/SemiBold from the new package) pass
release:dry-run --channel candidate + smoke:release-artifact pass. Packed @bloxwap/hig-tokens: 8,109 B, 0 font files (was about 4.3 MB). @bloxwap/hig-native-fonts: 4,420,375 B, 15 TTFs
catalog visual gate (local VM) Field/error 3.13% and Input/error 2.44% over 2%, from text antialiasing only (same glyphs; captures inspected). The docs catalog controls in those states don't use the changed selectors. CI is the reference renderer for these baselines; see the CI result below

Device checks: no KVM, so Android emulator and iOS Simulator runs were not repeated. Native registration is proven by the Metro export (Android/iOS) and the Expo-web gallery only.

No publish, tag, release or deploy.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HIG control CSS should use token font variables, not literal family names Move native TTFs out of the web @bloxwap/hig-tokens package

1 participant