Skip to content

feat(html_analyze): implement noInteractiveElementToNoninteractiveRole#10028

Open
Netail wants to merge 1 commit intobiomejs:nextfrom
Netail:feat/port-no-interactive-element-to-noninteractive-role
Open

feat(html_analyze): implement noInteractiveElementToNoninteractiveRole#10028
Netail wants to merge 1 commit intobiomejs:nextfrom
Netail:feat/port-no-interactive-element-to-noninteractive-role

Conversation

@Netail
Copy link
Copy Markdown
Member

@Netail Netail commented Apr 17, 2026

Summary

Port noInteractiveElementToNoninteractiveRole to HTML

Related #8155

Test Plan

Added unit tests

Docs

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 17, 2026

🦋 Changeset detected

Latest commit: 45dda8b

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-JavaScript Language: JavaScript and super languages L-HTML Language: HTML and super languages labels Apr 17, 2026
@Netail Netail mentioned this pull request Apr 17, 2026
32 tasks
@Netail Netail marked this pull request as draft April 17, 2026 22:01
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 17, 2026

Walkthrough

This PR introduces a new HTML accessibility lint rule noInteractiveElementToNoninteractiveRole that prevents assignment of non-interactive ARIA roles to interactive HTML elements. The change includes the core rule implementation for HTML, comprehensive test fixtures across HTML, Astro, Vue, and Svelte formats, a changeset entry documenting the new rule, and a control-flow refactor to the existing JavaScript variant of the rule.

Possibly related PRs

Suggested labels

A-Linter, A-Parser, L-HTML

Suggested reviewers

  • dyc3
  • ematipico
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: porting the noInteractiveElementToNoninteractiveRole rule to the HTML analyser.
Description check ✅ Passed The description is directly related to the changeset, explaining that it ports the rule to HTML and mentions added unit tests.

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

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

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

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.

🧹 Nitpick comments (2)
crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/vue/valid.vue (1)

2-8: Please add PascalCase native-lookalike cases to lock case-sensitive matching.

Nice fixture overall; adding a couple of explicit component-like tags (e.g. Input, A) would harden regressions around template-language case sensitivity.

🧪 Suggested fixture additions
 <TestComponent onClick="doFoo" />
 <Button onClick="doFoo" />
+<Input role="img" />
+<A href="http://x.y.z" role="img" />
 
 <a href="http://x.y.z" role="button" />
 <a href="http://x.y.z" tabIndex="0" role="button" />
 <button class="foo" role="button" />

Based on learnings: “When checking HTML element names in biome_html_analyze lint rules, the comparison must be case-insensitive for .html files, but case-sensitive for .astro, .vue, and .svelte files.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/vue/valid.vue`
around lines 2 - 8, Add PascalCase native-lookalike tag examples to the fixture
to lock case-sensitive matching for .vue files: add component-like tags such as
Input and A (e.g., <Input onClick="doFoo" /> and <A href="http://x.y.z"
role="button" />) and include the same attribute variants already present
(onClick, href, tabIndex, role, class) so the rule sees PascalCase native names
(Input, A) alongside TestComponent and Button to ensure template-language case
sensitivity is covered.
crates/biome_html_analyze/src/lint/a11y/no_interactive_element_to_noninteractive_role.rs (1)

94-102: Consider combining the svg and canvas exemptions.

These two checks have identical structure. You could consolidate them for brevity.

♻️ Optional refactor
-            // A <svg> element can be given an "img" to make it non-interactive for a11y reasons.
-            if is_html_tag(node, source_type, "svg") && role_attribute_value == "img" {
-                return None;
-            }
-
-            // A <canvas> element can be given an "img" to make it non-interactive for a11y reasons.
-            if is_html_tag(node, source_type, "canvas") && role_attribute_value == "img" {
+            // <svg> and <canvas> elements can be given "img" role to make them non-interactive for a11y reasons.
+            if (is_html_tag(node, source_type, "svg") || is_html_tag(node, source_type, "canvas"))
+                && role_attribute_value == "img"
+            {
                 return None;
             }
🤖 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_interactive_element_to_noninteractive_role.rs`
around lines 94 - 102, These two identical checks for is_html_tag(node,
source_type, "svg") and is_html_tag(node, source_type, "canvas") with
role_attribute_value == "img" should be consolidated into a single conditional:
check if role_attribute_value == "img" and the tag name is either "svg" or
"canvas" (e.g., by matching the tag string or using a small set/array), then
return None; replace the two separate if blocks with this single combined check
in the function containing these conditions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@crates/biome_html_analyze/src/lint/a11y/no_interactive_element_to_noninteractive_role.rs`:
- Around line 94-102: These two identical checks for is_html_tag(node,
source_type, "svg") and is_html_tag(node, source_type, "canvas") with
role_attribute_value == "img" should be consolidated into a single conditional:
check if role_attribute_value == "img" and the tag name is either "svg" or
"canvas" (e.g., by matching the tag string or using a small set/array), then
return None; replace the two separate if blocks with this single combined check
in the function containing these conditions.

In
`@crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/vue/valid.vue`:
- Around line 2-8: Add PascalCase native-lookalike tag examples to the fixture
to lock case-sensitive matching for .vue files: add component-like tags such as
Input and A (e.g., <Input onClick="doFoo" /> and <A href="http://x.y.z"
role="button" />) and include the same attribute variants already present
(onClick, href, tabIndex, role, class) so the rule sees PascalCase native names
(Input, A) alongside TestComponent and Button to ensure template-language case
sensitivity is covered.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4bb17e9a-c59a-4240-87d3-17343e38e3d7

📥 Commits

Reviewing files that changed from the base of the PR and between 3422d71 and 45dda8b.

⛔ Files ignored due to path filters (8)
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/astro/invalid.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/astro/valid.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/invalid.html.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/svelte/invalid.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/svelte/valid.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/valid.html.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/vue/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/vue/valid.vue.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (11)
  • .changeset/neat-signs-crash.md
  • crates/biome_html_analyze/src/lint/a11y/no_interactive_element_to_noninteractive_role.rs
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/astro/invalid.astro
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/astro/valid.astro
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/invalid.html
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/svelte/invalid.svelte
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/svelte/valid.svelte
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/valid.html
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/vue/invalid.vue
  • crates/biome_html_analyze/tests/specs/a11y/noInteractiveElementToNoninteractiveRole/vue/valid.vue
  • crates/biome_js_analyze/src/lint/a11y/no_interactive_element_to_noninteractive_role.rs

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Apr 17, 2026

Merging this PR will not alter performance

✅ 125 untouched benchmarks
⏩ 129 skipped benchmarks1


Comparing Netail:feat/port-no-interactive-element-to-noninteractive-role (45dda8b) with next (3422d71)

Open in CodSpeed

Footnotes

  1. 129 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.

@Netail Netail marked this pull request as ready for review April 17, 2026 22:15
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 L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant