oxc-project/oxc · warning
Interactive elements should not be assigned non-interactive
Error message
Interactive elements should not be assigned non-interactive roles.
What it means
This is the `jsx_a11y/no-interactive-element-to-noninteractive-role` rule in oxlint. It fires when a natively interactive HTML element (per `is_interactive_element`, e.g. `<a href>`, `<button>`, `<input>`) is given a non-interactive ARIA role (per `is_non_interactive_role`, e.g. `listitem`, `heading`, `presentation`). Converting an interactive element to a non-interactive one removes expected semantics while it still behaves interactively.
Source
Thrown at crates/oxc_linter/src/rules/jsx_a11y/no_interactive_element_to_noninteractive_role.rs:22
use oxc_ast::{AstKind, ast::JSXAttributeValue};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use oxc_str::CompactStr;
use crate::{
AstNode,
context::LintContext,
globals::HTML_TAG,
rule::{DefaultRuleConfig, Rule},
utils::{
get_element_type, has_jsx_prop_ignore_case, is_interactive_element, is_non_interactive_role,
},
};
fn no_interactive_element_to_noninteractive_role_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Interactive elements should not be assigned non-interactive roles.")
.with_help("WAI-ARIA roles should not be used to convert an interactive element to a non-interactive element. Wrap the element or use a different structure.")
.with_label(span)
}
#[derive(Debug, Default, Clone, Deserialize)]
pub struct NoInteractiveElementToNoninteractiveRole(
Box<NoInteractiveElementToNoninteractiveRoleConfig>,
);
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct NoInteractiveElementToNoninteractiveRoleConfig {
/// A mapping of HTML element names to arrays of ARIA role strings that are
/// allowed overrides for that element.
#[serde(default, flatten)]
pub allowed_roles: FxHashMap<CompactStr, Vec<CompactStr>>,
}
impl Default for NoInteractiveElementToNoninteractiveRoleConfig {View on GitHub (pinned to e1e7af627c)
Solutions
- Remove the non-interactive `role` from the interactive element.
- Restructure: wrap the interactive element in a container that carries the non-interactive role.
- If the pairing is intentional, whitelist it in the rule config's per-element allowed roles mapping.
Example fix
// before
<a href="#" role="listitem" onClick={select}>Item</a>
// after
<li><a href="#" onClick={select}>Item</a></li> Defensive patterns
Strategy: validation
Validate before calling
npx oxlint --jsx_a11y/no-interactive-element-to-noninteractive-role src/
Prevention
- Never override an interactive element's role with a non-interactive one; restructure instead.
- Keep the rule's allowed-roles mapping tiny; add entries only with an a11y review.
- When changing a tag in JSX, check its role attribute still matches.
When it happens
Trigger: A JSX element resolved as an interactive HTML tag that also has a `role` attribute (found via `has_jsx_prop_ignore_case`) whose literal value is a non-interactive role, and the pair is not whitelisted in the rule's configurable `roles` mapping (HTML element name -> allowed role list).
Common situations: `<a href="#" role="listitem">` inside custom lists; `<button role="presentation">` used to hide a control; CSS-framework markup that sprinkles roles on buttons/links; refactors that changed element types without updating roles.
Related errors
- Non-interactive elements should not be assigned interactive
- The `{element}` element has an implicit role of `{role}`. De
- Static HTML elements with event handlers require a role.
- Prefer `{tag}` over `role` attribute `{role}`.
- The attribute `{attr_name}` is not supported by the role `{r
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/099d965609a401bd.
Report an issue: GitHub.