Skip to content

dev: Swap out paid fonts for free alternatives#3573

Open
isTravis wants to merge 11 commits intomainfrom
tr/fonts
Open

dev: Swap out paid fonts for free alternatives#3573
isTravis wants to merge 11 commits intomainfrom
tr/fonts

Conversation

@isTravis
Copy link
Copy Markdown
Member

@isTravis isTravis commented Apr 9, 2026

Replaces all Adobe Typekit / paid font dependencies with free alternatives, self-hosted on our S3 bucket. Eliminates the Typekit subscription and third-party dependency.

Font Mapping

Original (Adobe/Typekit) Replacement Usage
multi-display Source Sans 3 Header font ($header-font), pub titles, pub preview titles
skolar-latin Source Serif 4 Body font ($body-font), pub body paragraphs
upgrade / upgrade-lights / upgrade-light Outfit Landing page
Source Han Serif/Sans (CJK via Typekit kit) Noto Serif/Sans TC/KR/JP/SC CJK fallbacks

All fonts are variable-weight woff2 files from Fontsource.

Font Hosting

Fonts are self-hosted on S3 at assets.pubpub.org/fonts/<hash>/. No font files are stored in this repo, and no fontsource packages are installed.

A single fonts.css (~940 @font-face rules, ~250KB gzipped) contains declarations for all font families. Each rule uses unicode-range subsetting so browsers only download the woff2 slices for characters actually on the page.

The URL includes a content hash so we never need to worry about cache invalidation — changing fonts produces a new URL. Old versions stay on S3 harmlessly.

How fonts are loaded

  • Web: <link rel="preconnect"> + <link rel="stylesheet"> in server/Html.tsx, placed before the main CSS bundle so the browser can fetch fonts in parallel
  • PDF exports: <link rel="stylesheet"> in workers/tasks/export/html.tsx — Chromium fetches from S3 directly
  • All rules include font-display: swap (from fontsource) so text renders immediately with fallbacks

Upload Script

scripts/upload-fonts-to-s3.sh handles everything:

  1. Downloads fontsource packages from npm via curl (not installed in the project)
  2. Copies all woff2 files to a staging directory
  3. Generates fonts.css with corrected font-family names and relative url() paths
  4. Computes a content hash (sha256, first 8 chars) from all staged files
  5. Uploads to s3://assets.pubpub.org/fonts/<hash>/
  6. Updates the hashed URL in server/Html.tsx and workers/tasks/export/html.tsx via sed
scripts/upload-fonts-to-s3.sh --dry-run   # preview, no upload
AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... scripts/upload-fonts-to-s3.sh  # real upload

Changes

Font loading:

  • server/Html.tsx — Added preconnect + <link> to S3-hosted fonts.css
  • workers/tasks/export/html.tsx — Typekit JS loader → <link> to S3-hosted fonts.css
  • client/styles/base.scss — Removed Typekit @import url()
  • client/styles/_fonts.scss — Removed (was intermediate, now unnecessary)
  • workers/tasks/export/styles/printDocument.scss — Removed Typekit @import url()

Font variables (client/styles/variables.scss):

  • $header-font: multi-displaySource Sans 3
  • $body-font: removed skolar-latin / $export-body-font / $screen-body-font split → unified Source Serif 4
  • $cjk-body-font: source-han-serif-*Noto Serif TC/KR/JP/SC
  • $cjk-header-font: source-han-sans-*Noto Sans TC/KR/JP/SC

Pub header tuning (client/containers/Pub/PubHeader/pubHeader.scss):

  • h1: weight 450, line-height 1.52em, letter-spacing 0.4px
  • Description: weight 300, letter-spacing 0.3px
  • Byline: weight 400, 19px, letter-spacing 0.1px

Pub body tuning (client/containers/Pub/PubDocument/pubBody.scss):

  • h1: 700/1.6em/1.1em, h2: 600/1.4em/1.1em, h3: 400/1.25em/0.8em
  • h4: 600, h5: 500, h6: 300 — all letter-spacing 0px
  • Paragraph: line-height 1.75, letter-spacing -0.023em

Landing page tuning (client/containers/Landing/landing.scss):

  • upgrade / upgrade-lights / upgrade-lightOutfit throughout
  • Global: weight 300→400, letter-spacing 1px→0.2px
  • Hero h1: weight 500, size 3.9em
  • Subtitle: weight 200, various weight adjustments throughout

Pub preview (client/components/PubPreview/pubPreview.scss):

  • multi-displaySource Sans 3
  • Large title: weight 600, letter-spacing -1.1px

Other:

  • workers/tasks/export/styles/buildCss.mts — Fixed typo in comment
  • README.md — Added Fonts section documenting hosting, loading, cache busting, and update process
  • scripts/upload-fonts-to-s3.sh — New script for font management

Tooling

Added tools/font-tuner.html — standalone side-by-side font comparison tool for visually tuning replacement fonts against the Typekit originals. Features overlay mode, all font candidates, weight/line-height/letter-spacing/font-size sliders, copy-to-clipboard SCSS output, and localStorage persistence.

Testing

  • Visual check: pub headers (all 4 themes)
  • Visual check: pub body headings (h1–h6) and paragraph text
  • Visual check: landing page hero, features, community grid
  • Visual check: pub preview cards (large, medium, small, minimal)
  • PDF/DOCX export renders correctly with new fonts
  • CJK content renders in exports
  • No remaining Typekit references (grep -r "typekit\|kmi0tdo\|seb8nix" client/ server/ workers/)

@isTravis isTravis changed the title dev: Swap out paid fonts for free altneratives dev: Swap out paid fonts for free alternatives Apr 9, 2026
@tefkah
Copy link
Copy Markdown
Member

tefkah commented Apr 11, 2026

very cool! could we host the fonts ourselves instead of pulling them from google? i don't think it'll make a big diff wrt latency as itll be cached in fastly, and one less tech giant to be dependent on for the app

@isTravis
Copy link
Copy Markdown
Member Author

Yes - absolutely. Great suggestion - I'll add that next commit.

@isTravis isTravis requested a review from tefkah April 14, 2026 03:08
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.

2 participants