Skip to content

Commit 5678024

Browse files
feat: add font-family-fallbacks rule (eslint#174)
* feat: add font-family-fallback draft * report font-family with no fallback * resolve conflict * update code and add docs * add test * resolve conflicts * update README.md * simplify code
1 parent 32e02d6 commit 5678024

File tree

4 files changed

+831
-14
lines changed

4 files changed

+831
-14
lines changed

README.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,20 +65,21 @@ export default defineConfig([
6565

6666
<!-- Rule Table Start -->
6767

68-
| **Rule Name** | **Description** | **Recommended** |
69-
| :----------------------------------------------------------------------------------- | :-------------------------------------------------- | :-------------: |
70-
| [`no-duplicate-imports`](./docs/rules/no-duplicate-imports.md) | Disallow duplicate @import rules | yes |
71-
| [`no-duplicate-keyframe-selectors`](./docs/rules/no-duplicate-keyframe-selectors.md) | Disallow duplicate selectors within keyframe blocks | yes |
72-
| [`no-empty-blocks`](./docs/rules/no-empty-blocks.md) | Disallow empty blocks | yes |
73-
| [`no-important`](./docs/rules/no-important.md) | Disallow !important flags | yes |
74-
| [`no-invalid-at-rule-placement`](./docs/rules/no-invalid-at-rule-placement.md) | Disallow invalid placement of at-rules | yes |
75-
| [`no-invalid-at-rules`](./docs/rules/no-invalid-at-rules.md) | Disallow invalid at-rules | yes |
76-
| [`no-invalid-named-grid-areas`](./docs/rules/no-invalid-named-grid-areas.md) | Disallow invalid named grid areas | yes |
77-
| [`no-invalid-properties`](./docs/rules/no-invalid-properties.md) | Disallow invalid properties | yes |
78-
| [`prefer-logical-properties`](./docs/rules/prefer-logical-properties.md) | Enforce the use of logical properties | no |
79-
| [`relative-font-units`](./docs/rules/relative-font-units.md) | Enforce the use of relative font units | no |
80-
| [`use-baseline`](./docs/rules/use-baseline.md) | Enforce the use of baseline features | yes |
81-
| [`use-layers`](./docs/rules/use-layers.md) | Require use of layers | no |
68+
| **Rule Name** | **Description** | **Recommended** |
69+
| :----------------------------------------------------------------------------------- | :---------------------------------------------------- | :-------------: |
70+
| [`font-family-fallbacks`](./docs/rules/font-family-fallbacks.md) | Enforce use of fallback fonts and a generic font last | yes |
71+
| [`no-duplicate-imports`](./docs/rules/no-duplicate-imports.md) | Disallow duplicate @import rules | yes |
72+
| [`no-duplicate-keyframe-selectors`](./docs/rules/no-duplicate-keyframe-selectors.md) | Disallow duplicate selectors within keyframe blocks | yes |
73+
| [`no-empty-blocks`](./docs/rules/no-empty-blocks.md) | Disallow empty blocks | yes |
74+
| [`no-important`](./docs/rules/no-important.md) | Disallow !important flags | yes |
75+
| [`no-invalid-at-rule-placement`](./docs/rules/no-invalid-at-rule-placement.md) | Disallow invalid placement of at-rules | yes |
76+
| [`no-invalid-at-rules`](./docs/rules/no-invalid-at-rules.md) | Disallow invalid at-rules | yes |
77+
| [`no-invalid-named-grid-areas`](./docs/rules/no-invalid-named-grid-areas.md) | Disallow invalid named grid areas | yes |
78+
| [`no-invalid-properties`](./docs/rules/no-invalid-properties.md) | Disallow invalid properties | yes |
79+
| [`prefer-logical-properties`](./docs/rules/prefer-logical-properties.md) | Enforce the use of logical properties | no |
80+
| [`relative-font-units`](./docs/rules/relative-font-units.md) | Enforce the use of relative font units | no |
81+
| [`use-baseline`](./docs/rules/use-baseline.md) | Enforce the use of baseline features | yes |
82+
| [`use-layers`](./docs/rules/use-layers.md) | Require use of layers | no |
8283

8384
<!-- Rule Table End -->
8485

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# font-family-fallbacks
2+
3+
Enforce the use of fallback fonts and a generic font last.
4+
5+
## Background
6+
7+
The `font-family` property in CSS is used to specify which fonts should be used to display text. One can specify one or a list of fonts in `font-family`.
8+
9+
The browser checks if the first font is available to the user's system, if the first font is not available, it tries the next font in the list. This process continues until a font is found. If none of the specified fonts are available, the browser uses the generic font family (like `serif`, `sans-serif`, etc.) at the end of the list.
10+
11+
It is a best practice to use a fallback font and a generic font last in the `font-family` list to ensure that text is always displayed in readable and consistent way, even if the preferred fonts are not available on the user's system or if the browser doesn't support the preferred font.
12+
13+
## Rule Details
14+
15+
This rule enforces the use of fallback fonts and a generic font last in `font-family` and `font` property.
16+
17+
Example of **incorrect** code:
18+
19+
```css
20+
/* eslint css/font-family-fallbacks: "error" */
21+
22+
:root {
23+
--my-font: "Open Sans";
24+
}
25+
26+
a {
27+
font-family: Arial;
28+
}
29+
30+
b {
31+
font-family: Verdana, Arial, Helvetica;
32+
}
33+
34+
.foo {
35+
font-family: var(--my-font);
36+
}
37+
```
38+
39+
Example of **correct** code:
40+
41+
```css
42+
/* eslint css/font-family-fallbacks: "error" */
43+
44+
:root {
45+
--my-font: "Open Sans", Arial, sans-serif;
46+
}
47+
48+
a {
49+
font-family: Verdana, Arial, Helvetica, sans-serif;
50+
}
51+
52+
b {
53+
font-family: serif;
54+
}
55+
56+
.foo {
57+
font-family: var(--my-font);
58+
}
59+
```
60+
61+
Fonts can also be specified using the `font` property, which acts as a shorthand for several font-related properties, including `font-family`. You must specify both the `font-size` and `font-family` when using `font` property.
62+
63+
Example of **incorrect** code:
64+
65+
```css
66+
/* eslint css/font-family-fallbacks: "error" */
67+
68+
:root {
69+
--my-font: "Times New Roman";
70+
}
71+
72+
a {
73+
font: 16px Arial;
74+
}
75+
76+
b {
77+
font:
78+
italic bold 2em "Open Sans",
79+
Helvetica;
80+
}
81+
82+
.foo {
83+
font: italic bold 2em var(--my-font);
84+
}
85+
```
86+
87+
Example of **correct** code:
88+
89+
```css
90+
/* eslint css/font-family-fallbacks: "error" */
91+
92+
:root {
93+
--my-font: "Times New Roman", Arial, serif;
94+
--font: "Open Sans", Helvetica;
95+
}
96+
97+
a {
98+
font:
99+
16px Arial,
100+
sans-serif;
101+
}
102+
103+
b {
104+
font:
105+
italic bold 2em "Open Sans",
106+
Helvetica,
107+
sans-serif;
108+
}
109+
110+
.foo {
111+
font: normal 2em var(--my-font);
112+
}
113+
114+
.bar {
115+
font:
116+
1em var(--font),
117+
monospace;
118+
}
119+
```
120+
121+
## When Not to Use It
122+
123+
If you are confident that the font will always load and render as expected, then you can safely disable this rule.
124+
125+
## Prior Art
126+
127+
- [`font-family-no-missing-generic-family-keyword`](https://stylelint.io/user-guide/rules/font-family-no-missing-generic-family-keyword/)

0 commit comments

Comments
 (0)