oxc-project/oxc · warning

Static HTML elements with event handlers require a role.

Error message

Static HTML elements with event handlers require a role.

What it means

This is the `jsx_a11y/no-static-element-interactions` rule in oxlint. It fires when a static (non-interactive, non-interactive-role, non-hidden) HTML element carries one of the default interaction handlers — `onClick`, `onMouseDown`, `onMouseUp`, `onKeyPress`, `onKeyDown`, `onKeyUp` (see `DEFAULT_HANDLERS`) or a configured `handlers` entry — without any ARIA role. A clickable `<div>` with no role is invisible as an operable control to assistive tech.

Source

Thrown at crates/oxc_linter/src/rules/jsx_a11y/no_static_element_interactions.rs:27

use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use oxc_str::CompactStr;

use crate::{
    AstNode,
    context::LintContext,
    globals::HTML_TAG,
    rule::{DefaultRuleConfig, Rule},
    utils::{
        get_element_type, get_prop_value, has_jsx_prop, has_jsx_prop_ignore_case, is_abstract_role,
        is_hidden_from_screen_reader, is_interactive_element, is_interactive_role,
        is_non_interactive_element, is_non_interactive_role, is_presentation_role,
    },
};

fn no_static_element_interactions_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Static HTML elements with event handlers require a role.")
        .with_help("Add a role attribute to this element, or use a semantic HTML element instead.")
        .with_label(span)
}

const DEFAULT_HANDLERS: &[&str] =
    &["onClick", "onMouseDown", "onMouseUp", "onKeyPress", "onKeyDown", "onKeyUp"];

#[derive(Debug, Default, Clone, Deserialize)]
pub struct NoStaticElementInteractions(Box<NoStaticElementInteractionsConfig>);

impl std::ops::Deref for NoStaticElementInteractions {
    type Target = NoStaticElementInteractionsConfig;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Replace the static element with `<button>` (styled to reset defaults) so semantics come free.
  2. Add an appropriate `role` (e.g. `role="button"`) plus `tabIndex={0}` and keyboard handlers when a real button is impossible.
  3. Extend the rule's `handlers` config for legitimate non-pointer cases like `onMouseDown` on drag containers.
  4. Disable inline only for genuinely mouse-only affordances (rare; prefer `role="presentation"` + documented reason).

Example fix

// before
<div onClick={() => setOpen(true)}>Show details</div>

// after
<button type="button" onClick={() => setOpen(true)}>Show details</button>
Defensive patterns

Strategy: validation

Validate before calling

npx oxlint --jsx-a11y/no-static-element-interactions src/

Prevention

When it happens

Trigger: A JSX element whose type is a static HTML tag (per `HTML_TAG` + `is_non_interactive_element`), carrying a handler from the checked list, while `get_prop_value` finds no `role` attribute, the role is not presentation/abstract, and the element is not hidden from screen readers.

Common situations: Clickable div cards, drag-and-drop surfaces, dismiss-on-click overlays, custom cells in data grids; the single most common violation when a team first enables jsx-a11y recommended.

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/c9e59c9c8406e6e2. Report an issue: GitHub.