oxc-project/oxc · warning

`aria-hidden` must not be true on focusable elements.

Error message

`aria-hidden` must not be true on focusable elements.

What it means

This is the `jsx_a11y/no-aria-hidden-on-focusable` rule in oxlint. It fires when an element has `aria-hidden="true"` (parsed via `parse_jsx_value`) but is still focusable — for example it has a non-negative `tabIndex` or is natively interactive. The element disappears from assistive tech while remaining in the tab order, so focus lands on an invisible element.

Source

Thrown at crates/oxc_linter/src/rules/jsx_a11y/no_aria_hidden_on_focusable.rs:17

use oxc_ast::{
    AstKind,
    ast::{JSXAttributeItem, JSXAttributeValue, JSXOpeningElement},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;

use crate::{
    AstNode,
    context::LintContext,
    rule::Rule,
    utils::{get_element_type, has_jsx_prop_ignore_case, parse_jsx_value},
};

fn no_aria_hidden_on_focusable_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("`aria-hidden` must not be true on focusable elements.")
        .with_help("Remove `aria-hidden=\"true\"` from focusable elements or modify the element to be not focusable.")
        .with_label(span)
}

#[derive(Debug, Default, Clone)]
pub struct NoAriaHiddenOnFocusable;

declare_oxc_lint!(
    /// ### What it does
    ///
    /// Enforces that `aria-hidden="true"` is not set on focusable elements.
    ///
    /// ### Why is this bad?
    ///
    /// `aria-hidden="true"` on focusable elements can lead to confusion or unexpected behavior for screen reader users.
    ///
    /// ### Examples
    ///

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Remove `aria-hidden="true"` from the focusable element.
  2. If the element must stay hidden from AT, make it unfocusable: remove `tabIndex` / set `tabIndex={-1}`, remove `href`, or use the `inert` attribute.
  3. Move `aria-hidden` to a purely decorative inner span instead of the interactive wrapper.

Example fix

// before
<button aria-hidden="true" tabIndex={0} onClick={doThing} />

// after
<button onClick={doThing}><span aria-hidden="true">icon</span></button>
Defensive patterns

Strategy: validation

Validate before calling

npx oxlint --jsx-a11y/no-aria-hidden-on-focusable src/

Type guard

const isHiddenButFocusable = (props: Record<string, unknown>) =>
  props['aria-hidden'] === true &&
  (Number(props.tabIndex ?? NaN) >= 0 || 'href' in props || props.type === 'button');

Prevention

When it happens

Trigger: A JSX element with `aria-hidden` resolving to `true` combined with focusability: `tabIndex={0}` (or any non-negative value), or an inherently interactive element such as `<a href>`, `<button>`, `<input>` that also sets `aria-hidden="true"`.

Common situations: Hiding decorative icons that were wrapped in a focusable container; toggling `aria-hidden` on modal overlays while forgetting to also set `tabIndex=-1` or inert; icon buttons marked hidden but still rendered; conditional UI hidden from screen readers but left clickable.

Related errors


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