Hides content visually while keeping it available to screen readers, for context that assistive technology needs and the visual design does not.
import { VisuallyHidden } from 'frontile';
The common case is naming a control whose meaning is carried by an icon.
Where meaning is carried by colour, shape, or position, the sighted reader gets it for free and everyone else gets nothing. A hidden word restores it without changing the layout.
Combined with aria-live, a hidden region announces something that the visual
design already shows some other way — a count that updated, a save that
finished — without printing it twice on screen.
The component renders a <div class="sr-only">, the Tailwind utility that takes
content out of the visual flow while leaving it in the accessibility tree:
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
That is a different thing from display: none, visibility: hidden, or
hidden, all of which remove content from the accessibility tree too. Use
those when you want content gone for everyone.
aria-labelBoth name a control, and for a plain string aria-label is shorter:
{{! equivalent }}
<Button aria-label='Close dialog'><CloseIcon /></Button>
<Button>
<VisuallyHidden>Close dialog</VisuallyHidden>
<CloseIcon />
</Button>
Reach for VisuallyHidden when aria-label cannot do the job:
aria-label takes text only.aria-label cannot announce.Note that aria-label on a container replaces everything inside it for
screen readers, so an element with both an aria-label and visible text hides
that text. VisuallyHidden adds instead of replacing.
Hidden text is concatenated with visible text into one accessible name, so repeating the visible label produces "Save changes Save changes":
{{! ✗ the name is read twice }}
<Button>
<VisuallyHidden>Save changes</VisuallyHidden>
Save changes
</Button>
{{! ✓ the hidden text supplies what the icon cannot }}
<Button>
<VisuallyHidden>Save changes</VisuallyHidden>
<SaveIcon />
</Button>
In a list, a generic label is announced identically on every row, so it names the action without identifying its target:
{{! ✗ every row announces "Delete" }}
<Button><VisuallyHidden>Delete</VisuallyHidden><DeleteIcon /></Button>
{{! ✓ }}
<Button>
<VisuallyHidden>Delete contact {{contact.name}}</VisuallyHidden>
<DeleteIcon />
</Button>
Anything focusable inside a VisuallyHidden becomes a keyboard trap in
appearance: a sighted keyboard user tabs to something they cannot see. Keep
links and controls out of it, or use a skip-link pattern that reveals itself on
focus instead.
sr-only comes from Tailwind, so it has to be in your generated CSS. Frontile's
own components use it, which means your Tailwind setup needs to scan the
frontile package — see
Installation
.
Element: <span class="hljs-title class_">HTMLDivElement</span>
| Name | Type | Default | Description |
|---|
| Name | Type | Default | Description |
|---|---|---|---|
default
*
|
Array
|
- |