Patterns
Icon-Only Control Label
When to use:
- a control is visually icon-only but still needs an accessible name
Composition notes:
- keep the hidden label concise
- prefer one clear hidden phrase
Trade-offs:
- preserves compact visuals
- visible clarity still depends on icon recognition
import { Icon, IconButton, VisuallyHidden } from '@vega-ui/react';
import { BellIcon } from '@vega-ui/icons';
import { useState } from 'react';
export default function IconOnlyControlLabel() {
const [unread, setUnread] = useState(4);
return (
<IconButton type='button' variant='secondary' appearance='ghost' onClick={() => setUnread(0)} style={{ position: 'relative' }}>
<Icon><BellIcon /></Icon>
<VisuallyHidden asChild>
<span>Notifications, {unread} unread</span>Hidden ARIA Description
When to use:
- a field needs extra assistive-only context
Composition notes:
- give the hidden node an id
- reference it from the control with
aria-describedby
Trade-offs:
- flexible and reusable
- easy to forget to update the hidden copy
import { Button, Label, TextField, TextFieldInput, VisuallyHidden } from '@vega-ui/react';
import { FormEvent, useState } from 'react';
export default function HiddenHelperDescription() {
const [budget, setBudget] = useState('');
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
setBudget(String(new FormData(event.currentTarget).get('budget')));
};
return (Hidden Status Copy
When to use:
- visual loading or status UI needs assistive-only text
Composition notes:
- keep the text synchronized with the real state
- do not duplicate misleading stale messages
Trade-offs:
- improves non-visual clarity
- state drift is easy if the parent logic is sloppy
import { Button, Icon, VisuallyHidden } from '@vega-ui/react';
import { ShoppingCartIcon } from '@vega-ui/icons';
import { useState } from 'react';
const products = ['Linen shirt', 'Canvas tote', 'Wool socks'];
export default function HiddenStatusText() {
const [cart, setCart] = useState<string[]>([]);
const [announcement, setAnnouncement] = useState('');
const add = (product: string) => {
const next = [...cart, product];Comparison
Quick Decision Rule
Use VisuallyHidden when content should stay available to assistive technology but not appear visually. Use normal text when the information should be visible to everyone.
VisuallyHidden vs visible helper text
- Use
VisuallyHiddenfor accessibility-only content. - Use visible helper text when the guidance benefits all users.
VisuallyHidden vs aria-label
- Use
VisuallyHiddenwhen you want real DOM content that can be referenced or composed. - Use
aria-labelfor short direct labels when no extra content node is needed.
Choose This Component When
- content should remain accessible but not visible
- a hidden description or label node is useful
Do Not Choose This Component When
- the information should be visible in the normal UI
- hidden text is being used to avoid fixing visible UX
Troubleshooting
The Hidden Text Sounds Wrong Or Outdated
Symptom
Assistive technology users receive copy that no longer matches the visible UI.
Likely Cause
The hidden text was not updated together with the visible state.
How To Verify
- compare the hidden string with the current visible state transitions
Fix
Drive the hidden content from the same state as the visible UI.
The UI Still Feels Unclear Even With Hidden Text
Symptom
Accessibility improves slightly, but the visible UI still feels weak or ambiguous.
Likely Cause
VisuallyHidden is being used to compensate for poor visible labeling.
How To Verify
- review the UI without assistive-only content
Fix
Improve visible labels first and keep hidden text only where it adds real value.
Hidden Content Unexpectedly Reappears
Symptom
The supposedly hidden content becomes visible in certain layouts or overrides.
Likely Cause
The visually hidden CSS was overridden.
How To Verify
- inspect computed styles for clipping and positioning rules
Fix
Keep the standard visually hidden CSS contract intact and avoid overriding it casually.