About This CSS Issue
font-display controls how a font face is displayed based on whether and when it has been downloaded and is ready to use. Its accepted values — auto, block, swap, fallback, and optional — determine the behavior during the font-loading timeline (block period, swap period, and failure period). Because it governs the loading behavior of a font face definition, it only makes sense within a @font-face rule, not as a property applied to an HTML element.
When you place font-display inside a regular selector like body or .heading, the CSS validator correctly flags it as an unknown property. Browsers will silently ignore it, meaning your intended font-loading strategy won’t take effect. Users may experience a flash of invisible text (FOIT) or other unwanted behavior because the browser falls back to its default font-display strategy.
Why this matters
-
Font-loading performance: Without a valid
font-displaydescriptor in your@font-facerule, you lose control over how the browser handles text rendering while custom fonts load. Usingfont-display: swap, for example, ensures text remains visible with a fallback font until the custom font is ready — a key web performance best practice. -
Standards compliance: The CSS Fonts Module Level 4 specification defines
font-displayexclusively as a@font-facedescriptor. Using it elsewhere produces invalid CSS. - Silent failure: Browsers won’t throw visible errors — they simply ignore the invalid property, which can make it difficult to diagnose why your font-loading behavior isn’t working as expected.
How to fix it
-
Remove
font-displayfrom any regular CSS rule (selectors likebody,h1,.class, etc.). -
Add
font-displayinside the@font-faceat-rule where you define your custom font. -
If you’re loading fonts via a third-party service (like Google Fonts), you can often append a
&display=swapparameter to the font URL instead of writing your own@font-faceblock.
Examples
❌ Incorrect: font-display used as a CSS property
body {
font-family: "Open Sans", sans-serif;
font-display: swap;
}
This triggers the validator error because font-display is not a valid CSS property for element selectors.
✅ Correct: font-display used inside @font-face
@font-face {
font-family: "Open Sans";
src: url("/fonts/open-sans.woff2") format("woff2"),
url("/fonts/open-sans.woff") format("woff");
font-display: swap;
}
body {
font-family: "Open Sans", sans-serif;
}
Here, font-display: swap is correctly placed inside the @font-face rule, telling the browser to immediately render text with a fallback font and then swap in “Open Sans” once it finishes loading.
✅ Correct: Using display=swap with Google Fonts
If you use Google Fonts, you don’t need to write your own @font-face block. Instead, add the display parameter to the URL:
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans&display=swap">
Google Fonts will generate the @font-face rules with font-display: swap included automatically.
❌ Incorrect: font-display in a class selector
.heading {
font-family: "Roboto", sans-serif;
font-display: optional;
}
✅ Correct: Separate the descriptor from the styling rule
@font-face {
font-family: "Roboto";
src: url("/fonts/roboto.woff2") format("woff2");
font-display: optional;
}
.heading {
font-family: "Roboto", sans-serif;
}
The font-display: optional descriptor now correctly lives in the @font-face block, where it tells the browser to use the custom font only if it’s already cached — otherwise, stick with the fallback. The .heading rule simply references the font family by name.
Find issues like this automatically
Rocket Validator scans thousands of pages in seconds, detecting HTML issues across your entire site.