# Visually Hidden VisuallyHidden renders content that is visible to screen readers but hidden from sighted users. Section: components Docs: https://nebula.kaluza.com/components/visually-hidden --- ## When to use - To add screen-reader-only context to an interactive element that has no visible text label, such as an icon-only button - To disambiguate repeated link or button labels, such as multiple "Find out more" links on the same page, by appending specific context - To announce a currency denomination or unit alongside a numeric input, where the context is visually clear from the UI but absent from the input's accessible label - To provide off-screen context that would be redundant or noisy if shown visually, but is necessary for screen reader users to understand the structure ## When not to use - **To hide content from all users** - `VisuallyHidden` is not a general hide utility. Content that sighted users should not see should be conditionally rendered or removed from the DOM, not hidden with this component. - **To hide content that visual users also need** - if the information is important, show it visually. Visually hidden content creates an inconsistency between visual and screen reader experiences. - **As a substitute for a proper accessible name** - if a button or input can have a visible label, give it one. Visually hidden labels are a fallback, not a first choice. - **For decorative content** - decorative images and icons should use `aria-hidden="true"`, not `VisuallyHidden`. ## Anatomy 1. **VisuallyHidden** (a span element positioned absolutely at 1x1px, with overflow hidden and white-space nowrap, so it does not affect page layout but remains in the accessibility tree) ## Basic usage Add visually hidden text inside an interactive element to supplement its label. ```tsx

Find out more about tracking carbon

``` ## Variants and features ### Supplementing repeated links When the same link text appears multiple times on a page, screen reader users navigating by links hear a list of identical items with no useful distinction. Add visually hidden text to make each link unique. Always include a leading space before the visually hidden text to prevent screen readers from concatenating the visible and hidden text without a pause. ```tsx Find out more about the Economy 7 tariff ``` ### Adding currency context to inputs `CurrencyInput` uses `VisuallyHidden` internally to announce the currency denomination. This pattern shows how the component is used internally. ```tsx <> ``` ### Icon-only buttons When a button contains only an icon, wrap the accessible label in `VisuallyHidden` so it is available to screen readers without affecting the visual layout. ```tsx ``` For a Nebula button, use [CTA Button](/components/cta-button#icon-only)'s `icon` prop instead - it handles the accessible name and tooltip for you. ## Rules and guidance ### Usage - **VisuallyHidden** MUST NOT be used to hide content from all users. Rationale: VisuallyHidden content remains in the accessibility tree and is announced by screen readers. Use conditional rendering or display:none to remove content from all users. [id: visuallyhidden.not-general-hide; testability: manual] - **VisuallyHidden** MUST NOT be used for decorative content that should be hidden from screen readers. Rationale: Decorative elements such as icons that duplicate visible text should use aria-hidden="true" instead, so screen readers do not announce them. [id: visuallyhidden.not-for-decorative; testability: manual] - **VisuallyHidden** MUST be used to provide an accessible name when a button or link contains only an icon and no visible text. Rationale: Icon-only interactive elements have no accessible name without additional labelling. A missing accessible name is a WCAG 2.2 AA failure (SC 4.1.2). [id: visuallyhidden.icon-button; testability: auto] - **VisuallyHidden** SHOULD NOT be used as a substitute for a visible label when a visible label is feasible. Rationale: Visible labels help all users, including those using voice control, and reduce the cognitive load of scanning a form or interface. Visually hidden labels are appropriate when a visible label would be redundant or disruptive to the visual design. [id: visuallyhidden.accessible-name-fallback; testability: manual] ### Placement - **VisuallyHidden** WILL apply position:absolute, width:1px, height:1px, overflow:hidden, and clip automatically. The consumer does not need to implement the visually-hidden CSS pattern manually. [id: visuallyhidden.css-technique-applied; testability: auto] - **VisuallyHidden** MUST NOT contain content that is expected to affect page layout. Rationale: VisuallyHidden uses position:absolute, width:1px, height:1px, and overflow:hidden. Content inside it is removed from layout flow. If layout impact is needed, the content should be visible. [id: visuallyhidden.not-layout-affecting; testability: manual] ### Content - **VisuallyHidden text** MUST start with a space character when appended to visible text within the same element. Rationale: Without a leading space, some screen readers concatenate the visible and hidden text without a pause, producing run-together announcements such as "Find out moreabout the Economy 7 tariff". [id: visuallyhidden.leading-space; testability: manual] - **VisuallyHidden text** SHOULD be concise and add only the context that is missing for screen reader users. Rationale: Verbose hidden text creates unnecessarily long announcements. Include only what a sighted user would infer from visual context. [id: visuallyhidden.concise; testability: manual] ### Composition - **VisuallyHidden** MUST NOT contain interactive elements such as links, buttons, or inputs. Rationale: Interactive elements inside VisuallyHidden are keyboard-focusable but not visible, creating a confusing focus experience for sighted keyboard users. [id: visuallyhidden.no-interactive-children; testability: auto] ## Accessibility ### In brief Visually Hidden is a `span` that is moved off the screen but stays in the page, so a screen reader reads its text and a sighted user does not see it. It does not take focus and adds no role. ### What Nebula provides - The text is hidden by clipping it to a 1 pixel box, not with `display: none`, so it stays in the accessibility tree. - Nebula uses it itself, for the "(opens in new window)" message in Text Link and the character limit message in Field. ### Accessible design #### Don't use hidden text in place of a visible label A sighted user, and a person using voice control, can only use what they see. Hidden text is for context the visible text cannot carry, such as which item a repeated "Edit" link belongs to. WCAG: [2.5.3 Label in Name](https://www.w3.org/WAI/WCAG22/Understanding/label-in-name.html) [3.3.2 Labels or Instructions](https://www.w3.org/WAI/WCAG22/Understanding/labels-or-instructions.html) #### Keep the hidden text short and add what the visible text lacks Repeating the visible words makes a screen reader say them twice. Write only the extra context. ### Accessible development #### Start the hidden text with a space when it follows visible text The hidden text sits inline after the visible words, so the space keeps the two apart. Text Link does the same for its "(opens in new window)" message. WCAG: [1.3.1 Info and Relationships](https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html) #### Never put a link, a button or an input inside Visually Hidden The clipping hides the box, not the tab stop. A focusable element inside it can still be reached with `Tab` while nothing shows where focus is. WCAG: [2.4.7 Focus Visible](https://www.w3.org/WAI/WCAG22/Understanding/focus-visible.html) ### Accessibility testing status Reviewed on 9 October 2026 against v17.0.1. | Test | Status | Tested with | | --- | --- | --- | | Default state | Not tested | — | | Advanced states | Not tested | — | | Keyboard navigation | Manually tested | keyboard (reported by the owner) | | Screen reader | Partially tested | VoiceOver with Chrome (reported by the owner) | What each test covers: - Default state: The component as it first appears, before anything is clicked or changed. - Advanced states: Every other state and variant: error, open, selected, each size and style, and hovering. - Keyboard navigation: Whether every function can be reached and used with the keyboard alone, with a visible focus ring. - Screen reader: Whether a screen reader announces what is on screen correctly: the name, the role and any state. What these statuses mean: - Tested: Every check in this row passed in an automated run that repeats on every pull request. - Manually tested: Every check in this row was run once, by a person or with a tool they started, and all of them passed. It is not re-run on every change. - Partially tested: Some checks in this row were run once and passed; the rest are still to do. - Issue found: A check in this row failed. The ticket is shown beside the status. - Not tested: No check in this row has been run yet. - Not applicable: This row does not apply to the component, for the reason given beside it. The table records which checks were run on this component on its own. It is not a statement of WCAG conformance, and it does not cover how the component is used. ## Properties ### VisuallyHidden Also accepts all props from `HTMLAttributes`. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ## Styles Reference only; you do not need this to use the component. This is the CSS its styled elements use, with theme tokens shown by name. `${...}` is an interpolation (a theme token, a mixin or another value from the code). A `&[prop]` or `@when ` block holds CSS that applies only under that condition; these are notation, not valid CSS. ### visually-hidden ```css border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; white-space: nowrap; width: 1px; ```