How to generate a production-ready @font-face block online
- Step 1Type the CSS font-family name — The `Family` field becomes `font-family: "…"` in the block. It does **not** have to match the font's internal name table — it's just the identifier your CSS `font-family` rules will reference. Pick whatever your design system uses (e.g. `Inter`, `Brand Sans`). Quotes are added for you.
- Step 2Set the base path with no extension — `Base path` is the URL stem — type `/fonts/inter`, not `/fonts/inter.woff2`. The generator appends `.woff2`, `.woff`, and `.ttf` to build each `src` URL. If you do include an extension, it is stripped before the format suffixes are added, so a stray `.woff2` won't produce `inter.woff2.woff2`.
- Step 3Choose weight and style — `Weight` is a single number, 100–900 in steps of 100 (the numeric `font-weight`). `Style` is a select: `normal`, `italic`, or `oblique`. Each generated block describes **one** weight/style pair — this is the one place a variable font needs a different approach (see the FAQ).
- Step 4Pick a font-display value — `Display` defaults to `swap` (fallback shows immediately, then swaps to the web font). The other four CSS values — `auto`, `block`, `fallback`, `optional` — are all available. If you're unsure which fits your case, the [Font Display Strategy tool](/font-tools/font-display-strategy) explains the trade-offs and picks one for you.
- Step 5Tick the formats you'll actually serve — Three checkboxes — **WOFF2**, **WOFF**, **TTF** — control which URLs land in the `src` list. WOFF2 is on by default. Each ticked format adds one `url(...) format(...)` entry in fixed order. If you somehow untick all three, the generator falls back to a single WOFF2 entry rather than emit an empty `src`.
- Step 6Add a unicode-range (optional), then copy — Leave `unicode-range` blank for a whole-font block, or paste a range list like `U+0000-00FF, U+0131` to scope the block to a subset. Hit run and use **Copy to clipboard** (or download the `.css` file). Paste it into your stylesheet — it's a complete, valid declaration.
The seven fields and what they emit
Every control in the @font-face Generator UI, the CSS line it produces, and its default. There are no other options — no size-adjust, ascent-override, or font-stretch fields in this tool (see the descriptors reference for why).
| Field | CSS it writes | Default | Notes |
|---|---|---|---|
Family (text) | font-family: "…"; | MyFont | Quotes added automatically; need not match the font's internal name |
Base path (text) | URL stem for each src entry | /fonts/myfont | No extension — .woff2/.woff/.ttf appended; any trailing extension is stripped first |
Weight (number) | font-weight: N; | 400 | Single value 100–900, step 100 — one weight per block |
Style (select) | font-style: …; | normal | normal / italic / oblique |
Display (select) | font-display: …; | swap | auto / block / swap / fallback / optional |
unicode-range (text) | unicode-range: …; | (empty) | Emitted verbatim only if you supply a value; omitted otherwise |
| WOFF2 / WOFF / TTF (checkboxes) | src: entries + format() hints | WOFF2 on | Each ticked format adds one ordered url() format() pair |
src ordering and the format() hint each checkbox emits
The generator always writes the src list smallest-format-first. The format() strings are fixed — these exact three, no others.
| Checkbox | src entry emitted | format() hint | Position in list |
|---|---|---|---|
| WOFF2 | url("<path>.woff2") format("woff2") | woff2 | 1st (always first when ticked) |
| WOFF | url("<path>.woff") format("woff") | woff | 2nd |
| TTF | url("<path>.ttf") format("truetype") | truetype | 3rd |
| (none ticked) | url("<path>.woff2") format("woff2") | woff2 | Fallback — never emits an empty src |
Cookbook
Each recipe shows the exact field values on the left and the literal block the generator prints on the right. Copy the output straight into your stylesheet.
The modern default — WOFF2-only, swap
ExampleFor a site that doesn't care about IE11 or pre-2016 mobile, one WOFF2 source is all you need. Tick only WOFF2, leave the rest at defaults.
Fields:
Family: Inter
Base path: /fonts/inter
Weight: 400 Style: normal Display: swap
Formats: [x] WOFF2 [ ] WOFF [ ] TTF
Output:
@font-face {
font-family: "Inter";
src: url("/fonts/inter.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}Belt-and-braces — WOFF2 + WOFF + TTF fallback chain
ExampleWhen you must support legacy engines, tick all three. The browser walks the list and uses the first format it understands, so modern browsers still take the WOFF2.
Fields:
Family: Inter
Base path: /fonts/inter
Formats: [x] WOFF2 [x] WOFF [x] TTF
Output:
@font-face {
font-family: "Inter";
src: url("/fonts/inter.woff2") format("woff2"),
url("/fonts/inter.woff") format("woff"),
url("/fonts/inter.ttf") format("truetype");
font-weight: 400;
font-style: normal;
font-display: swap;
}Italic of the same family
ExampleItalic is a separate file and a separate block. Re-run with Style: italic and point the base path at the italic file. Both blocks share the family name so font-style: italic selects the right one.
Fields:
Family: Inter
Base path: /fonts/inter-italic
Weight: 400 Style: italic
Output:
@font-face {
font-family: "Inter";
src: url("/fonts/inter-italic.woff2") format("woff2");
font-weight: 400;
font-style: italic;
font-display: swap;
}A Latin subset block with unicode-range
ExamplePaste a range list into the unicode-range field and the browser only fetches this file when a matching character is rendered. Pair it with a second block (different range, different file) for multi-script sites.
Fields:
Family: Inter
Base path: /fonts/inter-latin
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F
Output:
@font-face {
font-family: "Inter";
src: url("/fonts/inter-latin.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F;
}A four-weight family — four runs, concatenated
ExampleThe tool emits one block per run. For Regular + Medium + SemiBold + Bold, run it four times (changing Weight and Base path) and paste the four blocks into the same stylesheet under one family name. To automate this, see the build-time guide.
Run 1: Weight 400, path /fonts/inter-regular → block A Run 2: Weight 500, path /fonts/inter-medium → block B Run 3: Weight 600, path /fonts/inter-semibold → block C Run 4: Weight 700, path /fonts/inter-bold → block D Final CSS = A + B + C + D, all font-family: "Inter" (font-weight selects the right file at render time)
Edge cases and what actually happens
Every row below was probed against the live API. Some documented requirements (alphabetical axis order, numerical tuple order) are not actually enforced in practice — useful to know if you've been blaming the wrong thing for a 400.
You untick all three format checkboxes
By designRather than emit src: with nothing in it (which would be invalid CSS), the generator falls back to a single url("<path>.woff2") format("woff2") entry. So the worst case is a WOFF2-only block, never a broken one.
You type a full filename like /fonts/inter.woff2 in Base path
PreservedThe trailing extension is stripped before the format suffixes are appended (.replace(/\.[^.]+$/, "")), so you get /fonts/inter.woff2 not /fonts/inter.woff2.woff2. Type the stem to be safe, but a stray extension won't corrupt the URLs.
You need size-adjust or ascent-override for zero CLS
Not in this toolThis generator only writes font-family, src, font-weight, font-style, font-display, and optional unicode-range. The metric-override descriptors (size-adjust, ascent-override, descent-override, line-gap-override) are not generated — you add them by hand. See the descriptors reference for the values and the best-practices guide for the CLS pattern.
You want one block for a whole variable-weight family
Not supportedWeight is a single number input (100–900, step 100); you can't type a range like 100 900 into it. A variable font that exposes a weight range needs a hand-edited font-weight: 100 900; block, or freeze it to a static instance with the Variable Font Freezer and generate one block per instance.
You serve an OTF (CFF) file and want format("opentype")
Not emittedThe three checkboxes only produce woff2, woff, and truetype hints. There is no opentype checkbox, because shipping raw OTF to the web is rare and you should convert to WOFF2 anyway via TTF/OTF to WOFF2. If you genuinely must reference an .otf, hand-edit the hint to opentype.
Family name has spaces (e.g. Brand Sans)
SupportedSpaces are fine — the generator wraps the value in double quotes: font-family: "Brand Sans";. The output .css filename lower-cases and hyphenates the name (brand-sans-fontface.css), but the CSS identifier inside keeps your exact casing and spacing.
You leave unicode-range empty
ExpectedThe unicode-range line is omitted entirely — you get a clean whole-font block with no empty descriptor. Only supply a range when you're shipping subsets; an empty range would be meaningless.
EOT for very old IE
Not offeredThere is no EOT (format("embedded-opentype")) checkbox. EOT served IE6–8, which are long dead; including it is pure bloat. If a legacy intranet still needs it, hand-add the entry — but the right minimum today is WOFF (IE9–11) plus WOFF2.
Weight typed as 450 or 350
SteppedThe number input uses step 100, so the spinner moves in hundreds, but a manually typed in-between value like 450 is passed through to font-weight: 450;. That's valid CSS (custom numeric weights exist on variable fonts), so it isn't rejected — just confirm the file actually has that instance.
The CSS won't load the font in the browser
Check the pathThe generator never validates that the files at /fonts/inter.woff2 exist — it only builds the text. A 404 on the src URL, a wrong base path, or a MIME-type misconfiguration on the server are the usual culprits, none of which the tool can see. Confirm the files are deployed and reachable at the exact paths in the block.
Frequently asked questions
Does this tool upload my font file?
No. The @font-face Generator is purely generative — it produces CSS text from the fields you type and never asks for a font file. Nothing is uploaded; the tool runs in your browser and the output is deterministic. That makes it safe for licensed or commercial fonts, since the binary never leaves your machine.
What's the correct src ordering and does the tool get it right?
WOFF2 first (smallest, modern browsers stop here), then WOFF, then TTF. The browser walks the src list top-to-bottom and uses the first format it understands, so listing the smallest first means modern browsers never download the larger fallbacks. The generator always writes the list in this order automatically — you can't accidentally invert it.
Why is the format hint `truetype` and not `ttf`?
Because the CSS spec's format string for TrueType is truetype, not ttf. A format("ttf") hint is unrecognised and browsers may skip that source. The generator emits format("truetype") for the TTF checkbox automatically, which is one of the most common hand-written mistakes it removes. See format() hints explained for the full list.
Can it generate blocks for all my weights at once?
No — each run emits one block for one weight/style. For a multi-weight family, run the tool once per weight (changing the Weight and Base path each time) and paste the blocks into the same stylesheet under one family name. To do this in CI without clicking, see the build-time generation guide.
Do I type the file extension in the Base path?
No — type the stem only, e.g. /fonts/inter. The generator appends .woff2, .woff, and .ttf per ticked checkbox. If you do paste an extension, it's stripped before the suffixes are added, so you won't get a doubled extension, but the stem-only form is clearest.
Should I always include font-display?
Yes, and the tool does by default. font-display: swap shows a fallback during load then swaps to the web font, preventing the invisible-text flash (FOIT). The generator always writes a font-display line (defaulting to swap), so you never ship a block that omits it. For body text where a late swap is jarring, optional is the gentler choice.
When should I add a unicode-range?
When you ship more than one language/script subset of the same family. With unicode-range, the browser only downloads the subset file whose range matches characters actually rendered on the page — big savings on multilingual sites. For a single-language site there's nothing to scope, so leave it blank. Use the Character Coverage Map to confirm which ranges your font actually covers.
Does it emit size-adjust or ascent-override for CLS?
No. This tool covers font-family, src, font-weight, font-style, font-display, and unicode-range only. The metric-matching descriptors that eliminate layout shift (size-adjust, ascent-override, descent-override, line-gap-override) aren't generated here — add them by hand using the values in the descriptors reference.
Does the family name have to match the font's internal name?
No. The Family value is just the CSS identifier you'll reference in font-family rules; it's independent of the name stored in the font's name table. Many design systems deliberately use a neutral token like Brand Sans so the underlying font can be swapped without touching every component. If you want to read the internal name, run the Font Metadata Extractor.
What if I don't tick any format?
The generator falls back to a single WOFF2 entry rather than emitting an empty src, which would be invalid CSS. So unticking everything gives you a WOFF2-only block — the same as ticking just WOFF2. There's no way to produce a broken src from this tool.
Can I generate the CSS I need where I host the font files?
The generator only builds the declaration text; it has no idea where your files live or whether they exist. You supply the base path and it constructs the URLs. After pasting the block, confirm the files are deployed at exactly those paths and that your server sends a font MIME type — a 404 or wrong content-type is the usual reason a correct block still fails to load.
Can I produce this block from a script instead of the UI?
Yes. The tool is generative and exposes the same options over the JAD runner's local HTTP API — POST http://127.0.0.1:9789/v1/tools/font-face-generator/run with a JSON inputs body (family, base path, weight, style, font-display, the include flags) returns the CSS. That's the basis of the build-time generation guide, which wires it into CI from your fonts directory.
Privacy first
Every JAD Font tool runs entirely in your browser using opentype.js and the wawoff2 WASM Brotli encoder. Your fonts never leave your device — verified by zero outbound network requests during processing.