Brand just dropped a .ttf (or .otf) in Slack. Design opened it on macOS and it looks perfect. Marketing wants it on the landing page by Friday. The web does not want a raw TrueType file sitting in /public/fonts.
I used to either ship the TTF "for now", bounce it through Font Squirrel, or ask design to export WOFF2 from a desktop app I don't have installed. That path works for a brand refresh. It's painful when the file already exists and you just need a web-safe package before a deploy.
What the web actually wants
Browsers can load TTF. That doesn't mean you should ship it as the primary format.
A boring but complete handoff usually includes:
-
WOFF2 — the format you put in
@font-facefirst (smaller, modern browsers) - Optional WOFF — legacy fallback if you still care about very old clients
- Optional subset — Latin / brand glyphs only, not the full multilingual dump from the foundry
- A license note — so the next person doesn't assume every desktop font is free to host
You don't need five formats. You need the format your CSS actually references — and it should come from the same master file so metrics stay consistent.
What goes wrong with the usual shortcuts
Ship the TTF raw in /public/fonts — works in Chrome until someone audits payload size, or a license check asks how you redistributed the desktop file. Also often larger than WOFF2 for the same glyphs.
Font Squirrel / "webfont generator" upload sites — easy when the font is free. Awkward when the mark is a licensed brand face and you just put it in a stranger's converter bucket.
Desktop converters / Glyphs / FontLab export — correct for a foundry pipeline; overkill when you're on a locked laptop and the only ask is "make this loadable on the marketing site."
Ask design for "the web font package" — right for a rebrand; slow when they already handed you the TTF and assumed frontend would package it.
Copy-paste @font-face from an old project that still lists truetype first — you'll keep serving the heavy file forever because nobody revisits font CSS until Lighthouse yells.
What I actually need from the convert step
For frontend / DX use, the checklist is boring:
- WOFF2 out — not just "a smaller TTF"
- Same family metrics — no mystery tracking changes after convert
- No upload — brand font stays in the tab (Offline litmus still applies)
-
Predictable name —
BrandSans-Regular.woff2, notdownload (3).woff2 - Optional subset note — know whether you converted the full face or trimmed glyphs
If the tool can't do those five, I'm back to fonttools / woff2_compress in a one-off script.
A tab that fits the handoff
When the brand font is still a desktop .ttf / .otf and I just need WOFF2 before a deploy, I've used Uwarp's TTF → WOFF2 tool — drop the file, grab WOFF2 in the browser. Worth trying when the alternative is another desktop install or an upload converter you can't audit.
For a recurring pipeline (every brand refresh, every white-label tenant), script it: fonttools ttLib / pyftsubset, Google's woff2 CLI, or a tiny CI job that emits the files your @font-face lists. The tab is for the one-offs that shouldn't block a PR.
Wire the files you actually ship
A few rules that stopped me from collecting orphan fonts in /public:
- Commit WOFF2 (and maybe WOFF), not the raw TTF — keep the master desktop file in the design / brand repo if needed, not in the public web root
-
@font-facewithfont-displayand a real fallback stack — don't leavefont-family: BrandSanswithout a system fallback -
Name by family + weight (
BrandSans-700.woff2), neverfinal_font_v2.ttf - Document license + source in the README so the next person doesn't "just host whatever Slack sent"
Font packaging is packaging, not design. Treat it like that and the folder stays boring.
Tiny habit
Next time a repo needs a web font and you already have a clean TTF from brand, don't reopen Font Squirrel and don't upload the licensed face. Convert in a local-ish tab (or a one-liner CLI), drop the WOFF2 next to your CSS, verify @font-face loads, ship.
Fewer blocked deploys. Fewer mystery .ttf files in /public/fonts. Same brand face — just the format the browser and the network tab actually want.
Top comments (0)