Decorative by default
Icons render with aria-hidden="true" and no role. This is correct when the icon appears alongside text that already describes the action — like a button label.
More
Every StateGlyph icon ships with built-in ARIA support, reduced-motion handling, and predictable focus behavior.
Icons render with aria-hidden="true" and no role. This is correct when the icon appears alongside text that already describes the action — like a button label.
Set decorative={false} and the icon gains role="img" and an aria-label derived from the state definition. Override with the label prop when needed.
Every SVG renders with focusable="false" to prevent unexpected focus outlines in IE/Edge legacy and assistive technology.
Continuous states (like loading spinners) use CSS animations that respect prefers-reduced-motion. When enabled, animations are effectively disabled.
When an icon appears next to a text label that already describes the action, keep the default decorative={true}. The icon is hidden from the accessibility tree:
// Decorative — the default// The icon is purely visual; nearby text already// conveys the meaning.<UploadStateIcon state="idle" />
// Equivalent to:<UploadStateIcon state="idle" decorative={true} />
// Rendered SVG output:// <svg aria-hidden="true" focusable="false" ... />When the icon is the only visual indicator of state — for example, a standalone status icon — set decorative={false}. Screen readers will announce the state:
// Semantic — the icon conveys information// Use when the icon is the only indicator of state.<UploadStateIcon state="success" decorative={false}/>
// Rendered SVG output:// <svg role="img" aria-label="Upload complete" focusable="false" ... />
// Custom label override:<UploadStateIcon state="success" decorative={false} label="File uploaded successfully"/><button>Upload</button> — the button text already conveys meaning.Some states are marked continuous: true (e.g. a loading spinner). These use CSS animations that are automatically disabled when the user enables the prefers-reduced-motion setting:
/* StateGlyph docs include this — add it to your project */@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }}StateGlyph outputs data-state-icon and data-state attributes on every SVG. Use these with Testing Library or similar tools:
// Test with data attributesconst icon = screen.getByRole("img", { name: "Upload complete",});expect(icon).toHaveAttribute("data-state", "success");expect(icon).toHaveAttribute("data-state-icon", "upload");