require-font-display-on-font-face
Require a font-display declaration in every @font-face block.
Targeted pattern scopeโ
This rule inspects every @font-face at-rule block and checks two things:
- A
font-displaydeclaration must be present. - When
allowedValuesis configured, thefont-displayvalue must be one of the allowed keywords.
The default allowed values are block, fallback, optional, and swap. The auto keyword is excluded by default because its behavior is browser-defined and produces inconsistent results across rendering engines.
What this rule reportsโ
- An
@font-faceblock without anyfont-displaydeclaration. - An
@font-faceblock whosefont-displayvalue is not in the configuredallowedValueslist.
Why this rule existsโ
The font-display property controls how a browser behaves while a web font loads. Without it, browsers fall back to browser-default behavior โ typically either a Flash of Invisible Text (FOIT, where text is invisible while the font loads) or a Flash of Unstyled Text (FOUT, where text renders in a fallback font briefly). Both outcomes hurt Cumulative Layout Shift (CLS) and Largest Contentful Paint (LCP) scores.
Docusaurus sites frequently load fonts from CDN sources such as Google Fonts or Nerd Fonts. Any @font-face added to a Docusaurus custom stylesheet should declare font-display to control that loading behavior explicitly and ensure consistent performance.
โ Incorrectโ
/* Missing font-display */
@font-face {
font-family: "MyFont";
src: local("MyFont"), url("/fonts/myfont.woff2") format("woff2");
}
/* font-display: auto is not in the default allowed list */
@font-face {
font-family: "NerdFont";
font-display: auto;
src: url("/fonts/nerdfont.woff2") format("woff2");
}
โ Correctโ
/* Correct: font-display: swap declared */
@font-face {
font-family: "MyFont";
font-display: swap;
src: local("MyFont"), url("/fonts/myfont.woff2") format("woff2");
}
/* Correct: font-display: optional for a non-critical decorative font */
@font-face {
font-family: "DecorativeFont";
font-display: optional;
src: url("/fonts/decorative.woff2") format("woff2");
}
Behavior and migration notesโ
- The rule reports on the
@font-faceat-rule node itself rather than the individual declaration, so the error points to the block opening. - The
font-displayvalue comparison is case-insensitive. - This rule is report-only; no autofix inserts a
font-displaydeclaration. - When
allowedValuesis provided in secondary options, only the specified values pass. All otherfont-displayvalues are reported.
Additional examplesโ
Allowing only swap and optionalโ
// stylelint.config.mjs
export default {
rules: {
"docusaurus/require-font-display-on-font-face": [
true,
{ allowedValues: ["swap", "optional"] }
]
}
};
โ
Correct โ font-display: fallbackโ
@font-face {
font-family: "SystemFont";
font-display: fallback;
src: local("Arial"), url("/fonts/arial.woff2") format("woff2");
}
Stylelint config exampleโ
import { docusaurusPluginConfigs } from "stylelint-plugin-docusaurus";
export default {
plugins: ["stylelint-plugin-docusaurus"],
rules: {
"docusaurus/require-font-display-on-font-face": true
}
};
When not to use itโ
Disable this rule if your project does not load any custom web fonts, or if your font-loading pipeline injects font-display automatically at build time and the declarations are not present in the authored CSS.
Further readingโ
- MDN: font-display
- web.dev: Optimize WebFont loading and rendering
- web.dev: font-display best practices
- Docusaurus: Custom fonts
Rule catalog ID: R029