Repository navigation
feat(webui): ship the wordmark's own font with the client - #29
Merged
Merged
Conversation
added 3 commits
November 11, 2026 10:28
The mark used to name "Century Gothic", a Microsoft font, so a Mac without Office fell through to Arial Rounded MT Bold, a Linux box fell through to whatever its fontconfig had, and a Windows box got Century Gothic. The same mark came out in a different face on every platform, which is what issue #27 reported: one platform's recorded baseline stopped matching the other two without a line of WebUI source changing. The mark now uses Nunito, carried by the client. It is a rounded sans under the SIL Open Font License 1.1, so it is free to redistribute, and it keeps the round terminals the mark had on macOS — the face it replaces could not be carried, because Arial Rounded MT Bold and Century Gothic are Monotype fonts this repository may not ship. It travels as one variable file (weight axis 200-1000) that covers both weights the mark uses: 700 for the wordmark, 600 for its lower line. The family is named "MiniMax Wordmark" rather than "Nunito" on purpose — a name no host happens to have installed is a name only this file can answer, so a machine that already has Nunito cannot answer first with a different version of it. The faces that came before stay behind it in the stack, so a build where the font file did not make it into the artifact still draws the mark.
The mark now draws with the font the client carries, so every platform renders the same shapes and the recorded baselines had to be taken again. They are taken from what CI itself produced for run 42946296900: a baseline recorded on any other machine is that machine's rasteriser's idea of the same picture, and the runner that compares it would fail it. What the new baselines show is the point of the change. The mark's own box diverged between platforms by 19% to 24% of its pixels before, because each machine fell through to a different installed font. It now diverges by 5% to 8%, and that remainder is anti-aliasing rather than different letterforms. Four shots per platform are affected: the workspace panel at 1440, 1024 and 768, and the embedded browser panel, which is where the mark is drawn.
The mark's font is a web font now, and it is declared with font-display: block so the mark is never painted in a fallback face first. That has a consequence for the pixel gate: until the face is ready the mark is not painted at all, so a screenshot taken inside that window records a blank mark. The harness already waited for the two fonts the browser is pinned to. It now waits for the mark's face too, and the wait is an assertion rather than a hope: all three faces have to come back loaded, so a build where the font file did not make it into the artifact fails the suite instead of quietly recording a blank mark. Waiting for a face that is already there changes nothing on screen: the same four pixel shots were taken with and without this wait and came out identical to the pixel.
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.
What
Ship the wordmark's own font with the client, instead of naming a family that only exists on some machines.
The mark used to render in
"Century Gothic", "Arial Rounded MT Bold", "Segoe UI", sans-serif. "Century Gothic" is a Microsoft font, so a Mac without Office fell through to Arial Rounded MT Bold, a Linux box fell through to whatever its fontconfig had, and a Windows box got Century Gothic. The same mark came out in a different face on every platform. That is what issue #27 reported: the recorded baseline for one platform stopped matching the other two, with no WebUI source involved.The mark now uses Nunito, carried by the client.
Why this face
What changed
packages/webui/src/client/assets/fonts/brand/Nunito-latin.woff2, withOFL.txtbeside it.packages/webui/src/client/styles/brand-font.css— two@font-facerules (600 and 700) over the same variable file. The family is named "MiniMax Wordmark" rather than "Nunito" on purpose: a name no host happens to have installed is a name only this file can answer, so a machine that already has Nunito cannot answer first with a different version of it.font-display: blockkeeps the mark from being painted in a fallback face first — the mark is the one thing on that page that must not flicker between two shapes.packages/webui/src/client/styles/index.css— one import, aftertokens.css.packages/webui/src/client/features/workspace/ui/workspace.css— the wordmark's stack now reads"MiniMax Wordmark", "Century Gothic", "Arial Rounded MT Bold", "Segoe UI", sans-serif. The faces that came before stay behind it: if the font file does not make it into an artifact, the mark is still drawn, just not in its own face.packages/webui/test/unit/webui-w0-css-structure.test.ts— two guards. The@font-faceURLs must live under/assets/fonts/brand/and the files must exist (nothing else in the gate checks that a URL in a stylesheet resolves, and a missing font is a blank mark rather than a failure). The wordmark's stack must name the bundled face first and keep the old faces behind it.packages/webui/test/gates/fixtures/path-metadata-f00.json— the new stylesheet's path entry, classified by the frozen baseline classifier (scripts/lib/webui-dependency-rules.mjsatf00b1268c28ad15054a8882620c3fb08beb86d58, its sha256 verified against the fixture'ssourceSha256) and recorded indeclaredDelta.scripts/build-webui.mjs,scripts/package-webui-npm.mjs— the brand font directory travels with the built client and with the npm package. These two files sit outsidepackages/webui; this copy rule is the one thing about the font that has to live at the repository root.release/public-source.json— regenerated.packages/webui/test/browser/harness.mjs— the pixel harness now waits for the bundled face before it measures the client, and asserts that the face came back loaded.font-display: blockmeans the mark is not painted at all until the face is ready, so a screenshot taken inside that window records a blank mark; without the wait the gate could record a blank mark and then hold it up as the truth. The wait is an assertion, not a hope: a build where the font file did not make it into the artifact fails the suite instead.packages/webui/test/browser/responsive-baselines.spec.mjs-snapshots/andpackages/webui/test/browser/visual-baselines.spec.mjs-snapshots/— the twelve recorded baselines re-recorded on the CI runners (see below).Baseline impact
Four recorded groups change, and only inside the wordmark:
workspace-panel-at-1440,workspace-panel-at-768,workspace-panel-at-1024andembedded-browser-panel. The mark is drawn only on the launcher tab, so every other recorded view is unaffected.Measured on one machine by rendering the tree before and after this change and comparing the frames byte for byte: of the 22 rendered baseline groups, 18 are byte-identical, and the four above differ only inside the wordmark's own row — 7,039 / 3,667 / 1,823 / 7,039 pixels, at most 1.31% of a frame.
The recorded baselines therefore have to be re-recorded on each platform (4 groups × 3 platforms = 12 images). That has to happen on the CI runners: a frame recorded on one machine does not match another, because the three platforms rasterize the same font file their own way, and those differences are orders of magnitude above the 8-pixel budget this gate allows.
Re-recorded. The twelve images are the ones CI itself produced for run 42946296900, taken from that run's artifacts and written back per platform. A frame recorded on any other machine is that machine's rasterizer's idea of the same picture, and the runner that compares it would fail it.
What the re-recorded frames show:
Checks
native-runtime.spec.mjs— the spec's own teardown hook missing a 30-second deadline (the native host did not finish closing) while the test itself passed. That spec does not call the helper this change touches, and it passed when the same commit was re-run.Not included
No expected value changed and no budget was relaxed. The pixel budget stays at 8, the recorded baselines are re-recorded rather than re-tuned, and the fonts the mark used before are kept behind the bundled face.