fix(ui): render the visual gate in the typefaces the tokens name (RIG-2154) - #1061
Open
rigel-mintaka wants to merge 1 commit into
Open
fix(ui): render the visual gate in the typefaces the tokens name (RIG-2154)#1061rigel-mintaka wants to merge 1 commit into
rigel-mintaka wants to merge 1 commit into
Conversation
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
rigel-mintaka
force-pushed
the
compass-ux/2154-pin-gate-fonts
branch
from
September 10, 2026 02:57
3913546 to
8dacc14
Compare
|
Compass engineering docs preview: https://compass-ux-2154-pin-gate-fon.compass-eng-docs.pages.dev Deployed from |
…-2154) The design tokens ask for two faces that were never installed anywhere. `--rigel-mono` is "Space Mono" and `--rigel-display` is "Departure Mono" (apps/ui/src/design/tokens.css), but neither the CI runner nor a dev box has either, so every screenshot ever captured fell through the CSS stack to whatever fontconfig happened to pick. The substitution was invisible because the substitute is a near-metric match. From the font metrics: IBM Plex Mono is 600/1000em, i.e. 6.600px at 11px and 7.600px/glyph with the 1px letter-spacing `.bridge-col-head` uses. The committed baselines measure exactly 7.600px/glyph, so they are IBM Plex Mono — the runner's substitute, not the brand face. Space Mono is 612/1000em = 7.732px/glyph at the same spec, a 0.132px difference no reviewer could see. Pinning the real faces is therefore a deliberate visual change: every text-bearing baseline moves once, and this is the first time the UI rasterizes in the typeface it was designed for. Two determinism holes close with it, both of which made the baselines a function of the machine rather than of the repo. The host's font set was an undeclared input: whichever faces a box happened to have decided the substitute, with nothing in the repo recording it. The <dir> list now names format subdirectories rather than each package's whole share/fonts tree, so the woff/woff2 duplicates and Unifont's bdf/otb/pcf bitmap strikes never enter the scan — a strike outsorts the scalable face at some pixel sizes, which would make glyph choice depend on font size. Rasterization was also unpinned, and the font set alone does not fix it. hintstyle, antialias and rgba fell through to fontconfig's compiled-in defaults, making them a property of the linked library rather than of this repo: under the built-in hintfull, Space Mono renders 0.26px/glyph wider than under hintslight, same browser and same font file (measured in the pinned Chromium). A devenv.lock bump that moved fontconfig would then shift every baseline with no declared cause. The config now pins them, with rgba=none because subpixel order changes per-pixel colour and a diffed screenshot wants grayscale AA. Coverage needs one fallback, and which one is a brand decision rather than a packaging detail. The UI draws 49 distinct non-ASCII glyphs; the branded faces cover 28 (Space Mono 14, Departure Mono 27, overlapping). Unifont and its upper-plane companion cover the remaining 21 exactly — verified all 49 resolve under the built config. Deliberately NOT a general-purpose system font. DejaVu was the first choice and was wrong twice over: DejaVu Sans is a proportional humanist sans, and once present fontconfig also pulled in DejaVu Math TeX Gyre — a variable-width TeX math face — for 14 glyphs including `✓ ○ ■ ▼ ⇒ ≡`. That is proportional metrics in a monospace grid and a stock sans in a brand whose shape language is hard 1-bit dot-matrix. Unifont is the better choice on brand grounds but is not free: it is dual-width (fontconfig spacing=90), so 17 of the fallback glyphs sit at 0.5em and 4 — including the sidebar gear — at 1.0em, against Space Mono's 0.612em cell. Those glyphs do not land on the grid. It costs ~14MB more than DejaVu, accepted for the brand reason. RIG-3603 retires the whole fallback by drawing those 21 as dot-matrix SVG, the pattern BadgeGlyph.tsx already uses for six status glyphs — on-brand and font-free. Both lanes that touch pixels export it, and both fail closed. The regen lane needs it as much as the gate: it captures the bytes the gate later replays against. The guards matter because the two malformed values fail in opposite directions and both stay green — an empty FONTCONFIG_FILE is read as a config with no font dirs at all (every string measures zero width), while a nonexistent path falls back to the full host set, which is the input this change exists to remove. Linux-only in the dev shell: the faces build everywhere, but chromium is Linux-only in nixpkgs so a darwin shell cannot run this gate, and FONTCONFIG_FILE is process-tree-wide — it would replace the font universe for everything launched from the shell in exchange for nothing. google-fonts is filtered to the one family — unfiltered it is 2.3GB. Baselines are NOT regenerated here. That needs a CI capture under the pinned env, which RIG-3550 blocks (Actions cannot open the regen lane's PR), so the bytes are hand-carried as in #1024. The gate task itself is wired into `ci` in the follow-on commit, so this PR's own green CI does not exercise the change. Co-authored-by: Matt Wilkinson <matt@rigel.build>
rigel-mintaka
force-pushed
the
compass-ux/2154-pin-gate-fonts
branch
from
September 10, 2026 03:49
8dacc14 to
e846226
Compare
rigel-mintaka
marked this pull request as ready for review
September 10, 2026 04:03
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The design tokens ask for two faces that were never installed anywhere.
--rigel-monois "Space Mono" and--rigel-displayis "Departure Mono"(apps/ui/src/design/tokens.css), but neither the CI runner nor a dev box has
either, so every screenshot ever captured fell through the CSS stack to
whatever fontconfig happened to pick.
The substitution was invisible because the substitute is a near-metric match.
From the font metrics: IBM Plex Mono is 600/1000em, i.e. 6.600px at 11px and
7.600px/glyph with the 1px letter-spacing
.bridge-col-headuses. Thecommitted baselines measure exactly 7.600px/glyph, so they are IBM Plex Mono
— the runner's substitute, not the brand face. Space Mono is 612/1000em =
7.732px/glyph at the same spec, a 0.132px difference no reviewer could see.
Pinning the real faces is therefore a deliberate visual change: every
text-bearing baseline moves once, and this is the first time the UI
rasterizes in the typeface it was designed for.
Two determinism holes close with it, both of which made the baselines a
function of the machine rather than of the repo.
The host's font set was an undeclared input: whichever faces a box happened
list now names format subdirectories rather than each package's whole share/fonts tree, so the woff/woff2 duplicates and Unifont's bdf/otb/pcf bitmap strikes never enter the scan — a strike outsorts the scalable face at some pixel sizes, which would make glyph choice depend on font size.to have decided the substitute, with nothing in the repo recording it. The
Rasterization was also unpinned, and the font set alone does not fix it.
hintstyle, antialias and rgba fell through to fontconfig's compiled-in
defaults, making them a property of the linked library rather than of this
repo: under the built-in hintfull, Space Mono renders 0.26px/glyph wider than
under hintslight, same browser and same font file (measured in the pinned
Chromium). A devenv.lock bump that moved fontconfig would then shift every
baseline with no declared cause. The config now pins them, with rgba=none
because subpixel order changes per-pixel colour and a diffed screenshot wants
grayscale AA.
Coverage needs one fallback, and which one is a brand decision rather than a
packaging detail. The UI draws 49 distinct non-ASCII glyphs; the branded
faces cover 28 (Space Mono 14, Departure Mono 27, overlapping). Unifont and
its upper-plane companion cover the remaining 21 exactly — verified all 49
resolve under the built config.
Deliberately NOT a general-purpose system font. DejaVu was the first choice
and was wrong twice over: DejaVu Sans is a proportional humanist sans, and
once present fontconfig also pulled in DejaVu Math TeX Gyre — a
variable-width TeX math face — for 14 glyphs including
✓ ○ ■ ▼ ⇒ ≡. Thatis proportional metrics in a monospace grid and a stock sans in a brand whose
shape language is hard 1-bit dot-matrix.
Unifont is the better choice on brand grounds but is not free: it is
dual-width (fontconfig spacing=90), so 17 of the fallback glyphs sit at 0.5em
and 4 — including the sidebar gear — at 1.0em, against Space Mono's 0.612em
cell. Those glyphs do not land on the grid. It costs ~14MB more than DejaVu,
accepted for the brand reason. RIG-3603 retires the whole fallback by drawing
those 21 as dot-matrix SVG, the pattern BadgeGlyph.tsx already uses for six
status glyphs — on-brand and font-free.
Both lanes that touch pixels export it, and both fail closed. The regen lane
needs it as much as the gate: it captures the bytes the gate later replays
against. The guards matter because the two malformed values fail in opposite
directions and both stay green — an empty FONTCONFIG_FILE is read as a config
with no font dirs at all (every string measures zero width), while a
nonexistent path falls back to the full host set, which is the input this
change exists to remove.
Linux-only in the dev shell: the faces build everywhere, but chromium is
Linux-only in nixpkgs so a darwin shell cannot run this gate, and
FONTCONFIG_FILE is process-tree-wide — it would replace the font universe for
everything launched from the shell in exchange for nothing.
google-fonts is filtered to the one family — unfiltered it is 2.3GB.
Baselines are NOT regenerated here. That needs a CI capture under the pinned
env, which RIG-3550 blocks (Actions cannot open the regen lane's PR), so the
bytes are hand-carried as in #1024. The gate task itself is wired into
ciin the follow-on commit, so this PR's own green CI does not exercise the
change.