diff --git a/.changeset/fix-use-generic-font-names-supports.md b/.changeset/fix-use-generic-font-names-supports.md new file mode 100644 index 000000000000..84fbebbf20b4 --- /dev/null +++ b/.changeset/fix-use-generic-font-names-supports.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fixed [#8845](https://github.com/biomejs/biome/issues/8845): [`useGenericFontNames`](https://biomejs.dev/linter/rules/use-generic-font-names/) no longer reports a false positive when `font` or `font-family` is used inside `@supports` rules for feature detection. diff --git a/crates/biome_css_analyze/src/lint/a11y/use_generic_font_names.rs b/crates/biome_css_analyze/src/lint/a11y/use_generic_font_names.rs index 45b270864de8..5827f9c74a6d 100644 --- a/crates/biome_css_analyze/src/lint/a11y/use_generic_font_names.rs +++ b/crates/biome_css_analyze/src/lint/a11y/use_generic_font_names.rs @@ -27,6 +27,7 @@ declare_lint_rule! { /// - Property with a keyword value such as `inherit`, `initial`. /// - The last value being a CSS variable. /// - `font-family` property in an `@font-face` rule. + /// - `font` or `font-family` property in an `@supports` rule (feature detection). /// /// ## Examples /// @@ -62,6 +63,10 @@ declare_lint_rule! { /// @font-face { font-family: Gentium; } /// ``` /// + /// ```css + /// @supports (font: -apple-system-body) {} + /// ``` + /// pub UseGenericFontNames { version: "1.8.0", name: "useGenericFontNames", @@ -88,6 +93,12 @@ impl Rule for UseGenericFontNames { return None; } + // Ignore `@supports`. The font property inside @supports is used for feature detection, + // not as an actual font-family declaration. + if is_in_supports_at_rule(node) { + return None; + } + let is_font_family = property_name == "font-family"; let is_font = property_name == "font"; @@ -158,6 +169,18 @@ fn is_in_font_face_at_rule(node: &CssGenericProperty) -> bool { .is_some_and(|n| matches!(n, AnyCssAtRule::CssFontFaceAtRule(_))) } +fn is_in_supports_at_rule(node: &CssGenericProperty) -> bool { + // Check all ancestors, not just the first at-rule, because @supports may + // wrap other at-rules like @media. For example: + // @supports (font: -apple-system-body) { @media screen { font: ... } } + node.syntax().ancestors().any(|n| { + n.kind() == CssSyntaxKind::CSS_AT_RULE + && n.cast::() + .and_then(|n| n.rule().ok()) + .is_some_and(|n| matches!(n, AnyCssAtRule::CssSupportsAtRule(_))) + }) +} + fn is_shorthand_font_property_with_keyword(properties: &CssGenericComponentValueList) -> bool { properties.into_iter().len() == 1 && properties diff --git a/crates/biome_css_analyze/tests/specs/a11y/useGenericFontNames/valid.css b/crates/biome_css_analyze/tests/specs/a11y/useGenericFontNames/valid.css index 697ad9499b43..19264f958cdd 100644 --- a/crates/biome_css_analyze/tests/specs/a11y/useGenericFontNames/valid.css +++ b/crates/biome_css_analyze/tests/specs/a11y/useGenericFontNames/valid.css @@ -27,4 +27,8 @@ a { font-family: Arial, var(--fallback); } a { font: 1em "Noto Serif", var(--serif); } a { font: 14px/1.5 Arial, var(--fallback); } a { font-family: revert } -a { font-family: revert-layer } \ No newline at end of file +a { font-family: revert-layer } +@supports (font: -apple-system-body) {} +@supports (font-family: -apple-system) {} +/* Nested at-rules inside @supports should also be ignored */ +@supports (font: -apple-system-body) { @media screen { a { font-family: -apple-system; } } } \ No newline at end of file diff --git a/crates/biome_css_analyze/tests/specs/a11y/useGenericFontNames/valid.css.snap b/crates/biome_css_analyze/tests/specs/a11y/useGenericFontNames/valid.css.snap index 28ef235ae5ce..e5a1032e3f82 100644 --- a/crates/biome_css_analyze/tests/specs/a11y/useGenericFontNames/valid.css.snap +++ b/crates/biome_css_analyze/tests/specs/a11y/useGenericFontNames/valid.css.snap @@ -34,4 +34,8 @@ a { font: 1em "Noto Serif", var(--serif); } a { font: 14px/1.5 Arial, var(--fallback); } a { font-family: revert } a { font-family: revert-layer } +@supports (font: -apple-system-body) {} +@supports (font-family: -apple-system) {} +/* Nested at-rules inside @supports should also be ignored */ +@supports (font: -apple-system-body) { @media screen { a { font-family: -apple-system; } } } ```