Fonts / What the engine does with it
Faux bold and faux italic, and how to tell you are getting them
What the engine does when you ask for a weight or a style the family does not have: it makes one up, and the result is worse than the real thing in ways that are easy to miss.
What is actually going on
Ask for bold from a family where only the regular weight was loaded and the engine will not refuse. It smears the outlines to thicken them, which is not what a designed bold looks like: the counters close up, the stroke contrast disappears, and the spacing was never adjusted for the extra weight. Faux italic is worse, because it is the upright letterforms mechanically slanted, and a real italic is a different design with different letterforms, not the same ones at an angle. Both are easy to miss on screen at large sizes and obvious in print at text sizes, and both happen silently, because the mechanism exists precisely to avoid failing.
How fonts reach this API
There is no font option on the render. A font arrives the way every other subresource arrives: your @font-face names a URL, the renderer fetches it while the document loads, and what was used is embedded in the output. So there is no font upload, no list of available fonts, no substitution control and no subsetting switch. It also means the two failures that actually stop a font are network-shaped rather than typographic: the URL has to be absolute and publicly reachable, and the fetch has to finish before the page is captured. Everything else on this page is decided in your stylesheet and in the font file itself.
The decisions
Reasons rather than a description of the code. Each of these has a default, and the default is chosen by the engine rather than by you.
- Load a real face for every weight and style the document uses. If the document uses regular, bold and italic, that is three @font-face declarations, not one.
- Declare the weight and style on each @font-face accurately. A bold file declared at weight 400 will be used for regular text and then synthesised into a heavier fake when bold is asked for.
- Use only the weights you loaded. A stylesheet asking for 600 from a family that has 400 and 700 gets one of them adjusted, and which one is not up to you.
- Where a real italic does not exist for the family, do not fake it. Use weight, size, or a rule to carry the emphasis instead.
- Watch out for a heading set in a weight nobody loaded. It is the most common instance, because headings are styled separately and the font loading was set up for body text.
- Remember every real face is an extra embedded font in the output. Three faces is right; nine is a document carrying six fonts nobody set.
What it looks like
A fragment, with the wrong version kept in a comment where the wrong version is the thing people actually write.
/* One declaration per real face, each declaring what it actually is. */
@font-face {
font-family: "Brand Sans";
src: url("brand-sans-400.woff2") format("woff2");
font-weight: 400; font-style: normal;
}
@font-face {
font-family: "Brand Sans";
src: url("brand-sans-700.woff2") format("woff2");
font-weight: 700; font-style: normal; /* a real bold, not a smear */
}
@font-face {
font-family: "Brand Sans";
src: url("brand-sans-400-italic.woff2") format("woff2");
font-weight: 400; font-style: italic; /* a real italic, not a slant */
}
/* Use only the weights that exist. */
body { font-family: "Brand Sans"; font-weight: 400; }
strong { font-weight: 700; }
em { font-style: italic; }
/* Not this: 600 was never loaded, so the engine adjusts one of the two
it has and the result is nobody's design.
h2 { font-weight: 600; } */What people do instead
Loading one weight and styling with bold and italic freely. It renders, nothing errors, and the document goes out with smeared headings and slanted upright letterforms. The absence of an error message is the whole problem: synthesis is a fallback that never announces itself.
How to find out rather than assume
Render a line of text in the real bold and the same line in a synthesised bold at the same size, side by side, and print them. The synthesised one has closed counters and even stroke weight. Once you have seen the difference at text size you can spot it in any document in about a second.
Frequently asked
Can I upload a font instead of serving one?
No. There is no font upload and no font list. The renderer fetches what your @font-face points at, so the file has to be somewhere it can reach over the network. In practice that means a public URL on your own asset host or a font service, and it means a font on a private path or behind an authenticated endpoint fails to load silently and the fallback is used instead.
How do I know which fonts ended up in the document?
Open the finished file in a reader and look at its document properties, which list the embedded fonts. That list is the truth: it says whether the face you meant was used, whether a fallback was substituted, and how many faces the document is carrying. It is a five second check and it catches most of the failures in this cluster.
Why does the font work in my browser and not in the render?
Almost always because the URL resolves for you and not for the renderer. A relative path, a local file, a development host, or an asset behind a login all work while you are looking at the page and none of them work when a machine somewhere else loads it. The fallback is then used and nothing reports an error, which is why the symptom is a document that looks subtly wrong rather than a render that failed.
Related font topics
The topics that cause each other, then the rest of the same group.
Designing a font fallback chain for a document you cannot see
The list of families after the one you want, which decides what the document looks like on the day the font does not load.
Using a variable font in a paged document, and when not to
One file carrying a continuous range of weights instead of several files carrying fixed ones, which solves a download problem a generated document does not have.
Why two fonts at the same size give different line heights
The ascent, descent and line gap recorded inside the font file, which decide how tall a line is before your stylesheet says anything.
@font-face in paged output
The property behind this decision, with its real support status.
Every font topic
The full list, grouped by supply, engine behaviour, and what happens to the text afterwards.
Fonts reach a render the way every other subresource does, over the network from the URL you named. Render the document and look at what actually got embedded.