Skip to content

Delivered HTML fetches Google Fonts, so an offline or air-gapped viewer silently loses the typography #242

Description

@sertels

A delivered page links two external hosts. Measured on 2.16.0, on a diagram with no brand fields at
all, by grepping the output file:

2  https://fonts.googleapis.com
1  https://fonts.gstatic.com
43 http://www.w3.org        <- the SVG namespace, not a request

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:

3  http://www.w3.org        <- namespace only
0  external hosts

and the font is declared as local() with a fallback, so it degrades to a system font by design
rather 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:

  1. a flag — --embed-fonts — that inlines the faces as data: URIs in the delivered HTML;
  2. the SVG path's approach applied to the HTML: local() first with an explicit fallback stack, and
    the remote stylesheet as an optional enhancement rather than the only source;
  3. failing both, a line in the README saying the delivered page requires network access for
    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.

Activity

  1. shahidbeig-a11y commented on Aug 31, 2026

    @shahidbeig-a11y

    Taking this. I'll stop delivered HTML from fetching Google Fonts so an offline viewer keeps the typography.

  2. added 2 commits that reference this issue on Aug 31, 2026
    bc5323d
    e6e32fc
  3. added a commit that references this issue on Sep 2, 2026
    894da49
  4. JINITAIMEI121 commented on Sep 3, 2026

    @JINITAIMEI121

    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 ships local()-only faces. Delivered artifacts are clean of Google Fonts (verified via deliver + grep), but npm test fails 2 golden/reproducibility tests because the checked-in artifacts embedding the template (docs/gallery.html, examples/checkout-platform-delta.html) weren't regenerated; and with local()-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 test is 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.

  5. added a commit that references this issue on Sep 3, 2026
    42fe7ac
  6. added a commit that references this issue on Sep 4, 2026
    ebcce0f
  7. andreas-straub commented on Sep 5, 2026

    @andreas-straub

    Still present on main at 2.17.0-dev.1: assets/template.html carries the preconnect to fonts.gstatic.com, the media="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 embedding

    The 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 to assets/template.html on 2.17.0-dev.1 and rendered the packaged web-app.architecture.json and agent-run.lifecycle.json:

    • delivered HTML greps clean, only the w3.org namespace strings remain
    • archify check reports "issues": []
    • archify visual-check reports status: pass across all four combinations it captures (1440x900 and 2048x1320, dark and light), with overflowX/overflowY: false and readabilityOk: true at 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 standard map 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.html and the checked-in examples/*.html embedding 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.

  8. added a commit that references this issue on Sep 6, 2026
    7f9bb6b
  9. added a commit that references this issue on Sep 8, 2026
    1072200
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions