How to every @font-face descriptor, explained
- Step 1Start with the two required descriptors — `font-family` (the CSS identifier this face answers to) and `src` (the `url() format()` list) are mandatory — a rule without both is invalid. The [generator](/font-tools/font-face-generator) always writes both, with `src` ordered WOFF2 → WOFF → TTF and correct hints.
- Step 2Set the matching descriptors: weight and style — `font-weight` (here, the weight this *file* represents — `400`, or a range like `100 900` for a variable font) and `font-style` (`normal`/`italic`/`oblique`). The generator writes a single numeric weight and a style from its select; it cannot express a variable `100 900` range.
- Step 3Add the performance descriptors — `font-display` (`auto`/`block`/`swap`/`fallback`/`optional`) controls swap behaviour; `unicode-range` scopes the face to a character subset. The generator emits `font-display` always and `unicode-range` when you fill the field.
- Step 4Hand-add the metric overrides for zero CLS — `size-adjust` (global glyph scale), `ascent-override`, `descent-override`, `line-gap-override` (line-box metrics as % of em). These tune a fallback face to match a web font so swapping causes no layout shift. None are generated — read the web font's values with the [Font Metrics Analyzer](/font-tools/font-metrics-analyzer) and write them yourself.
- Step 5Hand-add stretch and feature defaults if needed — `font-stretch` (`ultra-condensed` … `ultra-expanded`, or a % range), `font-feature-settings` (default OpenType features for this face), `font-variation-settings` (default variable-axis values). These are rare and not generated — add them manually when a design token needs them.
- Step 6Validate the descriptor-vs-property meaning — Remember each descriptor *describes the file*, it doesn't *apply* a style. `font-weight: 700` in an `@font-face` says 'this file is the Bold' — it doesn't make text bold. Mismatching the descriptor to the actual file (tagging a Regular file `700`) is a silent bug the generator avoids by taking your explicit input.
Every @font-face descriptor
Descriptor, what it does inside the rule, an example value, and whether the @font-face Generator emits it. Browser support is current as of 2026; metric overrides landed in Chrome 87 / Firefox 89 / Safari 15.4.
| Descriptor | Role in the rule | Example value | Generator emits it? |
|---|---|---|---|
font-family | The CSS identifier this face answers to (required) | "Inter" | Yes |
src | Ordered url() format() source list (required) | url("i.woff2") format("woff2") | Yes |
font-weight | Which weight (or range) this file represents | 400 or 100 900 | Yes (single value only) |
font-style | Which style this file is | normal / italic / oblique | Yes |
font-display | Swap behaviour during load | swap | Yes |
unicode-range | Character subset this face covers | U+0000-00FF | Yes (if supplied) |
size-adjust | Global glyph scale (% of em) for metric matching | 107% | No — hand-add |
ascent-override | Override the face's ascent metric (% of em) | 90% | No — hand-add |
descent-override | Override the descent metric (% of em) | 22% | No — hand-add |
line-gap-override | Override the line-gap metric (% of em) | 0% | No — hand-add |
font-stretch | Width/stretch this file represents | condensed or 75% 125% | No — hand-add |
font-feature-settings | Default OpenType features for this face | "liga" 1, "kern" 1 | No — hand-add |
font-variation-settings | Default variable-axis values for this face | "wght" 450 | No — hand-add |
Metric-override descriptors — browser support
The four descriptors that eliminate font-swap CLS. All four are hand-added; the generator does not produce them. Support figures current as of 2026.
| Descriptor | Chrome | Firefox | Safari | Fallback if unsupported |
|---|---|---|---|---|
size-adjust | 92+ | 92+ | 17+ | Ignored — web font renders at native size |
ascent-override | 87+ | 89+ | 15.4+ | Ignored — native ascent used |
descent-override | 87+ | 89+ | 15.4+ | Ignored — native descent used |
line-gap-override | 87+ | 89+ | 15.4+ | Ignored — native line gap used |
Cookbook
Each recipe shows a descriptor in context. Blocks marked 'generated' come from the @font-face Generator; 'hand-added' blocks you write yourself.
The six generated descriptors in one block
ExampleEverything the @font-face Generator writes, all at once: family, src, weight, style, font-display, and unicode-range.
/* generated */
@font-face {
font-family: "Inter";
src: url("/fonts/inter.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
unicode-range: U+0000-00FF, U+0131;
}Metric overrides on a fallback face (hand-added)
ExampleThe four overrides plus size-adjust, applied to a system-font fallback so it occupies the same box as the web font. Values come from the web font's metrics — read them with the Font Metrics Analyzer.
/* hand-added — NOT produced by the generator */
@font-face {
font-family: "Inter Fallback";
src: local("Arial");
size-adjust: 107%;
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
}Variable font weight range (hand-edited)
ExampleThe generator writes a single font-weight. To cover a whole variable range in one block, hand-edit the value to a range. Or freeze instances and generate one static block each.
Generator output (single weight):
font-weight: 400;
Hand-edited for a variable WOFF2:
@font-face {
font-family: "Inter";
src: url("/fonts/inter.var.woff2") format("woff2");
font-weight: 100 900; /* the variable wght range */
font-style: normal;
font-display: swap;
}font-feature-settings as a face default (hand-added)
ExampleBind default OpenType features to a face so every element using it gets them without per-element rules. Not generated — append it yourself.
@font-face {
font-family: "Inter";
src: url("/fonts/inter.woff2") format("woff2");
font-weight: 400; font-style: normal; font-display: swap;
/* hand-added: */
font-feature-settings: "liga" 1, "calt" 1, "tnum" 1;
}font-stretch on a condensed face (hand-added)
ExampleDeclare that a file is the condensed cut so font-stretch: condensed on an element selects it. The generator has no stretch field — add this line by hand.
@font-face {
font-family: "Inter";
src: url("/fonts/inter-condensed.woff2") format("woff2");
font-weight: 400; font-style: normal; font-display: swap;
/* hand-added: */
font-stretch: condensed; /* or 75% */
}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.
Looking for a size-adjust field in the generator
Not presentThere's no size-adjust (or ascent-override/descent-override/line-gap-override) control in the @font-face Generator. These are hand-added descriptors. The right value depends on the specific font's metrics, which is why they're not a generic UI option — read the metrics with the Font Metrics Analyzer and write them yourself.
Confusing the descriptor with the property
Common mix-upfont-weight: 700 inside @font-face describes which file this is (the Bold); it does not make text bold. The matching happens when an element requests font-weight: 700. Tagging a Regular file as 700 in the descriptor means the browser uses it for bold requests — silently wrong. The generator avoids this by writing exactly the value you choose.
Variable font-weight range in the UI
Single value onlyThe generator's Weight is a number input, so it emits font-weight: 400, not a range. A variable font that should answer the whole 100 900 range needs the descriptor hand-edited to font-weight: 100 900;, or you generate one block per frozen instance via the Variable Font Freezer.
font-stretch declared but ignored
Check the filefont-stretch: condensed in the descriptor only helps if the file actually *is* the condensed cut and an element requests that stretch. Declaring it on a normal-width file mislabels the face. It's hand-added (no generator field), so confirm the file's width before writing it.
Metric overrides on an old browser
Gracefully ignoredsize-adjust arrived later (Chrome 92 / Safari 17) than the *-override trio (Chrome 87 / Firefox 89 / Safari 15.4). Browsers that don't know a descriptor ignore it and render the web font at native metrics — so you get slightly more CLS on old browsers, never a broken rule. Progressive enhancement, not a hard dependency.
unicode-range overlap between two faces
Last match wins-ishIf two @font-face rules for the same family have overlapping ranges, the browser's selection for an overlapping codepoint follows cascade/order rules and can be surprising. Keep ranges disjoint. The generator emits whatever range you type verbatim — it doesn't check for overlap, so that's on you.
Expecting font-variation-settings as a generator option
Not presentBinding default axis values (font-variation-settings: "wght" 450) to a face is a real technique, but the generator has no field for it. Add it by hand. To produce discrete static weights from one variable file instead, use the Variable Font Freezer.
Missing the required src descriptor
invalid ruleAn @font-face without src (or without font-family) is invalid and the browser drops the whole rule. The generator can never produce this — if you untick every format checkbox it still emits a WOFF2 src, and font-family always has a value. Hand-written rules are where this happens.
font-feature-settings vs the property of the same name
Both validAs a *descriptor* it sets the default features for any element using this face; as an element *property* it sets features for that element only. They coexist. The descriptor form is a design-token convenience and is hand-added — the generator doesn't write it.
Quoting the family name
Recommendedfont-family: Inter (unquoted) is legal only because Inter is a single identifier; multi-word names like Brand Sans must be quoted. The generator always quotes the value, so you never hit the unquoted-multi-word trap. In hand-written rules, always quote to be safe.
Frequently asked questions
How many descriptors can an @font-face rule have?
Around a dozen are in common use: the two required ones (font-family, src), the matching descriptors (font-weight, font-style, font-stretch), the performance ones (font-display, unicode-range), the metric overrides (size-adjust, ascent-override, descent-override, line-gap-override), and the default-setting ones (font-feature-settings, font-variation-settings). Most teams use five or six; this page lists them all with example values.
Which descriptors does the @font-face Generator emit?
Six: font-family, src (with WOFF2→WOFF→TTF ordering and correct format() hints), font-weight (a single number), font-style, font-display, and unicode-range when you supply a value. Everything else — metric overrides, font-stretch, font-feature-settings, font-variation-settings — you add by hand.
What's the difference between a descriptor and a property?
Inside @font-face, names like font-weight are *descriptors*: they describe which file this is (font-weight: 700 = 'this is the Bold file'). On an element, the same name is a *property*: it *requests* a weight, and the browser matches it to the face whose descriptor says it's that weight. Mismatching them (tagging a Regular file 700) silently uses the wrong file.
Why isn't size-adjust in the generator?
Because the correct size-adjust value is specific to the web font's metrics relative to the fallback you chose — it can't be derived from the generic block options. The generator writes the universally-correct core block; you compute size-adjust (and the *-override trio) from the actual font and add them to a fallback block. Read the metrics with the Font Metrics Analyzer.
What do the metric-override descriptors do?
size-adjust scales every glyph globally; ascent-override/descent-override/line-gap-override set the line-box metrics as a percentage of the em. Together, applied to a fallback face, they make the fallback occupy the same vertical and horizontal space the web font will — so when the web font swaps in, nothing on the page shifts (zero CLS).
What's the browser support for ascent-override and size-adjust?
The *-override trio (ascent-override/descent-override/line-gap-override) landed in Chrome 87, Firefox 89, and Safari 15.4. size-adjust came a bit later — Chrome 92, Firefox 92, Safari 17. Older browsers ignore any descriptor they don't recognise and render at native metrics, so you get slightly more swap-CLS on old engines, never a broken rule.
Can I set a font-weight range in the descriptor?
Yes in CSS — font-weight: 100 900; is valid for a variable font and tells the browser the face covers that range. But the generator's Weight field is a single number, so it emits one value; hand-edit it to a range for a variable WOFF2, or freeze named instances with the Variable Font Freezer and generate a static block per instance.
What is font-feature-settings on @font-face for?
Used as a descriptor, it sets the default OpenType features ("liga", "calt", "tnum", etc.) for any element rendered in that face — handy for binding features to a design-token face without repeating them per element. It's less common than the per-element property form and is not generated; add it by hand. To inspect which features a font has, use the OpenType Features Inspector.
What is font-variation-settings on @font-face for?
It sets default variable-axis values for the face — e.g. font-variation-settings: "wght" 450; to bind a single variable file to a 450-weight default. This lets one variable WOFF2 back several discrete @font-face entries at different default weights. The generator has no field for it; add it manually, or freeze the instances with the Variable Font Freezer.
Do I have to quote the font-family value?
Quote it whenever it's more than one word or contains characters that aren't valid in a CSS identifier — "Brand Sans" must be quoted; a bare Inter is tolerated. The generator always quotes the value, so its output is safe regardless of the name. In hand-written rules, quoting consistently avoids the multi-word trap.
Is font-stretch a descriptor or a property?
Both, like the others. As a descriptor it declares which width cut a file is (font-stretch: condensed; = 'this is the condensed file'); as a property it requests a width. It's not in the generator UI, so if you ship a condensed cut, add the descriptor by hand so font-stretch: condensed on an element selects it.
Where do I generate the core block these descriptors live in?
Use the @font-face Generator for the six it produces, then hand-add the rest from this reference. The online how-to walks every field, format() hints explained covers the src hints, and the 2026 best-practices guide shows where the metric overrides fit.
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.