PlVisuallyHidden
Content for a screen reader and for nobody else. It stays in the accessibility tree and takes no space on the screen, which is what makes it the right way to name a control that draws only a glyph.
import { PlVisuallyHidden } from 'plass-ui';
<button type="button">
<span aria-hidden="true">✕</span>
<PlVisuallyHidden>Close</PlVisuallyHidden>
</button>;This one is React-only, and it is not an omission. What it works around is a DOM problem, text that must be in the accessibility tree and off the screen at once, and Flutter's tree is not the render tree. The Dart answer is Semantics:
Semantics(
label: 'Close',
child: ExcludeSemantics(child: Text('✕')),
);Props
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | What is read out and drawn nowhere |
| focusable | boolean | false | Brings the content back into the flow while anything inside holds the focus. For a skip link |
| render | RenderProp | — | Renders something other than a <span> (<div />, <h2 />) |
| className | string | — | Sits alongside the component’s own classes |
PlVisuallyHidden is not in the Flutter package yet.
Every native <span> attribute passes straight through, aria-live and id included. There is no variant, no size and no color: nothing is drawn, so there is nothing for them to decide.
What the shared axes mean across the library is in prop conventions.
Examples
Naming a control that draws a glyph
The most common use, and the defect it fixes: a button whose whole label is an icon has no accessible name at all. The glyph takes aria-hidden so it is not read as a second one.
focusable
Brings the content back into the page while anything inside it holds the focus. That is one element in a document, the skip link, and it cannot be done from the outside: the clip is position: absolute, so revealing it means putting the element back in the flow.
It answers :focus-within rather than :focus, because what is tabbed to is almost always a link inside the box rather than the box itself.
A revealed box is
position: staticand takes its space back. Put it somewhere that can hold it, a positioned ancestor, or the top of the page, which is where a skip link belongs anyway.
A live region
An announcement with nothing to draw. aria-live on a hidden element is how a change that is obvious on screen (a count going up, a filter narrowing a list) reaches a reader who cannot see it happen.
render
Renders something other than a <span>. A heading that structures the page for a screen reader without appearing in the design, or the <div> a live region wants.
<PlVisuallyHidden render={<h2 />}>Search results</PlVisuallyHidden>Accessibility
- The content is in the accessibility tree.
hidden,display: noneandvisibility: hiddenall take it off;opacity: 0leaves a clickable ghost the size of the words. A one-pixel clipped box is the only form that is absent to a sighted reader and present to every other kind. - It does not set
aria-hiddenon itself, and putting one on it would defeat the component entirely. - Text inside it is still selected by a find-in-page and still copied by a select-all. That is the platform's behaviour and not something to work around.
- A hidden name and a visible one on the same control give it two names. Mark the glyph beside it
aria-hidden="true".