Skip to main content

Utilities

Helpers for accessibility, layout, and shared patterns that don't fit a specific component category.

Components

ComponentPurpose
Screenreader-onlyVisually hidden content accessible to screen readers
Skip navigationBypass repeated content for keyboard users
Focus trapContain focus within a modal or dialog
Visually hidden inputCustom checkbox/radio styling with accessible native input
Live regionDynamic content announcements for screen readers

Screenreader-only

Hides content visually while keeping it in the accessibility tree. Implemented as the <VisuallyHidden> component.

  • Used for descriptive labels on icon-only buttons
  • Used for additional context that sighted users get visually
  • Never use display: none or visibility: hidden for content that should be read
  • asChild prop merges sr-only class onto the child element instead of wrapping in a <span>

Props

PropTypeDefaultDescription
childrenReactNode(required)Content to hide visually
asChildbooleanfalseMerge onto child element instead of wrapping
classNamestringAdditional classes

Usage

  • Icon-only buttons: add a <VisuallyHidden> label inside the button alongside the icon
  • Data tables: add screenreader-only context to column headers where the visual context is ambiguous
  • Navigation: provide descriptive text that supplements visual cues
Screenreader-only
Icon button with VisuallyHidden label
<VisuallyHidden>

Visible: Account balance (Current balance for your primary checking account)

Skip navigation

A link that becomes visible on keyboard focus, allowing users to bypass repeated navigation.

  • First focusable element on the page
  • Links to the main content landmark
  • Visible only on :focus — hidden by default
  • Must pass WCAG 2.4.1 (bypass blocks)
Skip navigation link

Tab into the box below to reveal the skip link:

Focus trap

Contains keyboard focus within a specific area (dialog, modal, drawer).

  • Tab cycles through focusable elements inside the trap
  • Shift+Tab moves backwards within the trap
  • Focus returns to the trigger element when the trap is released
  • Built into the Dialog and Popover components via @base-ui/react

Visually hidden input

Native <input> elements positioned off-screen for custom styled checkboxes and radios.

  • Maintains native keyboard behavior and form submission
  • Screen readers announce the native input, not the visual replacement
  • :checked state drives visual styling via CSS sibling selectors
  • Used when the default Checkbox/Switch components don't fit the design

Live region

Announces dynamic content changes to screen readers via aria-live.

  • politeness: "polite" (default) waits for the user to finish, "assertive" interrupts immediately
  • atomic: true (default) reads the entire region, false reads only the changed nodes
  • relevant: controls which mutations trigger announcements — "additions", "removals", "text", or "all"
  • role: semantic roles "status", "alert", "log", "timer""alert" and "status" imply aria-live so it is omitted
  • visuallyHidden: true (default) applies sr-only — set to false when the live content is also visible

Props

PropTypeDefaultDescription
politeness"polite" | "assertive""polite"Urgency level of the announcement
atomicbooleantrueRead entire region or only changes
relevant"additions" | "removals" | "text" | "all"Which mutations to announce
role"status" | "alert" | "log" | "timer"Semantic role (some imply aria-live)
visuallyHiddenbooleantrueApply sr-only styling

Usage

  • Transfer confirmations: polite announcement after form submit
  • Error alerts: assertive announcement with role="alert"
  • Chat/log feeds: role="log" with relevant="additions"
  • Timers: role="timer" for countdown displays
Live region

Click the button to trigger a screen reader announcement:

politeness="polite"politeness="assertive"role="status"role="alert"role="log"role="timer"

Design and accessibility

  • Screenreader-only content must be meaningful and concise
  • Test with actual screen readers (VoiceOver, NVDA) to verify announcements
  • Skip navigation must be the first focusable element
  • Focus traps must release focus on Escape and return to trigger
  • Never remove focus outlines without providing an alternative indicator
  • Prefer polite live regions — use assertive only for errors or critical alerts
  • Live regions must exist in the DOM before content changes; do not conditionally render the container
  • Keep announcements brief — screen readers cannot be interrupted mid-sentence by a new polite announcement