Enhanced Accessibility Mode Implementation
Enhanced accessibility mode is a toggle that applies a set of additional interface improvements: enlarged font, increased contrast, simplified animation, improved focus indicators. It allows avoiding forcing changes on all users while giving choice to those who need it.
Accessibility Mode Components
Typical set of options:
- High contrast theme (7:1+ contrast)
- Enlarged font (+2–4px base size)
- Animation disabled (
prefers-reduced-motion) - Enlarged click targets
- More prominent focus indicator
- Simplified layout (single column)
CSS Variables as Foundation
/* Base variables */
:root {
--font-size-base: 16px;
--color-text: #333333;
--color-bg: #ffffff;
--color-accent: #0056b3;
--focus-outline: 2px solid #0056b3;
--focus-outline-offset: 2px;
--transition-duration: 300ms;
--min-touch-target: 44px;
}
/* High contrast mode */
[data-accessibility-mode~="high-contrast"] {
--color-text: #000000;
--color-bg: #ffffff;
--color-accent: #0000CC;
--focus-outline: 3px solid #000000;
--focus-outline-offset: 4px;
}
/* Enlarged font */
[data-accessibility-mode~="large-text"] {
--font-size-base: 20px;
}
/* Reduced animation */
[data-accessibility-mode~="reduced-motion"],
@media (prefers-reduced-motion: reduce) {
--transition-duration: 0ms;
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
/* Enlarged touch targets */
[data-accessibility-mode~="large-targets"] {
--min-touch-target: 56px;
a, button, [role="button"] {
min-height: var(--min-touch-target);
min-width: var(--min-touch-target);
display: inline-flex;
align-items: center;
}
}
React Component for Mode Control
type AccessibilityMode = 'high-contrast' | 'large-text' | 'reduced-motion' | 'large-targets';
function useAccessibilityMode() {
const [modes, setModes] = useState<Set<AccessibilityMode>>(() => {
const stored = localStorage.getItem('a11y-modes');
return stored ? new Set(JSON.parse(stored)) : new Set();
});
// Respect system settings
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
if (mediaQuery.matches) {
setModes(prev => new Set([...prev, 'reduced-motion']));
}
}, []);
const toggle = useCallback((mode: AccessibilityMode) => {
setModes(prev => {
const next = new Set(prev);
next.has(mode) ? next.delete(mode) : next.add(mode);
localStorage.setItem('a11y-modes', JSON.stringify([...next]));
return next;
});
}, []);
// Apply to document.documentElement
useEffect(() => {
document.documentElement.dataset.accessibilityMode = [...modes].join(' ');
}, [modes]);
return { modes, toggle };
}
function AccessibilityPanel() {
const { modes, toggle } = useAccessibilityMode();
const [isOpen, setIsOpen] = useState(false);
const options = [
{ id: 'high-contrast' as const, label: 'High contrast', icon: '◑' },
{ id: 'large-text' as const, label: 'Enlarged text', icon: 'A+' },
{ id: 'reduced-motion' as const, label: 'Less animation', icon: '⏸' },
{ id: 'large-targets' as const, label: 'Large buttons', icon: '⊞' },
];
return (
<div className="accessibility-widget">
<button
aria-expanded={isOpen}
aria-controls="a11y-panel"
onClick={() => setIsOpen(!isOpen)}
aria-label="Accessibility settings"
>
♿
</button>
{isOpen && (
<div id="a11y-panel" role="group" aria-label="Accessibility settings">
{options.map(opt => (
<label key={opt.id} className="a11y-option">
<input
type="checkbox"
checked={modes.has(opt.id)}
onChange={() => toggle(opt.id)}
/>
<span className="icon">{opt.icon}</span>
{opt.label}
</label>
))}
<button
onClick={() => {
localStorage.removeItem('a11y-modes');
setModes(new Set());
}}
>
Reset settings
</button>
</div>
)}
</div>
);
}
System Media Queries (no JS)
Some settings apply automatically from system settings:
/* Automatically for users who selected animation reduction in OS */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* System high contrast theme */
@media (prefers-contrast: more) {
:root {
--color-text: #000;
--color-bg: #fff;
--focus-outline: 3px solid #000;
}
}
/* Dark theme */
@media (prefers-color-scheme: dark) {
:root {
--color-text: #f0f0f0;
--color-bg: #1a1a1a;
}
}
Widget Positioning
Accessibility widget is usually placed in fixed position:
.accessibility-widget {
position: fixed;
bottom: 24px;
right: 24px;
z-index: 9000;
}
Important: the widget itself must be keyboard accessible — otherwise users who need it won't be able to use it.
Timeline
3–5 days: CSS variable system + React component + testing each mode with screen reader.







