Fonts
SkillDev toolsAdd, swap, or remove a self-hosted web font (latin variable woff2 from fontsource, localized via next/font/local). Use when adding a font role, or debugging font loading, preload, or CLS.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Fonts skill
What this skill tells your AI
The instructions your AI receives, as published by nrjdalal/zerostarter in .agents/skills/fonts/SKILL.md and read by ahel’s review.
Every font is self-hosted through next/font/local. That loader is what generates the metric-adjusted "<family> Fallback" faces that keep CLS near zero, so it is not optional. Do NOT switch to fontsource CSS imports: the bundler resolves their url() references to node_modules paths that 404 in dev, and they emit no compiler-only fallback metrics.
Layout
web/next/src/fonts/*.woff2, the vendored latin variable filesweb/next/src/lib/fonts.ts, onelocalFontper family, each exporting a CSS variableweb/next/src/app/layout.tsx, where the variables are applied on<html>web/next/src/app/globals.css@theme inline, where roles chain to the font variables (--font-sans: var(--font-dm-sans), sans-serif)
Add or swap a font
-
Fetch the latin variable file from the fontsource CDN directly, no dependency needed:
curl -L -o web/next/src/fonts/<name>-latin-wght-normal.woff2 \ https://cdn.jsdelivr.net/npm/@fontsource-variable/<name>/files/<name>-latin-wght-normal.woff2 -
Declare it in
web/next/src/lib/fonts.ts:localFont({ src: "../fonts/<file>", variable: "--font-<name>", weight: "<min> <max>" }). The weight range is MANDATORY for a variable font: omit it and the face pins to 400, so every bold glyph becomes faux-bold synthesis. Read the range from the fontsource CSS,curl -s https://cdn.jsdelivr.net/npm/@fontsource-variable/<name>/index.css | grep font-weight. A serif also setsadjustFontFallback: "Times New Roman"(the metrics default to Arial). -
Apply the export's
.variableon<html>inlayout.tsx. -
Wire the role in
globals.css@theme inline:--font-<role>: var(--font-<name>), <generic>. -
Verify loading: dev CSS emits hashed
/_next/static/media/*.woff2urls plus the generated"<family> Fallback"faces, and a production build (Vercel preview, protection-bypass header) emits one<link rel="preload" as="font">per file, with no font 404s. -
If a public page changed, check CLS and LCP for layout shift before shipping.
Notes
- The data table measures header labels from bundled metrics of the sans woff2, generated by
packages/scripts/src/data-table-metrics.tsinto.generated/data-table-metrics.json(in the web build, dev, and check-types chains). Swapping the sans re-measures automatically on the next run, but the script'sFONTpath must point at the new file: update it when the filename changes. - Preload is scoped by import. Any font in
web/next/src/lib/fonts.tspreloads on every page, because the root layout imports it. To scope a font to specific routes, declare it in a module only those routes import: author-only fonts live inweb/next/src/lib/marketing/fonts.ts, with woff2 underweb/next/src/fonts/marketing/. - DM Sans vendors its italics:
srcis an array of{ path, style }entries under one top-levelweight. Mono renders synthetic oblique for italics, by choice. - A non-variable font takes the per-weight files, with
srcas an array carrying aweightper entry. - To remove a font, delete the woff2, its
localFontdefinition, the<html>variable, and theglobals.cssrole, then grep the variable name to catch stragglers.
Signals
- GitHub stars
- 63
- Forks
- 11
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
fonts- Source
- github.com/nrjdalal/zerostarter