Fonts
WAGE automatically handles font loading — preloading, @font-face declarations, and font-display: swap — from two inputs: font files in a folder and font family tokens. All font logic lives in the Wage\Assets class.
How it works
Section titled “How it works”The system is token-driven. Wage\Assets::get_fonts() reads the --heading-font-family and --text-font-family tokens, extracts the first font family name, slugifies it, and matches .woff2 files in the child theme’s assets/fonts/ directory.
The flow
Section titled “The flow”- Token
--heading-font-familyis set to"'DM Serif Display', Georgia, serif"in the child’sinc/tokens.php Wage\AssetsextractsDM Serif Displayfrom the token value (first name before the comma)Wage\Assetsslugifies it todm-serif-displayWage\Assetsscansassets/fonts/for files matching that slugWage\Assetsmatchesdm-serif-display-400.woff2(static) ordm-serif-display.woff2(variable)Wage\Assetsoutputs in<head>via hooks registered byWage\Assets::init():Wage\Assets::preload_fonts()—<link rel="preload">for each matched file (priority 1)Wage\Assets::output_font_faces()—<style id="wage-fonts">with@font-facedeclarations usingfont-display: swap(priority 2)
The font-family name in the @font-face rule comes from the token, not the filename — so it always matches what the CSS expects. No mismatches possible.
Static vs variable fonts
Section titled “Static vs variable fonts”The system supports both static and variable font files:
- Static fonts have a weight in the filename:
playfair-display-400.woff2. Each weight is a separate file. The@font-facegetsfont-weight: 400. - Variable fonts have no weight in the filename:
inter.woff2. One file contains all weights. The@font-facegetsfont-weight: 100 900.
Many modern fonts (Inter, Outfit, etc.) are variable. You only need one file instead of separate files per weight. The system detects this automatically from the filename.
| Type | Filename | @font-face result |
|---|---|---|
| Static | dm-serif-display-400.woff2 | font-weight: 400; font-style: normal |
| Static | dm-serif-display-700.woff2 | font-weight: 700; font-style: normal |
| Variable | inter.woff2 | font-weight: 100 900; font-style: normal |
| Static italic | dm-serif-display-400-italic.woff2 | font-weight: 400; font-style: italic |
| Variable italic | inter-italic.woff2 | font-weight: 100 900; font-style: italic |
Adding fonts to a project
Section titled “Adding fonts to a project”Step 1: Download the font files
Section titled “Step 1: Download the font files”Download .woff2 files. For Google Fonts, use curl with a Chrome user-agent to get woff2 format:
curl -s -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36" \ "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap"This returns CSS with URLs to the .woff2 files. Download the latin subset URL. If the font is variable (same URL for all weights), you only need one file.
Step 2: Name and place the files
Section titled “Step 2: Name and place the files”Place .woff2 files in assets/fonts/. The family slug must match the slugified version of the font family name — lowercase, spaces replaced with hyphens.
Static fonts (one file per weight):
{family-slug}-{weight}.woff2{family-slug}-{weight}-italic.woff2Variable fonts (one file for all weights):
{family-slug}.woff2{family-slug}-italic.woff2Examples:
| Font Family | Type | Files |
|---|---|---|
| Inter | Variable | inter.woff2 |
| Inter (with italic) | Variable | inter.woff2, inter-italic.woff2 |
| DM Serif Display | Static | dm-serif-display-400.woff2 |
| DM Serif Display (with italic) | Static | dm-serif-display-400.woff2, dm-serif-display-400-italic.woff2 |
| Playfair Display | Static | playfair-display-400.woff2, playfair-display-700.woff2 |
| Quicksand | Variable | quicksand.woff2 |
Step 3: Set the font tokens
Section titled “Step 3: Set the font tokens”In the child theme’s inc/tokens.php, set the font family tokens:
'--heading-font-family' => "'DM Serif Display', Georgia, serif",'--text-font-family' => "'Inter', system-ui, sans-serif",Always include fallback fonts after the custom font name.
That’s it. Wage\Assets handles preloading, @font-face, and font-display: swap automatically.
What gets output
Section titled “What gets output”For a project with inter.woff2 (variable) and dm-serif-display-400.woff2 (static), the <head> will contain:
<!-- Preload (priority 1) — Wage\Assets::preload_fonts() --><link rel="preload" href=".../dm-serif-display-400.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href=".../inter.woff2" as="font" type="font/woff2" crossorigin>
<!-- @font-face (priority 2) — Wage\Assets::output_font_faces() --><style id="wage-fonts">@font-face { font-family: 'DM Serif Display'; font-style: normal; font-weight: 400; font-display: swap; src: url('.../dm-serif-display-400.woff2') format('woff2');}@font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap; src: url('.../inter.woff2') format('woff2');}</style>What if a file doesn’t match?
Section titled “What if a file doesn’t match?”Files that don’t match any token slug are silently ignored. For example, if your token says 'Playfair Display' (slug: playfair-display) but the file is named playfair-400.woff2 (slug: playfair), it won’t match and no @font-face will be generated. The browser will fall back to the next font in the token’s fallback chain.
Core defaults
Section titled “Core defaults”Core defaults both --heading-font-family and --text-font-family to system sans-serif fonts. With no font files and no token overrides, the site renders in the OS system font — clean and fast. This is also what wireframe mode uses.
Wireframe mode
Section titled “Wireframe mode”Font preloading and @font-face output are skipped entirely in wireframe mode. Wage\Assets::preload_fonts() and Wage\Assets::output_font_faces() both check Wage\Flags::mode() === 'wireframe' and return early. The site renders in system fonts regardless of what font files exist.
No Google Fonts CDN
Section titled “No Google Fonts CDN”Every project self-hosts fonts. No external requests to fonts.googleapis.com. This eliminates:
- External dependency on Google’s servers
- Extra DNS lookups and TCP handshakes
- Privacy concerns (Google tracking via font requests)
- Flash of unstyled text from late-loading external CSS