VisuallyHidden
Hide content visually while keeping it accessible to screen readers.
VisuallyHidden
The VisuallyHidden component hides content from sighted users while keeping it fully accessible to screen readers and assistive technologies. Use it for accessible labels, descriptions, and context that sighted users don't need to see.
VisuallyHidden uses industry-standard CSS that clips content to 1×1px and removes it from visual flow, while remaining accessible in the DOM.
Features
- Content hidden from sighted users
- Fully accessible to screen readers and AT
- No layout impact (doesn't shift other elements)
- Works with any inline content
- Preserves content for localization and copy-paste
Usage
import { VisuallyHidden } from '@loke/ui/visually-hidden';
export default function Example() {
return (
<button type="button">
<VisuallyHidden>Save the file</VisuallyHidden>
<span aria-hidden>💾</span>
</button>
);
}Props
Prop
Type
Examples
No visible label — screen readers announce "Delete item"
Input has no visible label — the hidden "Search" label is read by screen readers
Icon button with hidden label
<button type="button">
<VisuallyHidden>Delete item</VisuallyHidden>
<span aria-hidden="true">🗑️</span>
</button>Hidden label for input
<label htmlFor="email">
<VisuallyHidden>Email address</VisuallyHidden>
</label>
<input id="email" type="email" placeholder="email@example.com" />Additional context for button
<button type="button">
Subscribe
<VisuallyHidden>to our weekly newsletter</VisuallyHidden>
</button>Multi-part accessible name
<div>
<span>Status:</span>
<span className="font-bold">Active</span>
<VisuallyHidden>(Last updated 2 hours ago)</VisuallyHidden>
</div>CSS Implementation
VisuallyHidden applies these styles:
border: 0;
clip: rect(0, 0, 0, 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
white-space: nowrap;
width: 1px;
word-wrap: normal;This approach:
- Removes element from visual flow (
position: absolute,width/height: 1px) - Clips content to prevent any visibility (
clip) - Hides overflow and prevents wrapping (
overflow,white-space) - Keeps element in the DOM and accessibility tree
Accessibility
VisuallyHidden is one of the most important accessibility tools:
- Screen readers announce content normally
- Keyboard navigation includes only focusable elements; hidden text itself is not focusable unless explicitly made focusable
- Content is available to all assistive technologies
- Doesn't remove content from DOM or accessibility tree (unlike
display: noneoraria-hidden)
Common Patterns
Icon-only buttons
Always provide an accessible label:
// Good: visible text
<button>Download</button>
// Good: hidden text with icon
<button>
<VisuallyHidden>Download</VisuallyHidden>
<DownloadIcon />
</button>
// Acceptable: aria-label (but hidden text is preferred for i18n)
<button aria-label="Download">
<DownloadIcon />
</button>Form labels
Always label inputs:
// Good: visible label
<label htmlFor="name">Name</label>
<input id="name" />
// Good: hidden label (e.g., when label is implied visually)
<label htmlFor="search">
<VisuallyHidden>Search</VisuallyHidden>
</label>
<input id="search" placeholder="Search..." />Duplicate content
Don't hide content that's critical to understanding:
// Bad: hiding essential context
<button>
<VisuallyHidden>View all orders</VisuallyHidden>
See more
</button>
// Good: essential info is visible; extra context is hidden
<button>
See more orders
<VisuallyHidden>(5 new orders pending)</VisuallyHidden>
</button>Best practices
- Use for labels, descriptions, and context that aren't visually obvious
- Keep hidden text short and meaningful (not redundant)
- Pair with
aria-hidden="true"on decorative elements to avoid duplication - Test with a screen reader to ensure content is announced
- Avoid hiding large blocks of content; VisuallyHidden is for concise labels and context
- Consider localization: hidden text still needs translation
- Remember that hidden text can be selected and copied; write it clearly
Alternatives
- aria-label: Good for very short labels, less flexible
- aria-labelledby: Links to visible text as accessible name
- aria-describedby: Links to description (not the accessible name)
<legend>: For form fieldsets- Visible labels: Always prefer visible text when possible
VisuallyHidden is best for labels that are implied visually but need to be explicit for AT users.