Skip to main content

AccessibilityProvider

The AccessibilityProvider component provides comprehensive accessibility features and WCAG 2.1 AA compliance for React applications. It automatically detects system preferences, manages keyboard navigation, provides screen reader support, and includes development-time accessibility auditing.

Features

  • System Preference Detection - Automatically detects high contrast, reduced motion preferences
  • Keyboard Navigation - Enhanced focus indicators and keyboard user detection
  • Screen Reader Support - ARIA live announcements and proper semantic markup
  • Accessibility Auditing - Development-time issue detection and reporting
  • WCAG 2.1 AA Compliance - Meets accessibility standards out of the box
  • Zero Configuration - Works automatically with sensible defaults

Automatic Integration

AccessibilityProvider is automatically included in all QwickApp instances. No additional setup required!

// AccessibilityProvider is automatically applied
<QwickApp appName="My App">
<MyComponent /> {/* Automatically has accessibility features */}
</QwickApp>

Standalone Usage

For use outside of QwickApp:

import { AccessibilityProvider, useAccessibility } from '@qwickapps/react-framework';

function App() {
return (
<AccessibilityProvider enableAudit={true}>
<MyAccessibleComponent />
</AccessibilityProvider>
);
}

function MyAccessibleComponent() {
const { announce, highContrast, isKeyboardUser } = useAccessibility();

return (
<div>
{highContrast && <p>High contrast mode enabled</p>}
<button onClick={() => announce('Action completed')}>
Complete Action
</button>
</div>
);
}

useAccessibility Hook

Access accessibility features and state through the hook:

import { useAccessibility } from '@qwickapps/react-framework';

function MyComponent() {
const {
// System preferences
highContrast,
reducedMotion,
largeText,

// User interaction
isKeyboardUser,
focusVisible,

// Announcements
announce,
announcePolite,
announceAssertive,

// Controls
setHighContrast,
setReducedMotion,
setLargeText,

// Auditing
issues,
runAudit,
clearIssues
} = useAccessibility();

return (
<div>
<button
onClick={() => announce('Settings saved successfully')}
style={{
fontSize: largeText ? '1.2em' : '1em',
animation: reducedMotion ? 'none' : 'pulse 2s infinite'
}}
>
Save Settings
</button>

{issues.length > 0 && (
<div>Found {issues.length} accessibility issues</div>
)}
</div>
);
}

System Preferences

AccessibilityProvider automatically detects and responds to user system preferences:

High Contrast Mode

const { highContrast, setHighContrast } = useAccessibility();

// Automatically detected from system
// Can be manually controlled
<button
onClick={() => setHighContrast(!highContrast)}
className={highContrast ? 'high-contrast' : ''}
>
Toggle High Contrast
</button>

Reduced Motion

const { reducedMotion, setReducedMotion } = useAccessibility();

// Respects user's motion preferences
<div
style={{
animation: reducedMotion ? 'none' : 'fadeIn 0.3s ease-in'
}}
>
Content with respectful animations
</div>

Large Text

const { largeText, setLargeText } = useAccessibility();

// Enlarges text for better readability
<div style={{ fontSize: largeText ? '1.2em' : '1em' }}>
Resizable text content
</div>

Screen Reader Support

ARIA Live Announcements

Provide important updates to screen reader users:

const { announce, announcePolite, announceAssertive } = useAccessibility();

// Polite announcements (default)
announce('Form saved successfully');
announcePolite('Loading complete');

// Urgent announcements
announceAssertive('Error: Please fix required fields');

Announcement Levels:

  • Polite - Announced when screen reader is idle
  • Assertive - Interrupts current screen reader activity

Live Regions

AccessibilityProvider automatically creates ARIA live regions:

  • aria-live="polite" for non-urgent announcements
  • aria-live="assertive" for urgent announcements

Keyboard Navigation

Enhanced Focus Indicators

Automatically provides enhanced focus styles for keyboard users:

/* Automatically applied when using keyboard navigation */
.keyboard-user *:focus {
outline: 3px solid #005cee !important;
outline-offset: 2px !important;
}

.high-contrast *:focus {
outline: 3px solid #ffffff !important;
box-shadow: 0 0 0 1px #000000 !important;
}

Keyboard User Detection

const { isKeyboardUser } = useAccessibility();

// Automatically detects Tab key usage
// Switches back to mouse mode on mouse interaction
<div className={isKeyboardUser ? 'keyboard-user' : 'mouse-user'}>
Content with appropriate interaction styles
</div>

Accessibility Auditing

Development Mode Auditing

Automatically audits your application for common accessibility issues:

// Audit runs automatically in development mode
// Or manually enable/disable
<AccessibilityProvider enableAudit={true}>
<App />
</AccessibilityProvider>

Manual Auditing

const { runAudit, issues, clearIssues } = useAccessibility();

function AccessibilityPanel() {
return (
<div>
<button onClick={runAudit}>Run Accessibility Audit</button>
<button onClick={clearIssues}>Clear Issues</button>

{issues.length > 0 && (
<div>
<h3>Accessibility Issues ({issues.length})</h3>
{issues.map((issue, index) => (
<div key={index} className={`issue-${issue.level}`}>
<strong>{issue.type}:</strong> {issue.message}
</div>
))}
</div>
)}
</div>
);
}

Common Issues Detected

  • Missing alt attributes on images
  • Unlabeled form inputs
  • Buttons without accessible names
  • Missing ARIA labels
  • Poor color contrast (coming soon)

Props

PropTypeDefaultDescription
childrenReactNode-Child components
enableAuditbooleandevelopment modeEnable accessibility auditing

Accessibility State

The hook provides access to the complete accessibility state:

interface AccessibilityState {
highContrast: boolean; // High contrast mode enabled
reducedMotion: boolean; // Reduced motion preference
largeText: boolean; // Large text mode enabled
focusVisible: boolean; // Focus indicators visible
isKeyboardUser: boolean; // User is using keyboard navigation
issues: AccessibilityIssue[];// Current accessibility issues
lastAnnouncement: Announcement | null; // Last screen reader announcement
}

CSS Classes

AccessibilityProvider automatically applies CSS classes to the body:

  • .keyboard-user - When user is navigating with keyboard
  • .high-contrast - When high contrast mode is enabled
  • .reduced-motion - When reduced motion is preferred
  • .large-text - When large text mode is enabled

Best Practices

  1. Use Semantic HTML - Start with proper HTML elements
  2. Provide Alt Text - Always include alt attributes for images
  3. Label Form Controls - Associate labels with form inputs
  4. Announce State Changes - Use announcements for dynamic content
  5. Respect User Preferences - Honor system accessibility settings
  6. Test with Keyboard - Ensure all functionality is keyboard accessible
  7. Run Regular Audits - Use the built-in auditing during development

Examples

Form with Accessibility Features

import { useAccessibility } from '@qwickapps/react-framework';

function AccessibleForm() {
const { announce, isKeyboardUser } = useAccessibility();
const [errors, setErrors] = useState([]);

const handleSubmit = async (data) => {
try {
await saveForm(data);
announce('Form saved successfully');
} catch (error) {
setErrors(error.fields);
announce('Please fix the form errors', 'assertive');
}
};

return (
<form onSubmit={handleSubmit}>
<label htmlFor="email">
Email Address
{errors.email && <span className="error" aria-live="polite">{errors.email}</span>}
</label>
<input
id="email"
type="email"
required
aria-describedby={errors.email ? "email-error" : undefined}
/>

<button
type="submit"
className={isKeyboardUser ? 'keyboard-focus' : ''}
>
Save Form
</button>
</form>
);
}

Dynamic Content Updates

function DataDashboard() {
const { announce } = useAccessibility();
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);

const refreshData = async () => {
setLoading(true);
announce('Loading data...');

try {
const newData = await fetchData();
setData(newData);
announce(`Data updated. Showing ${newData.length} items.`);
} catch (error) {
announce('Failed to load data. Please try again.', 'assertive');
} finally {
setLoading(false);
}
};

return (
<div>
<button onClick={refreshData} disabled={loading}>
Refresh Data
</button>

<div role="status" aria-live="polite">
{loading ? 'Loading...' : `${data?.length || 0} items`}
</div>
</div>
);
}

High Contrast Theme

function ThemeAwareComponent() {
const { highContrast, reducedMotion } = useAccessibility();

return (
<div
className={`
component
${highContrast ? 'high-contrast-theme' : ''}
${reducedMotion ? 'reduced-motion' : ''}
`}
style={{
backgroundColor: highContrast ? '#000000' : '#ffffff',
color: highContrast ? '#ffffff' : '#000000',
transition: reducedMotion ? 'none' : 'all 0.3s ease'
}}
>
Content that adapts to accessibility preferences
</div>
);
}