Linter Rules and Sources

Purpose and Scope

This page explains how to read Biome’s linter as both a user-facing rule catalog and a source-backed analyzer implementation. In the public documentation, linter rules are organized by language and group, with rule pages describing diagnostic categories, severity, recommendation status, fix availability, configuration examples, and options. In the repository evidence for this page, the most concrete implementation slice is the CSS analyzer crate, which exposes the analyzer entry points, rule metadata registration, service dependencies, plugin parameters, suppression integration, and the syntax coverage that CSS rules can inspect.

Sources: crates/biome_css_analyze/src/lib.rs

The most important term is analyzer. In Biome, a linter rule is executed by an analyzer over a parsed language root, not by a standalone text scanner. The CSS analyzer API accepts a LanguageRoot<CssLanguage>, an AnalysisFilter, analyzer options, CSS-specific services, plugin definitions, and a callback that receives analyzer signals. Those signals are the point where diagnostics and actions leave the rule engine and become observable by higher layers such as CLI reporters, editor integrations, or code-action machinery.

Sources: crates/biome_css_analyze/src/lib.rs

The official CSS rules documentation groups rules into categories such as a11y, complexity, correctness, nursery, and style. Examples include useGenericFontNames, noImportantStyles, noInvalidGridAreas, noUnknownProperty, noUnusedClasses, useBaseline, and noDescendingSpecificity. Those documentation groups are reader-facing, while the source-backed analyzer API shows the execution contract that makes any group possible: register metadata, analyze a CSS syntax tree, optionally use semantic and project services, and emit analyzer signals for diagnostics or actions.

Sources: crates/biome_css_analyze/src/lib.rs, crates/biome_css_analyze/src/services/mod.rs

Relevant Source Files

  • crates/biome_css_analyze/src/lib.rs - Defines the CSS analyzer crate surface, imports analyzer framework types, exposes visit_registry, builds METADATA, defines CssAnalyzerServices, and provides analyze plus analyze_with_inspect_matcher.
  • crates/biome_css_analyze/src/services/mod.rs - Declares CSS analyzer service modules for module_graph and semantic, matching the optional service fields exposed by CssAnalyzerServices.
  • crates/biome_css_factory/src/lib.rs - Exposes CssSyntaxFactory, CssSyntaxTreeBuilder, the generated node_factory as make, and a hidden syntax re-export used by tests that need CSS syntax construction.
  • crates/biome_css_formatter/src/css/any/mod.rs - Lists generated formatter modules for many CSS syntax union-like node families, showing the breadth of concrete CSS structures available across the syntax tree.
  • crates/biome_css_formatter/src/css/auxiliary/mod.rs - Lists generated formatter modules for auxiliary CSS nodes such as declarations, media conditions, container queries, import layers, functions, and nested qualified rules.
  • crates/biome_css_formatter/src/css/bogus/mod.rs - Lists generated formatter modules for bogus and unknown CSS nodes, which are important because analysis and formatting must tolerate malformed or partially parsed code.

System-to-Code Mapping

At the top of the CSS analyzer crate, the module list gives a compact architecture map: assist, baseline_data, fonts, keywords, lint, order, registry, services, suppression_action, and utils. That split is useful when navigating linter behavior. Rule definitions live behind the lint and registry layers, while shared knowledge such as CSS keywords, font handling, Baseline data, and ordering utilities can support multiple diagnostics. Suppression behavior is separated into its own action module, reinforcing that disabling diagnostics and producing suppression actions are analyzer concerns rather than formatter concerns.

Sources: crates/biome_css_analyze/src/lib.rs

The registry is the bridge between rule implementation and documentation-like metadata. The CSS analyzer publicly re-exports visit_registry, and the static METADATA value builds a MetadataRegistry by calling that registry visitor. A rule page such as noEmptySource in the official docs names a diagnostic category like lint/suspicious/noEmptySource, describes default severity, and states whether the rule is recommended or fixable. The source evidence here does not enumerate every rule, but it shows the mechanism that gathers rule metadata into a registry for consumers that need a coherent catalog.

Sources: crates/biome_css_analyze/src/lib.rs

CSS analyzer services are explicit inputs rather than hidden globals. CssAnalyzerServices can carry a CSS semantic model, a CssFileSource, a module database, and a project layout. Builder-style methods such as with_file_source, with_semantic_model, with_module_db, and with_project_layout let the caller construct only the context needed for a given analysis run. This design matters for rules such as class-usage checks or Baseline-related checks, where project context, module graph information, or source kind can affect what the rule can soundly report.

Sources: crates/biome_css_analyze/src/lib.rs, crates/biome_css_analyze/src/services/mod.rs

The services module reinforces that CSS linting is not limited to local syntax matching. Its two declared service areas are module_graph and semantic. A semantic service can support rules that need resolved meaning rather than raw tokens, while a module graph service can support rules that relate one file to another. The official CSS docs list noUnusedClasses as a nursery rule that reports CSS class selectors never referenced in JSX or HTML; this is the kind of user-facing rule that explains why an analyzer service layer has room for project and module information.

Sources: crates/biome_css_analyze/src/lib.rs, crates/biome_css_analyze/src/services/mod.rs

Rule Execution Flow

The public analyze function is the normal entry point for CSS lint analysis. It receives the parsed CSS root, an analysis filter, analyzer options, services, plugin slice, and an emit_signal callback. It returns a pair containing an optional control-flow result and a list of diagnostic errors. The comments in the source describe the filter as a way to restrict analysis to specific rules or a specific source range, then call the signal emitter when a rule emits a diagnostic or action.

Sources: crates/biome_css_analyze/src/lib.rs

For tools that need to observe rule matching more closely, analyze_with_inspect_matcher adds an inspect_matcher callback. Its documentation says the callback can inspect query matches emitted by the analyzer before they are processed by the lint rules registry. That is a contributor-oriented extension point: it exposes the boundary between syntax-query matching and rule execution without changing the public rule result contract. It also explains why the analyzer imports MatchQueryParams<CssLanguage> alongside signal, filter, and registry types.

Sources: crates/biome_css_analyze/src/lib.rs

The analyzer imports suppression-related types from both biome_analyze and biome_suppression, including AnalyzerSuppression, to_analyzer_suppressions, SuppressionDiagnostic, and parse_suppression_comment, and it imports a CSS-specific CssSuppressionAction. The user-facing effect is that suppression comments and suppression actions are part of the same analyzer pipeline as diagnostics. A linter rule can therefore be reported, filtered, or accompanied by an action according to the analysis framework rather than by ad hoc string matching in each rule.

Sources: crates/biome_css_analyze/src/lib.rs

Plugin support is visible in the analyzer signature through AnalyzerPluginSlice<'a> and in imported framework names such as BatchPluginVisitor and PluginTargetLanguage. The supplied evidence does not define plugin authoring APIs, but it does show that CSS analysis is designed to receive plugin data as part of the run. This is the right layer for plugin-related analyzer extension points because plugins need access to the language root, filter, options, and signal emission path in the same phase as built-in analyzer rules.

Sources: crates/biome_css_analyze/src/lib.rs

CSS Syntax Coverage and Rule Sources

Rules can only be precise if the parser and tree model preserve the structures they need to inspect. The CSS factory crate exposes a generated CssSyntaxFactory, a CssSyntaxTreeBuilder specialized for CssLanguage, and a generated node factory re-exported as make. It also re-exports CSS syntax under a hidden test-oriented name. That combination is a testing signal: rule tests and analyzer fixtures can construct or inspect CSS syntax consistently with the generated parser and tree infrastructure instead of inventing separate node builders.

Sources: crates/biome_css_factory/src/lib.rs

The formatter module lists are relevant to linter contributors because they enumerate the concrete CSS syntax surface shared across Biome’s CSS tooling. The generated any formatter modules include structures such as at-rules, declarations, functions, keyframes, media queries, pseudo-classes, pseudo-elements, selectors, supports conditions, and unknown at-rule names. Even though these files are formatter code, they reveal that the syntax model is broad enough for lint rules covering selector validity, media feature names, unknown functions, invalid grid areas, and import placement.

Sources: crates/biome_css_formatter/src/css/any/mod.rs

Auxiliary formatter modules show another level of CSS grammar detail: declaration blocks, declaration_important, empty declarations, import layers, media conditions, container queries, function parameters, nested qualified rules, namespaces, and query feature ranges. These names align with the kinds of rule sources documented publicly, including rules inspired by Stylelint and @eslint/css. When a rule source mapping says Biome implements declaration-no-important as noImportantStyles, the analyzer still depends on Biome’s own syntax vocabulary for declarations and important markers.

Sources: crates/biome_css_formatter/src/css/auxiliary/mod.rs

Bogus syntax modules are also part of rule quality. The generated bogus formatter modules cover malformed at-rules, blocks, functions, properties, pseudo-classes, pseudo-elements, selectors, supports conditions, unicode ranges, URL modifiers, and generic value-at-rule content. Lint analysis should avoid crashing or producing misleading reports when a file is incomplete in an editor or contains syntax errors. A syntax tree that represents bogus nodes gives rules and formatters a shared way to tolerate partial code while diagnostics guide the user toward a valid source.

Sources: crates/biome_css_formatter/src/css/bogus/mod.rs

Compact Reference

ComponentSource-level contractWhy it matters for linter users and contributors
visit_registryPublicly re-exported from the CSS analyzer registry moduleAllows metadata collection for the CSS rule catalog and rule discovery.
METADATAStatic LazyLock<MetadataRegistry> populated by visit_registryCentralizes rule metadata used by tooling that needs the available rules.
CssAnalyzerServicesHolds optional semantic model, file source, optional module database, and optional project layoutSupplies context-sensitive rules without requiring every run to have every service.
analyzeRuns analysis for a CSS root with filter, options, services, plugins, and signal callbackMain entry point for diagnostics and rule actions.
analyze_with_inspect_matcherAdds a callback for inspecting analyzer query matches before registry processingUseful for debugging, tests, and analyzer internals.
CssSyntaxTreeBuilderTreeBuilder specialized for CssLanguage and CssSyntaxFactorySupports generated CSS syntax construction in tests and tooling.

Sources: crates/biome_css_analyze/src/lib.rs, crates/biome_css_factory/src/lib.rs

Practical Reading Path

If you are configuring Biome as a user, start from the official rule page for the diagnostic you want to enable, disable, or tune. A page like noEmptySource shows the category path, severity, recommendation status, fix status, and JSON configuration shape. Then use the CSS rule list and rule-source mapping to understand whether a rule is Biome-exclusive, inspired by Stylelint, aligned with @eslint/css, or related to another ecosystem. That context helps teams migrate rule names without assuming exact behavioral identity.

Sources: crates/biome_css_analyze/src/lib.rs

If you are contributing a CSS rule, begin with the analyzer crate rather than the formatter crate. Use crates/biome_css_analyze/src/lib.rs to understand the run signature, services, metadata registration, suppression plumbing, plugin parameter, and signal emission model. Then use the factory and generated syntax module names to identify the CSS nodes your rule needs to match. The generated formatter files should not be edited by hand, but their module names are a useful map of the syntax concepts available to rule implementations and tests.

Sources: crates/biome_css_analyze/src/lib.rs, crates/biome_css_factory/src/lib.rs, crates/biome_css_formatter/src/css/any/mod.rs, crates/biome_css_formatter/src/css/auxiliary/mod.rs, crates/biome_css_formatter/src/css/bogus/mod.rs