You downloaded a variable font, put it on your site, and only one weight works. Bold does nothing, or bold looks fake and slightly smeared. This is the most common problem with variable fonts and it has four possible causes, in descending order of likelihood.
One: the font-weight in your @font-face is a single number
This is it about eight times out of ten. A static font declares font-weight: 400. A variable font has to declare the range it covers — font-weight: 100 900 — because that descriptor is how the browser learns the file has a range at all.
Declare a single value and the browser believes you. Ask for bold and it will synthesise one by smearing the regular, which is the "fake bold" look. The range has to match what the font actually contains; every font page on this site generates the rule with the correct range already in it.
Two: the platform form asked which weight it was
Webflow, Shopify and most theme uploaders ask you to tag each uploaded file with a weight and style, because that is how they build the CSS for you. Tag a variable file as 400 and you have just done the same thing as above through a form instead of a stylesheet. These platforms support variable fonts; you have to tell them the file is one.
Three: the application does not read variable fonts
If the problem is in Word, Canva, or an older version of Photoshop rather than in a browser, the font is fine and the software is not. Those read the file’s default instance and offer one weight. The fix is the static download — every variable family on this site ships a second zip with one ordinary file per weight, and the variable font post explains which to take.
Four: the file defaults to something other than regular
A handful of families default to a weight that is not 400 — some open to a hairline. Install one of those and the menu shows a very thin font, which reads as the download being broken when it is not. Font pages here say so explicitly where it applies, because the alternative is somebody deleting a perfectly good file.
How to check what the file actually contains
Every font page here lists the weights measured from the file rather than claimed by a description, so the range in the CSS snippet is the range in the font. If you got the file elsewhere, drop it into the font converter — it reads the file’s tables and will tell you what it is, which is also how you find out that a file called .ttf is actually something else.
| Family | Variable download | Static download |
|---|---|---|
| Inter | 957 KB2 files | 9.1 MB54 files |
| Roboto Flex | 1017 KB1 file | None published |
| Outfit | 58 KB1 file | 270 KB9 files |
| Lora | 224 KB2 files | 555 KB8 files |
Five: the browser is faking the weight instead of using the axis
If bold text looks smeared or slightly blurry rather than genuinely heavier, the browser has drawn the regular and thickened it artificially. That happens when the family it is asked for does not contain a bold — a common outcome when a variable file was declared with a single weight, as above, or when the fallback stack is doing the rendering because the font never arrived.
The tell is that every weight looks like the same weight, slightly bolder. Setting font-synthesis: none turns the faking off, which does not fix anything but makes the fault visible instead of plausible. Then check that the file is loading at all — see self-hosting a font for the two lines that usually go wrong.
Getting the axis into your CSS properly
Two properties reach a variable font and they are not equivalent. font-weight asks for a weight and lets the browser use the axis, inheritance, and the fallback stack normally. font-variation-settings sets the axis directly by its four-letter tag, overrides everything, and does not inherit in the way anybody expects. Use the first for weight, and keep the second for axes that have no CSS property of their own — optical size, grade, a foundry’s own invention.
- In
@font-face, give the range: two numbers, not one. - In the rules, ask for a weight:
font-weight: 620is a legitimate value for a variable family and will be rendered exactly. - Do not declare the same family twice with different files unless each declaration names a different style — two competing declarations is the other way a page ends up with one weight.
| Family | Weights in the file | Installs as | Way out |
|---|---|---|---|
| Inter | 100–900 | 400Regular | Static download54 files |
| Roboto Flex | 100–1000 | 400Regular | Noneno static build |
| Outfit | 100–900 | 100Thin | Static download9 files |
| Lora | 400–700 | 400Regular | Static download8 files |
When the file itself is the problem
Subsetting can remove the axes. A tool asked for Latin characters and nothing else may flatten the file to a single instance, which is correct behaviour for a page that only ever sets one weight and a disaster for one that does not. If the weights disappeared after you optimised the file, rebuild it keeping the axis — the subsetting guide has the flag.
The other file-level cause is a static build wearing a variable name. Plenty of downloads called "Variable" on other sites are a folder of ordinary weights, and plenty of "static" folders contain one file per weight that works everywhere. Every family here says which it is, with the file count beside the button.
The smallest thing that works
When several of the causes above are possible at once, start from a page that does nothing else. Four declarations are enough: an @font-face with a weight range rather than a single number, a font-family rule on the body, a heading at one weight and a paragraph at another. If that page renders two distinct weights, the file is fine and the fault is in the project.
From there, add one thing at a time — the framework’s CSS, the theme, the component library — and reload after each. The step that flattens the weights is the answer, and it is usually a global rule setting font-weight on an element you forgot about. The same routine is how to prove a self-hosted setup works at all; see how to self-host a font.
