The Bold Button Is Faking It

Ask a font for a bold weight it never shipped, and the browser fakes one rather than refuse. What synthetic bold actually does to your letterforms, and the CSS line that catches it before export.

The words "It's not bold." in heavy pale type over a close-up photo of carved wooden letterpress printing blocks bearing Chinese characters, warm brown and charcoal tones under a dark overlay with a fine grain, above the smaller line "Most fonts never had one.", the featured image for this post. Try this template

Click bold, or set font-weight: 700 in CSS, and it never fails - the text always gets heavier, even when the font family never drew a bold face at all. That is not a bug: it is the browser faking one, a documented fallback the CSS spec calls out by name and ships on by default almost everywhere. This post covers what that fake actually is, a debate CSS engineers were still having this year, and a bug it caused in Lede until writing this sent us looking for it.

What “bold” actually asks for

A font family is a set of files, each one a face at a specific weight, and a browser can only draw a face that exists. When your CSS asks for a weight nobody drew - a family that shipped Regular and nothing else, asked to render at 700 - something still has to appear on screen. The CSS Fonts Module Level 4 spec is candid about what happens: for the purposes of matching a font, a missing bold face is still “treated as if it exists within the family,” and a section called Controlling synthetic faces spells out the mechanism -

Synthetic font faces are a fallback strategy to provide an approximation to the requested font when a font family lacks a suitable face. For example, a user agent might synthesize a bold face by drawing a thin stroke around each glyph… Synthesis is a fallback strategy because the end result, while perhaps better than nothing at all, does not meet the level of quality of a face designed for the purpose.

The same document adds, a few sections later, the line that gives this post its title: “Although the practice is not well-loved by typographers, bold faces are often synthesized by user agents for families that lack actual bold faces.” The spec is not hiding the trade-off. It is naming it and shipping it anyway, because a page with no bold at all is judged worse than a page with a fake one.

It is on by default, and there are two versions of “off”

The property that controls this is font-synthesis-weight, and its initial value is auto - synthesis allowed. To stop it, you have to say so:

h1 {
  font-synthesis-weight: none;
}

There’s a wrinkle in the support numbers: font-synthesis-weight is the newer, granular longhand; the older font-synthesis shorthand (which also toggles synthetic italics and small-caps in one property) has been around far longer.

PropertyChromeFirefoxSafari
font-synthesis (shorthand)97349
font-synthesis-weight (longhand)9711116.4

If you tested this before Firefox 111 or Safari 16.4 existed, “not supported” was the granular property’s fault, not the feature’s. All three ship it today.

Variable fonts were supposed to dodge this - browsers were still arguing about it this year

Variable fonts get an explicit exemption: a single file with a weight axis is not “missing” a bold the way a static font is, so the CSS Fonts 4 text says plainly that “variable fonts… do not count as font synthesis.” Ask one for a weight outside its range and it should clamp to the nearest weight its own axis supports - not synthesize at all.

That distinction sounds settled on paper. It was not settled in shipping browsers. CSSWG issue 7999 sat dormant for two years before reopening in March 2025, when Chromium’s Dominik Röttsches confirmed the spec wording still left room to read synthesis as “needed” whenever a requested weight sat outside a variable font’s range - exactly the ambiguity the issue existed to close.

The fix landed in WebKit in June 2026 - and took two tries. The first patch, “Avoid synthesis outside of variable range”, stopped synthesis on variable fonts entirely; six days later, a second patch fixed the regression it caused, since some faces still declare a specific static weight via @font-face and do need synthesis. Two passes to get the boundary right tells you this is genuinely subtle, not a simple on/off switch.

Catch it before it ships

font-synthesis-weight: none is not a permanent fix - it is a smoke test. Turn it off while picking fonts, and any face without a real bold renders at whatever weight it actually has instead of faking one - the same moment thin type at small size gets caught, just for a different reason. The trap is narrowest on thin, editorial, single-weight serifs and single-cut display faces, especially in a display-plus-workhorse pairing: check the display half for a real bold before you ever set it to one, and swap the family - or accept the real weight and size around it - rather than ship a synthetic guess and find out later.

We had this exact bug

Lede’s title text has always rendered at a fixed weight - 600, semibold - regardless of which font is selected. Four of the five fonts bundled with the editor have a genuine 600 face and render exactly as intended. The fifth, Instrument Serif, ships only Regular upstream - no bold cut exists at all, on Lede or anywhere else that uses it - so every cover title set in Instrument Serif was quietly getting a browser-faked 600 baked straight into the exported PNG. Nobody had reported it as a bug; it just looked a little heavier and a little softer in the counters than the rest of the type on the page, exactly the “does not meet the level of quality” the spec warns about, and easy to miss unless you were comparing it side by side with a font that has a real bold.

Writing this post surfaced it. The fix, live as of this release: before drawing the title, Lede now checks the selected font’s real face descriptors and only asks for 600 when one genuinely exists, falling back to the font’s real weight otherwise. The same check covers the wider font-search catalog too, not just the five bundled fonts - several of those, like Anton and Bebas Neue, never had a 600 either. None of them fake it now.

Make it in Lede

Pick any font in Lede’s picker and the title weight is now guaranteed real - the editor checks before it draws, so a font without a true bold renders honestly instead of baking a synthetic guess into your export. Open the editor and try Instrument Serif for a title; it now sits at its real Regular weight, on purpose.

The next time a “bold” heading looks a little off, do not blame your eyes. Turn off the synthesis for a second and look at what is actually there - the font is either telling you the truth, or it never had a bold to begin with.