Skip to content

feat(html/a11y): port noAriaUnsupportedElements to HTML#9491

Merged
ematipico merged 5 commits intobiomejs:nextfrom
IxxyDev:feat/html-no-aria-unsupported-elements
Apr 6, 2026
Merged

feat(html/a11y): port noAriaUnsupportedElements to HTML#9491
ematipico merged 5 commits intobiomejs:nextfrom
IxxyDev:feat/html-no-aria-unsupported-elements

Conversation

@IxxyDev
Copy link
Copy Markdown

@IxxyDev IxxyDev commented Mar 15, 2026

This PR was written primarily by Claude Code.

Summary

Ported the noAriaUnsupportedElements lint rule from JSX to HTML.

  • Biome now detects role and aria-* attributes on HTML elements that do not support ARIA: <meta>, <html>, <script>, and <style>.
  • Provides an unsafe fix to remove the offending attribute.
  • Added edge-case tests for multiple ARIA attributes on a single element and invalid aria-* attributes.

Test Plan

  • cargo test -p biome_html_analyze -- no_aria_unsupported_elements — 2 spec tests pass (valid + invalid HTML)
  • Invalid cases cover all 4 unsupported elements with both role and aria-* attributes
  • Valid cases confirm that role/aria-* on supported elements (<div>, <input>) are not flagged
  • Edge case: element with both role and aria-hidden — only first attribute is reported (consistent with JSX version)
  • Edge case: invalid aria-foo attribute on <meta> — not flagged (consistent with JSX version)

Docs

Documentation is inline in the rule's rustdoc.

IxxyDev added 2 commits March 13, 2026 12:49
…dElements HTML rule

Add missing #[derive(Debug)] on AttributeKind and RuleState, reorder
struct before impl block, and add test cases for multiple aria attrs
on one element and invalid aria-* attributes.
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 15, 2026

🦋 Changeset detected

Latest commit: 18bb0fd

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 14 packages
Name Type
@biomejs/biome Minor
@biomejs/cli-win32-x64 Minor
@biomejs/cli-win32-arm64 Minor
@biomejs/cli-darwin-x64 Minor
@biomejs/cli-darwin-arm64 Minor
@biomejs/cli-linux-x64 Minor
@biomejs/cli-linux-arm64 Minor
@biomejs/cli-linux-x64-musl Minor
@biomejs/cli-linux-arm64-musl Minor
@biomejs/wasm-web Minor
@biomejs/wasm-bundler Minor
@biomejs/wasm-nodejs Minor
@biomejs/backend-jsonrpc Patch
@biomejs/js-api Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added A-Linter Area: linter L-HTML Language: HTML and super languages labels Mar 15, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 15, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: eee350f4-1e1b-467a-8a8c-89a0d970930f

📥 Commits

Reviewing files that changed from the base of the PR and between 7600d66 and 18bb0fd.

⛔ Files ignored due to path filters (3)
  • crates/biome_html_analyze/tests/specs/a11y/noAriaUnsupportedElements/astro/valid.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/noAriaUnsupportedElements/svelte/valid.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/noAriaUnsupportedElements/vue/valid.vue.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (4)
  • crates/biome_html_analyze/src/lint/a11y/no_aria_unsupported_elements.rs
  • crates/biome_html_analyze/tests/specs/a11y/noAriaUnsupportedElements/astro/valid.astro
  • crates/biome_html_analyze/tests/specs/a11y/noAriaUnsupportedElements/svelte/valid.svelte
  • crates/biome_html_analyze/tests/specs/a11y/noAriaUnsupportedElements/vue/valid.vue
✅ Files skipped from review due to trivial changes (2)
  • crates/biome_html_analyze/tests/specs/a11y/noAriaUnsupportedElements/svelte/valid.svelte
  • crates/biome_html_analyze/tests/specs/a11y/noAriaUnsupportedElements/vue/valid.vue
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_html_analyze/tests/specs/a11y/noAriaUnsupportedElements/astro/valid.astro

Walkthrough

Adds a changeset and a new HTML accessibility lint rule noAriaUnsupportedElements targeting meta, html, script, and style elements. The rule flags role (case-insensitive) and aria-* attributes on these elements, emits a diagnostic with a note indicating role or aria-*, and offers an unsafe automatic fix that removes the offending attribute from the AST. Adds corresponding valid/invalid test fixtures for HTML, Astro, Svelte and Vue.

Suggested reviewers

  • ematipico
  • dyc3
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarises the main change: porting the noAriaUnsupportedElements rule from JSX to HTML, which is reflected throughout the changeset.
Description check ✅ Passed The description clearly explains the rule's purpose, what it detects, provides test coverage details, and acknowledges AI assistance as required.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.

OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/biome_html_analyze/src/lint/a11y/no_aria_unsupported_elements.rs (1)

68-71: Consider storing the attribute reference in RuleState.

Storing only AttributeKind means action must re-search for the offending attribute. Storing a reference or identifier of the actual attribute would eliminate the need to duplicate the search logic and prevent the inconsistency noted above.

pub struct RuleState {
    attribute_kind: AttributeKind,
    attribute: AnyHtmlAttribute, // or appropriate type
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_html_analyze/src/lint/a11y/no_aria_unsupported_elements.rs`
around lines 68 - 71, The RuleState currently only stores attribute_kind which
forces the action code to re-search for the offending attribute; add a field to
RuleState (e.g., attribute or attribute_id / AnyHtmlAttribute-like identifier)
and populate it where RuleState is constructed so consumers like the action
closure can use the stored attribute directly instead of re-querying; update all
places that build RuleState (constructors/factory call sites) and adjust action
logic to reference RuleState.attribute (or attribute_id) and remove the
duplicated search, taking care to satisfy any required lifetimes or type
parameters for the attribute type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_html_analyze/src/lint/a11y/no_aria_unsupported_elements.rs`:
- Around line 133-145: The action function currently matches attributes by name
using starts_with("aria-")/role which can pick up invalid aria-* names; change
the attribute selection in action (in fn action) to mirror run's validation: for
each attribute, if attribute.as_html_attribute() yields a name, parse it with
AriaAttribute::from_str() and only treat it as an ARIA attribute when the parser
returns Some(valid_attribute) (or allow the special-case "role" as run does);
this ensures action skips invalid aria-* names like aria-foo just as run does.

---

Nitpick comments:
In `@crates/biome_html_analyze/src/lint/a11y/no_aria_unsupported_elements.rs`:
- Around line 68-71: The RuleState currently only stores attribute_kind which
forces the action code to re-search for the offending attribute; add a field to
RuleState (e.g., attribute or attribute_id / AnyHtmlAttribute-like identifier)
and populate it where RuleState is constructed so consumers like the action
closure can use the stored attribute directly instead of re-querying; update all
places that build RuleState (constructors/factory call sites) and adjust action
logic to reference RuleState.attribute (or attribute_id) and remove the
duplicated search, taking care to satisfy any required lifetimes or type
parameters for the attribute type.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 31532de7-92e5-4b0e-81c8-1c12f2bebd28

📥 Commits

Reviewing files that changed from the base of the PR and between 8574432 and e8b7da6.

⛔ Files ignored due to path filters (2)
  • crates/biome_html_analyze/tests/specs/a11y/noAriaUnsupportedElements/invalid.html.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/noAriaUnsupportedElements/valid.html.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (4)
  • .changeset/html-no-aria-unsupported-elements.md
  • crates/biome_html_analyze/src/lint/a11y/no_aria_unsupported_elements.rs
  • crates/biome_html_analyze/tests/specs/a11y/noAriaUnsupportedElements/invalid.html
  • crates/biome_html_analyze/tests/specs/a11y/noAriaUnsupportedElements/valid.html

@Netail Netail mentioned this pull request Mar 16, 2026
32 tasks
Copy link
Copy Markdown
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rule lacks in tests. We should test case sensitivity and components for Vue/Svelte/Astro

IxxyDev added 2 commits March 21, 2026 19:07
The `action` function used only `starts_with("aria-")` to find
attributes to remove, while `run` also checked `AriaAttribute::from_str()`
to ensure the attribute is a valid ARIA attribute. This inconsistency
meant that for an element like `<meta aria-foo="bar" aria-hidden="true">`,
`action` would remove `aria-foo` (invalid, not flagged by `run`) instead
of `aria-hidden` (valid, actually flagged).

Add the same `AriaAttribute::from_str()` validation to `action` and add
a test case covering this scenario.
…/Astro tests

Fix case sensitivity for uppercase aria attributes like ARIA-LABEL by
lowercasing attribute names before prefix and AriaAttribute::from_str
checks. Add test coverage for uppercase element names (META, STYLE,
SCRIPT) and uppercase attribute names (ROLE, ARIA-LABEL). Add test
specs for Vue, Svelte, and Astro file formats.
@IxxyDev IxxyDev requested a review from ematipico March 21, 2026 16:14
Comment on lines +142 to +143
let attribute_name_text = attribute_name.text_trimmed();
let attribute_name_lower = attribute_name_text.to_lowercase();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These allocate memory, use TokenText instead

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to TokenText

Comment on lines +92 to +94
let attribute_name_text = attribute_name.text_trimmed();

let attribute_name_lower = attribute_name_text.to_lowercase();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here it allocates memory

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to TokenText

…non-HTML files

Use token_text_trimmed() and to_ascii_lowercase_cow() instead of
text_trimmed() and to_lowercase() to avoid unnecessary memory
allocations.

Check HtmlFileSource to match element names case-insensitively in
HTML files but case-sensitively in Vue/Svelte/Astro, so PascalCase
components like <Meta> are not flagged in framework files.

Add PascalCase component tests to Vue/Svelte/Astro valid specs.
@IxxyDev IxxyDev force-pushed the feat/html-no-aria-unsupported-elements branch from 119172e to 18bb0fd Compare March 22, 2026 13:58
@IxxyDev IxxyDev requested a review from dyc3 March 22, 2026 14:01
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 29, 2026

Merging this PR will degrade performance by 27.26%

❌ 1 regressed benchmark
✅ 63 untouched benchmarks
⏩ 152 skipped benchmarks1

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
html_analyzer[index_1033418810622582172.html] 425.3 µs 584.8 µs -27.26%

Comparing IxxyDev:feat/html-no-aria-unsupported-elements (18bb0fd) with next (8574432)

Open in CodSpeed

Footnotes

  1. 152 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@ematipico ematipico merged commit b3eb63c into biomejs:next Apr 6, 2026
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Linter Area: linter L-HTML Language: HTML and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants