Skip to content

build(fonts): the two faces are served from this origin - #304

Merged
JanWelker merged 1 commit into
mainfrom
build/self-hosted-fonts
Sep 8, 2026
Merged

build(fonts): the two faces are served from this origin#304
JanWelker merged 1 commit into
mainfrom
build/self-hosted-fonts

Conversation

@JanWelker

Copy link
Copy Markdown
Owner

Anton and Archivo came from fonts.googleapis.com — the only third party the app talked to. That is a privacy leak before it is a performance one: a stylesheet fetched from another host tells that host who is baking, and it put a server we do not run in front of the first paint of an app that is otherwise entirely local.

What changed

  • src/app.html — the preconnect / preload-promotes-to-stylesheet / <noscript> block is gone. The shell links no third party at all now.
  • src/app.css — four @font-face rules of our own: Anton and Archivo, Latin and Latin Extended, font-display: swap, the variable face declared across both axes (wght 100 900, font-stretch 62% 125%) so label-caps' 84 % width still resolves. They are ours rather than the packages' own CSS because Fontsource names the variable family Archivo Variable and the design calls it Archivo — this way --font-sans and the family assertions in headings.spec.ts are untouched. The Vietnamese subset the packages also carry is left out; the five locales do not need it.
  • package.json@fontsource/anton and @fontsource-variable/archivo as dependencies, so the version stays one literal Renovate can see. Vite hashes the .woff2 files into _app/immutable/assets/ with relative url(), so BASE_PATH needs no help. Checked in both vite dev and the static build.
  • THIRD-PARTY-NOTICES.md — OFL-1.1 attribution for both faces, since the build now redistributes the font files.

The contract

The app fetches nothing from a host it is not served from: no backend, no analytics, no CDN, no font server. The one outbound request left in the whole app is the TRMNL webhook, on an explicit click, to a URL the user typed themselves.

e2e/self-hosted.spec.ts enforces it by watching the actual network across all three views and the print sheet. It lives in the browser suite because grepping the source cannot settle this — a CDN can come back through app.html, an @import in app.css, a component's <img>, or a dependency that pulls its own stylesheet; only what the browser fetches tells. A third test asserts both faces really load, so a page that quietly fell back to the system stack everywhere cannot pass as compliance. Verified it bites: putting a Google Fonts <link> back fails it with the three URLs listed.

Recorded in CLAUDE.md under Stack & deploy, in the Type section, and in the e2e "what belongs here" list; README.md gained a matching paragraph.

Checks

  • 953 unit tests, 100 % statements/branches/functions/lines
  • 126 browser tests (123 + the 3 new); test-baseline.json raised to match
  • npm run lint clean; version bumped to 7.0.3 (patch — no user-visible behaviour, no URL schema change)

🤖 Generated with Claude Code

Anton and Archivo came from fonts.googleapis.com, which was the only third
party the app talked to. It is a privacy leak before it is a performance one:
a stylesheet fetched from another host tells that host who is baking, and it
put a server we do not run in front of the first paint of an app that is
otherwise entirely local.

Both faces now ship with the site. The Fontsource packages are dependencies,
so the version stays a literal Renovate can see, and app.css declares the
@font-face rules itself rather than importing theirs — the package names the
variable family "Archivo Variable" and the design calls it Archivo. Latin and
Latin Extended only; the five locales need no more. Vite hashes the .woff2
files and emits relative urls, so BASE_PATH needs no help.

Written down as a contract: the app fetches nothing from a host it is not
served from, and the one outbound request left is the TRMNL webhook, on an
explicit click, to a URL the user typed. e2e/self-hosted.spec.ts enforces it
by watching the actual network across all three views and the print sheet —
grepping the source cannot settle this, since a CDN can come back through
app.html, an @import, a component's <img> or a dependency's own stylesheet.
It also asserts both faces really load, so falling back to the system stack
everywhere cannot pass as compliance.

Font notices in THIRD-PARTY-NOTICES.md: the build now redistributes the files.
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-09-08 17:50 UTC

@JanWelker
JanWelker merged commit f0a446a into main Sep 8, 2026
6 of 7 checks passed
@JanWelker
JanWelker deleted the build/self-hosted-fonts branch September 8, 2026 17:49
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.

1 participant