Accessibility features in sensory-ui: reduced motion, keyboard support, and inclusive design.
sensory-ui is designed with accessibility as a core principle. Sound enhances the experience but never replaces visual feedback.
sensory-ui respects the user's prefers-reduced-motion setting. When enabled, all sounds are suppressed.
// sensory.config.js
module.exports = {
reducedMotion: "inherit", // Default
};| Option | Behavior |
|---|---|
"inherit" | Respect the OS/browser prefers-reduced-motion setting |
"force-off" | Always suppress sounds, regardless of user preference |
"force-on" | Always play sounds, ignore prefers-reduced-motion |
The provider reads prefers-reduced-motion as a media query on mount and updates on change:
function useReducedMotion(pref: SensoryUIConfig["reducedMotion"]): boolean {
const [matches, setMatches] = React.useState(() => {
if (typeof window === "undefined") return false;
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
});
React.useEffect(() => {
if (typeof window === "undefined") return;
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
const handler = (e: MediaQueryListEvent) => setMatches(e.matches);
mq.addEventListener("change", handler);
return () => mq.removeEventListener("change", handler);
}, []);
if (pref === "force-off") return true;
if (pref === "force-on") return false;
return matches;
}Test reduced motion by enabling "Reduce motion" in your OS accessibility settings. Chrome DevTools also has a "Emulate CSS media feature prefers-reduced-motion" option.
Disable all sounds without removing any code:
// sensory.config.js
module.exports = {
enabled: false,
};Or programmatically:
<SensoryUIProvider config={{ enabled: false }}>
{children}
</SensoryUIProvider>Disable specific categories without affecting others:
module.exports = {
categories: {
interaction: true,
navigation: true,
notification: true,
overlay: false, // No dialog/sheet sounds
hero: false, // Disabled by default
},
};Silence individual components with the sound={false} prop:
<Button sound={false}>No sound</Button>All sensory-ui components maintain the same keyboard support as their shadcn/ui counterparts. Sounds trigger from:
Sounds never fire from lifecycle methods, effects, or programmatic state changes. They only fire from direct user interaction events (pointer events, keyboard events).
Every audio cue has a visual equivalent:
| Sound | Visual Feedback |
|---|---|
interaction.tap | Button press state |
interaction.toggle | Checkbox/switch state change |
navigation.tab | Tab active state |
overlay.open | Dialog/sheet appearing |
notification.success | Toast appearing |
hero.complete | Completion animation |
Sound enhances, never replaces.
prefers-reduced-motion: reduce in browser dev toolssound={false} silences individual componentsenabled: false suppresses all soundsA maximally careful setup with every accessibility lever in one place:
<SensoryUIProvider
config={{
enabled: true,
volume: 0.3,
theme: "soft",
categories: { hero: false }, // celebrations off unless earned
reducedMotion: "inherit", // always respect the OS setting
}}
>
{children}
</SensoryUIProvider>For command-level verification (type checks, registry validation, rapid-fire overlap tests), see Testing.
See MDN: prefers-reduced-motion for browser support and usage details.