Axon 911 Frontend DocsAxon 911 Frontend Docs
Axon 911 Frontend Docs
    Loading navigation…
U
User
Switch to light mode
  1. Primitives
  2. Visually Hidden
  3. Accessibility

Visually Hidden

Accessibility

Import VisuallyHidden, VISUALLY_HIDDEN_ELEMENTS from @prepared911/ui/visually-hidden.
In the tree, out of the layout
VisuallyHidden keeps its content in the accessibility tree: a screen reader reads it, and it can supply an element's accessible name (directly as its content, or referenced through aria-labelledby/aria-describedby). It removes the content from the visible layout through position: absolute, a 1px clipped box, clip-path: inset(50%), and overflow: hidden, not through display: none or visibility: hidden, both of which would also remove it from the accessibility tree.
aria-hidden, aria-label, aria-labelledby, hidden, and inert are excluded, not just discouraged
VisuallyHidden's whole purpose is to stay in the accessibility tree while leaving the visible layout, so aria-hidden="true", hidden, and inert are excluded as props entirely (types and runtime, warned once each when dropped), not merely documented as a footgun. Any of the three removes the element from the accessibility tree, which would silently defeat the primitive: an icon-only button whose accessible name comes from <VisuallyHidden aria-hidden="true">Close incident</VisuallyHidden> would render with no accessible name and no error (WCAG 4.1.2, Name, Role, Value). role is excluded alongside them (also warned once when dropped) so a caller cannot swap in different assistive-technology semantics either; and because role is fixed, the host's implicit ARIA role is always generic, which ARIA 1.2 does not let anyone give an accessible name. aria-label and aria-labelledby are excluded for that reason (dropped silently; there is nothing to warn about beyond the type error). Name a region VisuallyHidden's content labels by pointing aria-labelledby at its id from the other element, not from VisuallyHidden itself.
Never a tab stop
VisuallyHidden takes no tabIndex, so its own host is never a tab stop. It does not, by itself, make a focusable descendant unfocusable: a link or a form control nested inside it keeps its native tab stop, at a location with no visible focus indicator, even when it is nested inside a wrapping component rather than directly. That combination is a keyboard trap in disguise, so VisuallyHidden warns once in development when it finds one anywhere in its children: a/area with href, an enabled form control (not disabled, and not input type="hidden"), summary, iframe, video/audio with controls, tabIndex >= 0, or an editable contentEditable value.
Icon-only controls
The most common use is naming an icon-only control. Put the VisuallyHidden text as the control's content, next to a decorative icon marked aria-hidden="true", so the control's accessible name comes from the hidden text and the icon adds nothing redundant to the tree.
Skip links are out of scope
A skip link needs to become visible on keyboard focus so a sighted keyboard user can see where focus is. VisuallyHidden's hiding is unconditional; a skip link is a separate, later primitive.
Previous
Visually Hidden / API and Development
Next
Accordion / Usage
On this page
In the tree, out of the layout
aria-hidden, aria-label, aria-labelledby, hidden, and inert are excluded, not just discouraged
Never a tab stop
Icon-only controls
Skip links are out of scope