VisuallyHidden
Text that screen readers read but the screen doesn't show. Use it to add context that sighted people get from the layout, like which invoice an "Edit" link belongs to, or to announce a change.
Installation
Set up Camaleo once (Installation), then import it. It has no state, so it renders on the server.
import { VisuallyHidden } from "@camaleo/react";Anatomy
One <span> shrunk to 1 × 1 px and clipped. It stays in the page and in the accessibility tree, so screen readers and translation tools still get the text.
Why not display: none or hidden?
display: none, visibility: hidden and the hidden attribute remove the element from the accessibility tree as well as from the screen: nobody gets the text. aria-hidden="true" does the opposite of what you want here: it stays on screen and is skipped by screen readers. VisuallyHidden is the one way to keep text for assistive technology only.
Examples
Every example is a real file you can copy. Turn on a screen reader (VoiceOver, NVDA or TalkBack) to hear the hidden text, or inspect the accessibility tree in your browser's dev tools.
| Technique | On screen | Screen reader |
|---|---|---|
VisuallyHidden | No | Yes |
display: none | No | No |
hidden attribute | No | No |
aria-hidden="true" | Yes | No |
Four ways to hide
Only VisuallyHidden takes text off the screen and keeps it for screen readers. display: none and hidden hide it from everyone; aria-hidden hides it only from screen readers.
aria-label or hidden text
For an IconButton, aria-label is enough. When part of the name is visible, add the rest as hidden text: it is real text, so browser translation translates it too.
Announcing a change
Add to the cart. The count changes on screen, and a hidden role="status", rendered empty from the start, tells screen reader users what happened.
Skip link
Click the hint, then press Tab. A SkipLink sits at the top of the page and appears over it on focus: use it to jump to the main content.
Shown on focus
Click the hint, then press Tab. With focusable, the "Skip to results" link appears in place while it has focus, and Enter jumps past the filters. Without it, the text stays hidden even when focused.
| Client | Amount | Status |
|---|---|---|
| Padaria Estrela | $412.00 | Paid |
| Café Aurora | $186.40 | Open |
| Mercado Bom Preço | $1,240.00 | Overdue |
Table caption
When the layout makes a table's purpose obvious, a hidden <caption> still names it, and names the scroll region on phones.
A label you can't see
A lone search box next to a search button can hide its label. It must still be a real <label>; a placeholder disappears as soon as someone types.
Symbols in words
"★ 4.8 (312)" means nothing read aloud. Hide the symbols with aria-hidden and give screen readers the whole sentence instead.
In context
Hidden text is everywhere in a good interface. These are whole pieces of UI you can copy, in the skin you picked.
| Feature | Free | Pro |
|---|---|---|
| Invoices per month | 10 | Unlimited |
| Payment reminders | ||
| Bank reconciliation | ||
| Phone support |
Guidelines
On screen, "Edit" sits in its row. In a list of links, "Edit invoice #4821" still makes sense.
Screen reader users who list the links hear "Edit, Edit, Edit" and can't tell which is which.
Everybody benefits from a label: people with memory or attention difficulties, and anyone checking what they typed.
Once the fields are filled, nothing on screen says which is which.
Props
"status" (polite) or "alert" (urgent) to announce changes. Render it empty first, then set the text.Every attribute of a <span> passes through, like id (to point aria-describedby at it) or aria-live.
Styling hooks
There is nothing to style: the hiding styles are inline, so no stylesheet can undo them by accident. With focusable they come from the stylesheet instead, because inline styles can't react to focus.
Attributes
Tokens it reads
Accessibility
- The text stays in the accessibility tree: it joins the accessible name of the link or button around it.
- Without focusable it stays hidden on focus. For the jump to the main content use SkipLink; for a link that appears in place, like "Skip to results", use focusable.
- For live messages, the role="status" element must be in the page before the text changes, or screen readers miss it.
- Hidden text is for context that sighted people get from the layout. If everyone needs it, show it.