How to find the gaps in a font family's weight lineup
- Step 1Upload the font to audit — Drop the TTF, OTF, WOFF, or WOFF2. The viewer reads `fvar` to enumerate weights, decompressing WOFF/WOFF2 first.
- Step 2Count the rows — One row labelled `Static` = a single weight. Multiple named or numeric rows = a real range. Compare that count against the weights your design needs.
- Step 3Check the range ends — Note the first and last numeric rows. Because steps round the start up and clip the top to the last multiple of 100, the genuine min/max may sit just outside the rendered rows.
- Step 4Look for absent instances — For named-instance fonts, scan for the weights you expect (e.g. SemiBold). A missing one simply won't have a row — that's your gap.
- Step 5Verify against your tokens — Map your design's weight tokens to rendered rows. Any token with no nearby row is a weight the font can't deliver.
- Step 6Decide: subset, swap, or add a file — If a weight is missing, plan to source it, choose a different family, or — for a static gap — load an additional file. This single-file viewer audits one font at a time.
What each render result tells you about the lineup
Read the row pattern to diagnose the font's weight coverage. All outcomes come straight from fvar.
| Render result | Diagnosis | Action |
|---|---|---|
Single Static row | One static weight (or a wght-less variable font) | Need more weights? Source another file or family |
| Numeric rows, narrow range (e.g. 300–600) | Variable, but limited axis | Don't spec weights outside the range — they clamp |
Numeric rows up to e.g. wght 800, none at 900 | Axis max likely < 900 | Avoid "wght" 900; top usable round weight is 800 |
| Named rows missing an expected style | That instance isn't defined | Use a nearby instance or pick another family |
| Full 100–900 stack or Thin–Black instances | Complete lineup | Map tokens freely |
Why the rendered ends can mislead — and how to read them
The 100-unit stepping rounds the start and clips the top. Keep this in mind when judging the true range.
| Declared axis | First rendered row | Last rendered row | True ends not shown |
|---|---|---|---|
| min 1, max 1000 | wght 100 | wght 1000 | min 1 (floor never a row) |
| min 50, max 950 | wght 100 | wght 900 | min 50 and max 950 both clipped |
| min 200, max 800 | wght 200 | wght 800 | (none — both on 100s) |
| min 100, max 850 | wght 100 | wght 800 | max 850 clipped (no 900 row) |
Cookbook
Five audit walk-throughs. Each shows the rendered result and the gap it reveals — the viewer never invents a weight, so absence is the signal.
The 'variable' font that's really static
ExampleA download labelled variable renders a single Static row — it carries one weight only.
Rendered: 1 row -> Static (font-weight: 400) Metric: Has fvar = false Diagnosis: not actually variable. For multiple weights you'll need additional files.
Axis stops short of Black
ExampleThe top rendered numeric row reveals the axis ceiling is below 900.
Rendered rows: wght 100 … wght 800 (no wght 900 row) Diagnosis: axis max < 900. A hero set to "wght" 900 will clamp to the max. Use 800 as the heaviest.
Missing SemiBold in a named-instance family
ExampleScanning the named rows shows Medium jumps straight to Bold — no 600.
Named rows: Light, Regular, Medium, Bold, Black (no SemiBold / 600 instance) Diagnosis: 500 -> 700 gap. If your system needs 600, set "wght" 600 on the axis directly, or pick another family with the instance.
Narrow text-only variable font
ExampleA text-optimised file may only span 300–600, fine for body but not display.
Rendered rows: wght 300, wght 400, wght 500, wght 600 Diagnosis: good for body, no display weight. Pair with a separate display cut for headlines.
Confirming a complete lineup
ExampleA full Thin–Black instance list (or 100–900 steps) means you can map every token.
Named rows: Thin, ExtraLight, Light, Regular, Medium, SemiBold, Bold, ExtraBold, Black Diagnosis: complete. Every common weight token maps to a row.
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.
Variable font with no wght axis read as single weight
Single Static rowIf the file is variable on other axes only (e.g. opsz), the viewer can't step weight and renders one Static row at 400. The metric Has fvar = true will still be set, so check the metric, not just the row count, to tell this from a truly static font.
Genuine axis minimum below 100 hidden
Floor not renderedSteps start at ceil(min/100)*100, so a min of 1 or 50 never gets its own row. Don't conclude the font can't go below 100 — it can; the viewer just doesn't render sub-100 multiples.
Axis max not on a hundred clipped
Top weight understatedA max of 850 renders up to wght 800. The font can reach 850 via font-variation-settings, but the viewer shows 800 as the top row. Read the true max with an axis inspector if precision matters.
Static file that's actually Bold shows as 400
Misleading weight labelThe static fallback applies font-weight: 400 regardless of the file's real style, so an actual Bold static renders at 400 weight. Use the font metadata extractor to read its declared weight.
Named-instance gap vs axis continuity
Gap is in names, not rangeA missing SemiBold instance doesn't mean the axis skips 600 — on a continuous wght axis you can still set "wght" 600. The gap is only in the curated named points.
Many instances make gaps hard to see
Scroll requiredSuperfamilies with width + weight instances produce long lists; an expected weight can be present under a width variant. Read the settings strings to confirm what's actually missing.
Corrupt or non-font upload
Error: Unsupported font format.If the file isn't TTF/OTF/WOFF/WOFF2, fileToSfntBuffer throws Unsupported font format. Confirm the format with the font format identifier first.
File over the 5 MB free limit
RejectedLarge desktop variable fonts can exceed 5,000,000 bytes. Convert to WOFF2 or subset, then audit the smaller file.
Single-instance variable font
One numeric or named rowA variable font with a wght axis whose min and max round to the same hundred, or with one instance, renders a single weight row — still a real (if minimal) range, distinct from the static fallback.
Frequently asked questions
How do I tell if a font is really variable or just one weight?
Run the viewer. A single Static row (and metric Has fvar = false) means one static weight. Multiple rows mean a real range. A Has fvar = true with a single Static row means it's variable on a non-weight axis.
Does a missing row mean the weight is impossible?
For named instances, a missing instance is a gap in curated points — but on a continuous axis you can still set any value in range. For numeric steps, a missing top row usually means the axis max is below it.
Why doesn't the viewer show my font's exact minimum weight?
Numeric steps start at the first multiple of 100 at or above the minimum, so a min of 1 or 50 isn't rendered as its own row. The font can still go that low via font-variation-settings.
How do I know the true axis max?
The viewer's top numeric row is the last multiple of 100 within range, which can understate a max like 850. For the exact min/default/max, inspect the axis with the variable font freezer.
My static font shows as weight 400 but it's Bold — why?
The static fallback always applies font-weight: 400. To read the file's real declared weight, use the font metadata extractor.
Can I audit two fonts at once to compare lineups?
No — it's single-file. Audit each font separately and compare the rendered stacks.
Will it tell me which OpenType features the font has?
No, it audits weights only. For features like ligatures and stylistic sets, use the opentype features inspector.
What if my upload isn't a font?
You'll get Unsupported font format. Verify the type with the font format identifier first.
Does the row count equal the number of usable weights?
For named-instance fonts, yes — each row is an instance. For continuous axes, the rows are 100-unit samples; usable weights are the entire range between min and max.
Can it add a missing weight, like synthesising a Bold?
No. It only renders what the font carries. Faux-bold synthesis is a browser behaviour the viewer doesn't apply.
What's the file size limit for auditing?
5 MB on the free tier. Convert large fonts to WOFF2 or subset them first.
How do I read whether a 'gap' is in names or the axis?
If the font shows named rows, a missing name is a curated-point gap, not necessarily an axis gap. Cross-check the declared axis range to know whether the weight is reachable numerically.
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.