Repository navigation
Delivered HTML fetches Google Fonts, so an offline or air-gapped viewer silently loses the typography #242
Description
Activity
Taking this. I'll stop delivered HTML from fetching Google Fonts so an offline viewer keeps the typography.
- added 2 commits that reference this issue
on Aug 31, 2026 - added a commit that references this issue
on Sep 1, 2026 - added a commit that references this issue
on Sep 2, 2026 Verification note (macOS arm64, Node v24.19.0), comparing both open fixes at their heads:
- fix(delivery): stop delivered HTML from requesting Google Fonts #249 (
f28069d): removes the remote request and shipslocal()-only faces. Delivered artifacts are clean of Google Fonts (verified viadeliver+ grep), butnpm testfails 2 golden/reproducibility tests because the checked-in artifacts embedding the template (docs/gallery.html,examples/checkout-platform-delta.html) weren't regenerated; and withlocal()-only faces, geometry depends on each reader's locally installed font. - fix(viewer): embed the viewer font so delivered pages stay self-contained #256 (
4df3e7e): embeds the JetBrains Mono variable woff2 subsets (+~92 KB per artifact) with pinned digests + OFL license file;npm testis green at its head (0 failures), delivered artifacts contain zero remote references; currently needs a rebase.
Details and repro commands are posted on both PRs.
- fix(delivery): stop delivered HTML from requesting Google Fonts #249 (
- added a commit that references this issue
on Sep 3, 2026 - added a commit that references this issue
on Sep 4, 2026 Still present on
mainat2.17.0-dev.1:assets/template.htmlcarries thepreconnecttofonts.gstatic.com, themedia="print"stylesheet link, and the<noscript>duplicate, so every delivered artifact still makes the outbound request.Two things to add that I did not see in the thread yet.
A third consequence: EU data protection
The offline-degradation and outbound-request framings above are both right, but in the EU there is a concrete legal edge to the same defect. Embedding the Google Fonts CDN transmits the viewer's IP address to a third party in the US without consent, which German courts have treated as an actionable privacy violation (LG München I, 3 O 17493/20). For a diagram that is only ever opened by its author this is theoretical. For an architecture diagram handed to a client, attached to a report, or published on a site, whoever ships the file carries that exposure, and the README's "self-contained" framing is exactly what stops people from checking.
This does not change the technical fix, but it does argue for the default being local rather than an opt-in flag:
--embed-fonts(suggestion 1 in the issue body) leaves the unsafe behaviour as the default that most artifacts will ship with.Evidence for the
local()approach over font embeddingThe open question between #249 (
local()-only faces) and #256 (font embedded as a data URI) is whether the fallback stack changes text metrics enough to break layouts that were validated against JetBrains Mono. I measured that on a machine where JetBrains Mono is not installed, so the fallback path is what actually rendered.Applied the
local()-only change toassets/template.htmlon2.17.0-dev.1and rendered the packagedweb-app.architecture.jsonandagent-run.lifecycle.json:- delivered HTML greps clean, only the
w3.orgnamespace strings remain archify checkreports"issues": []archify visual-checkreportsstatus: passacross all four combinations it captures (1440x900 and 2048x1320, dark and light), withoverflowX/overflowY: falseandreadabilityOk: trueat every one
So the system monospace stack does not cost containment or readability on the packaged examples. That is a narrow result (two diagrams, macOS, one fallback font) and it does not say anything about a dense
standardmap or a Linux fallback, but it does suggest the lighter fix is sufficient and that embedding a font is not needed to protect the layouts.On the golden-test failures reported for #249 above: those look like they follow from
docs/gallery.htmland the checked-inexamples/*.htmlembedding a copy of the template, so re-rendering the committed artifacts in the same commit should settle them rather than the template change itself being wrong.Happy to run the same measurement against whichever branch you prefer.
- delivered HTML greps clean, only the
- added a commit that references this issue
on Sep 6, 2026 - added a commit that references this issue
on Sep 8, 2026
A delivered page links two external hosts. Measured on 2.16.0, on a diagram with no
brandfields atall, by grepping the output file:
So every viewer's browser makes a request to a third party in order to render the page as designed.
Two consequences, and the second one is the one I care about
Offline it degrades silently. On a machine with no route to those hosts the page still opens, the
diagram still renders, and the typography falls back — so the artifact looks fine to whoever produced
it and different to whoever receives it. Nothing reports it. This is the normal case for an
air-gapped or restricted network, which for infrastructure diagrams is not an edge case: those are
exactly the environments people draw diagrams of.
It is an outbound request per viewer. A self-contained HTML file that reaches a third party on
open is a property worth being deliberate about, and the README's framing of the output as
self-contained is what made me check.
The SVG export is already clean, which is what makes this fixable rather than inherent
Same diagram, exported to SVG from the viewer:
and the font is declared as
local()with a fallback, so it degrades to a system font by designrather than by accident. The right behaviour already exists in one output path.
Suggestion
Any of these would close it, in rough order of how much work they look like from outside:
--embed-fonts— that inlines the faces asdata:URIs in the delivered HTML;local()first with an explicit fallback stack, andthe remote stylesheet as an optional enhancement rather than the only source;
typography — that at least makes it a known property instead of a surprise.
Happy to test a branch; the check is one grep over the delivered file.