VisuallyHidden

Hide content from sighted users while keeping it announced by screen readers.

VisuallyHidden is a thin wrapper around Radix’s VisuallyHiddenPrimitive.Root. It renders a <span> whose styles take it out of the visual layout (zero dimensions, clipped, absolutely positioned) while leaving the text in the accessibility tree. Screen readers read it; sighted users never see it.

Reach for it when the visual design and the accessible experience disagree: an icon-only button that still needs a spoken label, a dialog that has an obvious visual heading but no <h2>, a live region that provides extra context for an action. It is not the same as display: none or visibility: hidden, both of which remove the element from the accessibility tree entirely, and it is the opposite of aria-hidden, which hides from assistive tech but leaves the element on screen.

Import

import { VisuallyHidden } from "@unkey/ui";

Icon-only button

An icon alone has no accessible name. Wrap a short verb in VisuallyHidden so screen readers announce “Delete workspace, button” while sighted users just see the trash glyph.

aria-label="Delete workspace" achieves the same announcement and is often the better choice for a single word. Prefer VisuallyHidden when the label is more than a couple of words, contains punctuation, or needs to be translated alongside the rest of the page copy — DOM text goes through the same i18n pipeline as every other string, aria-label values often do not.

Dialog title

Radix’s Dialog requires a DialogTitle for screen readers, but a confirmation prompt often doesn’t need a visible heading — the body copy already carries the question. Hide the title visually and keep the ARIA contract intact.

Delete API key

Are you sure you want to delete this API key? Active clients will start returning 401 immediately.

Extra context for an action

Icon-only controls sometimes need a longer sentence for screen-reader users than would fit next to the glyph. Render the short label inline and the longer one inside VisuallyHidden.

Accessibility

Four mechanisms get confused. Pick deliberately:

  • VisuallyHidden — hidden visually, announced by screen readers, part of the DOM. Use when the accessible name or description is richer than the visual design can show.
  • display: none / visibility: hidden — hidden everywhere, including the accessibility tree. Use when the content is genuinely not present for anyone (e.g. collapsed panels).
  • aria-hidden="true" — visible on screen, hidden from assistive tech. Use for decorative glyphs that sit next to a text label, so the label isn’t announced twice.
  • aria-label / aria-labelledby — no DOM text, but an accessible name is attached to the element. Use for short, stable labels on interactive controls.

A good default for icon-only buttons: if the label is one or two words and never changes, reach for aria-label. If it’s a sentence, a translated string, or needs to stay in sync with surrounding copy, use VisuallyHidden.

Do not put interactive elements (links, buttons, inputs) inside VisuallyHidden. They remain in the tab order and keyboard users will land on a control they cannot see.

Props

VisuallyHidden accepts every prop that a native <span> accepts. It forwards its ref to the underlying element.

children ReactNode Optional

The content to hide visually. Keep it to non-interactive elements: text, headings, paragraphs.

className string Optional

Additional Tailwind classes. Rarely needed — the primitive already sets every style required to hide the element without removing it from the accessibility tree.

...rest HTMLAttributes<HTMLSpanElement> Optional

Any other span attribute (id, lang, role, data attributes). Useful when another element references this one with aria-labelledby or aria-describedby.

  • Button — the most common host for a hidden label; consider aria-label first for one- or two-word names.
  • Separator — uses decorative to opt out of the accessibility tree; the mirror image of this component.
  • Alert — for messages that should both appear on screen and be announced.