Tracking now comes from androidx's TypeScaleTokens.kt as sp/16 rem, so Display Large is -0.0125rem, Title Medium and Body Medium 0.0125rem where material-web's pre-Expressive numbers stood. The emphasized set gets its own --md-sys-typescale-emphasized-<style>-tracking, which the emphasized utilities read: M3 widens four of them and takes Display Large back to zero. Every regular utility sets `font-variation-settings: normal`, because the property inherits and body copy inside an emphasized heading is meant to read as itself. bin/check-font.mjs (fontkit) prints the packaged woff2's fvar axes, so a re-subset cannot silently drop the ROND axis the emphasized styles depend on; `npm run check:font` runs it and tests/Feature/FontTest.php runs it through the configured node binary, skipping when node cannot run (PHP cannot open a woff2 without Brotli). Plan: docs/plans/material-3-alignment.md step 4, findings core C5, C6, C7. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
61 lines
2.2 KiB
JavaScript
61 lines
2.2 KiB
JavaScript
/**
|
||
* Checks that the packaged Google Sans Flex subset still carries the variable axes the CSS uses.
|
||
*
|
||
* Run from the repository root with `npm run check:font` (or `node bin/check-font.mjs [woff2]`).
|
||
* tests/Feature/FontTest.php runs it through the configured `node` binary as well, because PHP
|
||
* cannot open a woff2 without the Brotli extension.
|
||
*
|
||
* wght 400–700 — every typescale weight (font.css `font-weight: 400 700`, type.css's regular,
|
||
* medium and bold reference tokens).
|
||
* ROND 0–100 — the roundness axis every `type-emphasized-*` utility sets to 100. Re-subset
|
||
* the font without it and the emphasized styles quietly stop being round.
|
||
*
|
||
* Output is one JSON object on stdout — {file, postscriptName, numGlyphs, axes: {tag: {name, min,
|
||
* default, max}}} — and the exit status is 1, with the reason on stderr, when an axis is missing
|
||
* or narrower than the range above.
|
||
*/
|
||
import { openSync } from 'fontkit'
|
||
import { fileURLToPath } from 'node:url'
|
||
|
||
/** The axes the stylesheets depend on, and the range each one has to cover. */
|
||
const REQUIRED = {
|
||
wght: { min: 400, max: 700 },
|
||
ROND: { min: 0, max: 100 },
|
||
}
|
||
|
||
const file = process.argv[2] ?? fileURLToPath(new URL('../resources/fonts/google-sans-flex/GoogleSansFlex-Latin.woff2', import.meta.url))
|
||
|
||
let font
|
||
|
||
try {
|
||
font = openSync(file)
|
||
} catch (error) {
|
||
process.stderr.write(`${file}: ${error.message}\n`)
|
||
process.exit(1)
|
||
}
|
||
|
||
const axes = font.variationAxes ?? {}
|
||
const problems = Object.entries(REQUIRED).flatMap(([tag, range]) => {
|
||
const axis = axes[tag]
|
||
|
||
if (!axis) {
|
||
return [`${tag} is missing; the subset has ${Object.keys(axes).join(', ') || 'no variable axes'}.`]
|
||
}
|
||
|
||
return axis.min > range.min || axis.max < range.max
|
||
? [`${tag} covers ${axis.min}–${axis.max}, not the ${range.min}–${range.max} the stylesheets ask for.`]
|
||
: []
|
||
})
|
||
|
||
process.stdout.write(`${JSON.stringify({
|
||
file,
|
||
postscriptName: font.postscriptName,
|
||
numGlyphs: font.numGlyphs,
|
||
axes,
|
||
})}\n`)
|
||
|
||
if (problems.length > 0) {
|
||
process.stderr.write(`${file}\n${problems.map((problem) => ` ${problem}`).join('\n')}\n`)
|
||
process.exit(1)
|
||
}
|