Repository navigation
[quality] src/css/custom.css <-> static/fonts/ contract is untested; 27 italic faces (764K) shipped but never declared #317
Description
Finding
Nothing in the test suite reads the url() references in src/css/custom.css,
and nothing enumerates static/fonts/. Both directions of that contract are
unguarded, and the gap has already let real drift accumulate.
Reverse direction — 27 orphaned font files, 764K, shipped but never declared.
src/css/custom.css declares 9 @font-face blocks referencing 27 files
(ClarityCity-{100..900} x woff2/woff/ttf). static/fonts/ contains
54 files. The 27 unreferenced ones are the complete italic set:
ClarityCity-100Italic.{ttf,woff,woff2}
ClarityCity-200Italic.{ttf,woff,woff2}
ClarityCity-300Italic.{ttf,woff,woff2}
ClarityCity-400Italic.{ttf,woff,woff2}
ClarityCity-500Italic.{ttf,woff,woff2}
ClarityCity-600Italic.{ttf,woff,woff2}
ClarityCity-700Italic.{ttf,woff,woff2}
ClarityCity-800Italic.{ttf,woff,woff2}
ClarityCity-900Italic.{ttf,woff,woff2}
custom.css contains zero font-style: italic declarations, so no
@font-face can ever match italic text. Two consequences, both silent:
- Docusaurus copies all of
static/intobuild/, so 764K of the 1.5M
static/fonts/tree is published as unreachable bytes on every deploy. - Every
<em>/*italic*on the site renders as a synthetic oblique — the
browser mechanically slanting the upright face — rather than the real
ClarityCity italic that is sitting right there in the repo. That is a
visible typographic regression against the designer's intent, and it is
invisible to every existing gate.
Forward direction — unverified. All 27 declared paths resolve today
(verified at 00b44df), but no test pins that. A typo in a font path produces
a clean build and a silent fallback to the system font stack.
Why existing gates miss it
docusaurus.config.jsonBrokenLinks: 'throw'enforces page routes, not
static assets referenced from CSS.tests/validate-button-contrast.test.mjsis the only test that reads
custom.css, and it only extracts--cncf-button-backgroundhex values.scripts/validate-architecture-assets.mjsandscripts/validate-awards.mjs
checkdata/architecturesanddata/awards.jsonrespectively; neither
touchessrc/css/orstatic/fonts/.- PR test: verify local /img static asset references resolve under static/ (tests/static-assets.test.mjs) #289 (
tests/static-assets.test.mjs, open) scopes itself explicitly to
/imgreferences fromdocs/,blog/,src/anddata/*.json. It does not
readcustom.cssand does not enumeratestatic/fonts/. This issue is
disjoint from that PR.
Recommendation
Two deliverables. The second is blocked on a maintainer decision, which is why
they are tracked together rather than as one test-only change.
- Decide the italic faces. Either (a) add 9
@font-faceblocks with
font-style: italicpointing at the existingClarityCity-*Italic.*
files — restoring the intended typography at no download cost for readers
who never hit italic text — or (b)git rmthe 27 files and drop 764K
from every build. (a) is the better default: the files were deliberately
committed alongside their upright counterparts, so their absence from the
CSS reads as an oversight rather than a decision. - Add
tests/css-assets.test.mjsasserting both directions, so
whichever choice is made above stays true:
- everyurl('/...')insrc/css/custom.cssresolves to a real file
understatic/, compared case-sensitively per path segment via
readdirSync(a case-only typo resolves on a developer's
case-insensitive filesystem and 404s on GitHub Pages);
- every file understatic/fonts/is referenced by at least one
@font-faceincustom.css;
- the extraction finds a non-zero number of references, so a regression
in the regex cannot make the other assertions silently vacuous.
Note that the second box cannot be written green until the first is resolved:
the orphan assertion fails against main today by exactly the 27 files above.
Why there is no PR attached
The forward-direction half would land green on its own, but on its own it is a
second implementation of the same "does a static path resolve" invariant that
open PR #289 already implements, and the hive's duplicate-work rule says to
stand down rather than ship a competing partial. The half that carries the
actual value — orphan detection — is red until the italic decision above is
made. Splitting the test across two PRs to make one of them mergeable would
weaken the finding to fit what is pushable, so the whole contract is filed here
instead and should land as one test file once the first box is ticked.
Evidence
- Revision:
00b44df,node v26.8.2, run locally 2026-09-19. - Counts:
ls static/fonts | wc -l-> 54; distinct/fonts/*refs in
custom.css-> 27;grep -c '@font-face' src/css/custom.css-> 9;
font-style: italicoccurrences -> 0. - Size:
du -chover the 27 undeclared files -> 764K;du -sh static/fonts-> 1.5M. - Unit coverage:
node --test --experimental-test-coverage-> 55 tests pass;
no test readscustom.cssurl()refs or enumeratesstatic/fonts/. - End-to-end coverage: the repository has no e2e or browser suite
(noplaywright/cypress/puppeteerinpackage.json), so e2e evidence is
unobtainable rather than absent. See [quality] CI publishes no coverage evidence, so coverage findings cannot be verified #186 for the missing coverage-evidence
pipeline. The synthetic-oblique rendering in particular is exactly the class
of defect only a browser suite would catch.
Priority
- Impact: medium — a visible typographic regression plus 764K of dead weight per deploy, both silent
- Effort: low — one CSS decision plus one test-only file, no new dependencies
Filed by quality agent (hold-gated mode)
— hive: agent=quality backend=copilot model=claude-opus-5
Activity
fix: declare italic ClarityCity faces and test the custom.css<->stati…