require-font-face-local-src-before-remote
Require local() sources to appear before url() sources in @font-face src declarations.
Targeted pattern scopeโ
This rule inspects src declarations inside @font-face blocks and reports two problems:
- A
srcthat containsurl()sources but nolocal()fallback at all. - A
srcwhere the firsturl()appears before the firstlocal()(wrong ordering).
The rule only applies when a url() source is present. A @font-face with only local() sources passes.
What this rule reportsโ
@font-facewhosesrccontains at least oneurl()but nolocal()โ missing local fallback.@font-facewhosesrchas bothlocal()andurl()but theurl()appears first โ wrong ordering.
Why this rule existsโ
local() sources tell browsers to prefer any copy of the font already installed on the operating system before downloading a remote copy. Placing local() first โ and ensuring it exists โ yields two concrete benefits:
- Zero-latency font rendering for users who already have the font installed (common for web-safe fonts, system fonts, and popular CDN fonts).
- Reduced bandwidth and CLS because no network request is needed in the local-hit case.
Docusaurus sites commonly add @font-face blocks for icon fonts (Nerd Fonts, Material Icons) and branding fonts. If a new @font-face is added with only a CDN url() and no local(), users who have that font installed waste a network round-trip on every page load.
โ Incorrectโ
/* No local() fallback โ always downloads from CDN */
@font-face {
font-family: "NerdFont";
font-display: swap;
src: url("/fonts/nerdfont.woff2") format("woff2");
}
/* url() appears before local() โ browser downloads before checking locally */
@font-face {
font-family: "MyFont";
font-display: swap;
src:
url("/fonts/myfont.woff2") format("woff2"),
local("MyFont");
}
โ Correctโ
/* Correct: local() before url() */
@font-face {
font-family: "MyFont";
font-display: swap;
src:
local("MyFont"),
local("MyFont-Regular"),
url("/fonts/myfont.woff2") format("woff2");
}
/* Correct: only local() sources โ no url() present */
@font-face {
font-family: "SystemFallback";
src: local("Arial"), local("Helvetica Neue"), local("Helvetica");
}
Behavior and migration notesโ
- The rule reports on the
@font-faceat-rule node itself. - Detection uses lightweight string matching on the
srcvalue โ it does not parse the full CSS value AST, so comma-separated multi-line values are checked as a concatenated string. - This rule is report-only; no autofix reorders or inserts
local()sources because the correct local font name depends on the font family. - A
@font-facewith nosrcdeclaration passes silently (the CSS is invalid for other reasons).
Additional examplesโ
โ Correct โ multiple url() formats with local() firstโ
@font-face {
font-family: "OpenSans";
font-display: swap;
src:
local("Open Sans"),
local("OpenSans"),
url("/fonts/open-sans.woff2") format("woff2"),
url("/fonts/open-sans.woff") format("woff");
}
โ Incorrect โ mixed formats with url() firstโ
@font-face {
font-family: "OpenSans";
font-display: swap;
src:
url("/fonts/open-sans.woff2") format("woff2"),
local("Open Sans");
}
Stylelint config exampleโ
import { docusaurusPluginConfigs } from "stylelint-plugin-docusaurus";
export default {
plugins: ["stylelint-plugin-docusaurus"],
rules: {
"docusaurus/require-font-face-local-src-before-remote": true
}
};
When not to use itโ
Disable this rule for icon fonts (such as Material Icons or Nerd Fonts) where the local system version may have a different glyph set or version than the one your site requires. In those cases a local() fallback might produce incorrect icons if the locally installed version differs from the expected one.
Further readingโ
- MDN: @font-face src
- web.dev: Best practices for fonts โ local() sources
require-font-display-on-font-facerule โ companion rule for font-loading behavior
Rule catalog ID: R030