diff --git a/docs/rules/font-family-fallbacks.md b/docs/rules/font-family-fallbacks.md index a286da21..a59abc32 100644 --- a/docs/rules/font-family-fallbacks.md +++ b/docs/rules/font-family-fallbacks.md @@ -12,7 +12,7 @@ It is a best practice to use a fallback font and a generic font last in the `fon ## Rule Details -This rule enforces the use of fallback fonts and a generic font last in `font-family` and `font` property. +This rule enforces the use of fallback fonts and a generic font last in `font-family` and `font` property. Global values (`inherit`, `initial`, `revert`, `revert-layer`, `unset`) are always allowed. Example of **incorrect** code: @@ -56,9 +56,13 @@ b { .foo { font-family: var(--my-font); } + +.bar { + font-family: inherit; +} ``` -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. +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, unless you are using a CSS-wide keyword. Example of **incorrect** code: @@ -116,6 +120,10 @@ b { 1em var(--font), monospace; } + +.baz { + font: unset; +} ``` ## When Not to Use It diff --git a/src/rules/font-family-fallbacks.js b/src/rules/font-family-fallbacks.js index e3cc6a8b..30469640 100644 --- a/src/rules/font-family-fallbacks.js +++ b/src/rules/font-family-fallbacks.js @@ -33,6 +33,32 @@ const genericFonts = new Set([ "fangsong", ]); +const cssWideKeywords = new Set([ + "inherit", + "initial", + "unset", + "revert", + "revert-layer", +]); + +/** + * Check if the value is a CSS-wide keyword. + * @param {string} value The value to check. + * @returns {boolean} True if the value is a CSS-wide keyword, false otherwise. + */ +function isCSSWideKeyword(value) { + return cssWideKeywords.has(value.trim().toLowerCase()); +} + +/** + * Check if the node is an identifier with a CSS-wide keyword. + * @param {Object} node The node to check. + * @returns {boolean} True if the node is a CSS-wide keyword identifier, false otherwise. + */ +function isCSSWideKeywordIdentifier(node) { + return node.type === "Identifier" && isCSSWideKeyword(node.name); +} + /** * Check if the node is a CSS variable function. * @param {Object} node The node to check. @@ -55,6 +81,10 @@ function reportFontWithoutFallbacksInFontProperty( context, node, ) { + if (isCSSWideKeyword(fontPropertyValues)) { + return; + } + const valueList = fontPropertyValues.split(",").map(v => v.trim()); if (valueList.length === 1) { @@ -120,6 +150,10 @@ export default { const valueArr = node.children; if (valueArr.length === 1) { + if (isCSSWideKeywordIdentifier(valueArr[0])) { + return; + } + if ( valueArr[0].type === "Function" && valueArr[0].name === "var" @@ -133,6 +167,10 @@ export default { return; } + if (isCSSWideKeyword(variableValue)) { + return; + } + const variableList = variableValue .split(",") .map(v => v.trim()); diff --git a/tests/rules/font-family-fallbacks.test.js b/tests/rules/font-family-fallbacks.test.js index 34c5471c..7e021e0e 100644 --- a/tests/rules/font-family-fallbacks.test.js +++ b/tests/rules/font-family-fallbacks.test.js @@ -10,8 +10,20 @@ const ruleTester = new RuleTester({ plugins: { css }, language: "css/css" }); ruleTester.run("font-family-fallbacks", rule, { valid: [ + "a { font-family: inherit; }", + "a { font-family: initial; }", + "a { font-family: revert; }", + "a { font-family: revert-layer; }", + "a { font-family: unset; }", + "a { font: inherit; }", + "a { font: initial; }", + "a { font: revert; }", + "a { font: revert-layer; }", + "a { font: unset; }", ":root { --my-font: sans-serif; } a { font-family: var(--my-font); }", ":root { --foo: 3rem; } a { font-family: var(--my-font); }", + ":root { --my-font: inherit; } a { font-family: var(--my-font); }", + ":root { --my-font-value: inherit; } a { font: var(--my-font-value); }", ":root { --my-font: 'Arial', sans-serif; } a { font-family: var(--my-font); }", ":root { --my-font: 'Arial', 'Segoe UI Emoji', serif; } a { font-family: var(--my-font); }", "a { font-family: serif; }",