Skip to content

feat(fallbacks): face-aware lookups for Regular-only substitutes#43

Merged
caio-pizzol merged 1 commit into
mainfrom
caio-pizzol/face-aware-fallback
Jun 6, 2026
Merged

feat(fallbacks): face-aware lookups for Regular-only substitutes#43
caio-pizzol merged 1 commit into
mainfrom
caio-pizzol/face-aware-fallback

Conversation

@caio-pizzol
Copy link
Copy Markdown
Contributor

The package API is family-oriented - getRenderableFallback("Baskerville Old Face") answers "which family", not "which face." But Baskerville -> Bacasime is Regular-only (and it's already in published 0.2.0), so a consumer wiring a family-level map could route bold/italic Baskerville to a substitute that has no such face. Cooper Black -> Caprasimo (measured, regular-only) would add a second such row. This lands the face-scope safety before any of that.

  • getRenderableFallbackForFace(family, face, opts) returns the substitute only for a face it actually provides - null for bold/italic of a Regular-only row, instead of mis-routing.
  • getFallbackDecisionForFace adds a face_missing decision kind (the substitute exists, but not this face) so a UI can route it through absence handling. A covered face carries its OWN verdict, not the family worst-face rollup - Cambria regular reads metric_safe even though the family rolls up to visual_only.
  • Every FontFallback now carries faces (the substitute's RIBBI coverage), so the family-level createFallbackMap is self-describing and a consumer can route per-face without a second lookup.

No data change - this is package-level safety only. The Cooper Black row stays internal (it needs the reviewed-record -> export pipeline, not a hand-edit) until that path is restored.

Verified: typecheck, lint clean; bun test 20 pass (5 new face-aware tests incl. Baskerville Regular-only routing and Cambria per-face verdict); built dist loads in plain Node and exports the new helpers. Built from a clean worktree off origin/main; the local main tree's WIP is untouched.

@caio-pizzol caio-pizzol force-pushed the caio-pizzol/face-aware-fallback branch from de77a2f to 86d1da6 Compare June 6, 2026 00:15
A Regular-only row (Baskerville -> Bacasime, already published; Cooper Black ->
Caprasimo, coming) could be blindly routed for bold/italic by the family-level
API. Add face safety:

- getRenderableFallbackForFace(family, face, opts) returns the substitute only
  for a covered face, null otherwise - bold/italic of a Regular-only row no
  longer route to a face the substitute lacks.
- getFallbackDecisionForFace adds a face_missing decision kind (substitute
  exists, this face does not). A covered face carries its OWN verdict (Cambria
  regular is metric_safe though the family rolls up to visual_only).
- Every FontFallback now carries faces, so the family-level createFallbackMap is
  self-describing; its doc notes a face-scoped entry is only safe in a
  face-aware resolver.

Face-scope rule: an all-false faces means the row is NOT face-scoped (a category
fallback whose physical font does have faces), so it renders for every face and
never becomes face_missing. Only a measured per-face substitute (Baskerville) is
gated. No data change.
@caio-pizzol caio-pizzol force-pushed the caio-pizzol/face-aware-fallback branch from 86d1da6 to 837538f Compare June 6, 2026 00:20
@caio-pizzol caio-pizzol merged commit 985a430 into main Jun 6, 2026
1 check passed
@caio-pizzol caio-pizzol deleted the caio-pizzol/face-aware-fallback branch June 6, 2026 00:22
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.

2 participants