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
- Remove `aria-hidden="true"` from the focusable element.
- If the element must stay hidden from AT, make it unfocusable: remove `tabIndex` / set `tabIndex={-1}`, remove `href`, or use the `inert` attribute.
- 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 hiding an element from AT, also remove it from the tab order (tabIndex={-1} or inert).
- Never put aria-hidden on interactive controls; hide decorative children instead.
- Test tab order with the keyboard after any aria-hidden change.
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
- Missing value for `aria-label` attribute.
- Missing value for `aria-labelledby` attribute.
- Ambiguous text within anchor, screen reader users rely on li
- Missing accessible content when using `a` elements.
- `{role}` role is missing required aria props {props}.
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/084d92d1a471076c.
Report an issue: GitHub.