Accessibility Guide
Overviewβ
The use-typewriter-animation library is designed with accessibility-first principles, ensuring WCAG 2.1 AA compliance and providing an inclusive experience for all users, including those using assistive technologies.
π― Accessibility Featuresβ
β Screen Reader Supportβ
- ARIA live regions for real-time announcements
- Screen reader-only content for full context
- Progressive announcements during typing
- Completion announcements when animation finishes
β Reduced Motion Supportβ
- Automatic detection of
prefers-reduced-motion
preference - Instant text display for users who prefer reduced motion
- Dynamic preference changes - responds to real-time changes
- Cursor animation respect - disables blinking when needed
β Keyboard Navigationβ
- Configurable keyboard shortcuts for full control
- Pause/Resume functionality (default: Space bar)
- Skip animation (default: Escape)
- Reset animation (default: R key)
- Focus management for better keyboard navigation
β ARIA Complianceβ
- Comprehensive ARIA attributes (live, label, describedby, role)
- Semantic roles (status, log, alert, marquee)
- Busy state indicators during active animations
- Atomic announcements for complete context
β High Contrast Supportβ
- System color respect - uses
currentColor
for better contrast - Non-color-dependent indicators for state changes
- Border and background transparency for theme compatibility
π Quick Start (Accessible)β
Basic Accessible Implementationβ
import { useTypewriter } from 'use-typewriter-animation';
function AccessibleTypewriter() {
const { typewriter, elements, cursor, accessibilityProps, screenReaderAnnouncement } =
useTypewriter({
// Basic accessibility settings
ariaLabel: 'Welcome message typewriter',
respectReducedMotion: true,
announceCompletion: true,
});
useEffect(() => {
typewriter
.type('Welcome to our accessible website!', {
screenReaderText: 'Welcome to our accessible website!',
announceCompletion: true,
})
.start();
}, []);
return (
<div {...accessibilityProps}>
{elements}
{cursor}
{screenReaderAnnouncement}
</div>
);
}
Full Accessibility Configurationβ
function FullyAccessibleTypewriter() {
const { typewriter, elements, cursor, accessibilityProps, screenReaderAnnouncement } =
useTypewriter({
// ARIA Configuration
ariaLive: 'polite',
ariaLabel: 'Product announcement typewriter',
role: 'status',
// Reduced Motion Support
respectReducedMotion: true,
reducedMotionFallback: 'instant',
// Keyboard Controls
enableKeyboardControls: true,
autoKeyboardHandling: true,
keyboardShortcuts: {
pause: ['Space', ' '],
resume: ['Space', ' '],
skip: ['Escape', 'Enter'],
reset: ['KeyR', 'r'],
},
// Screen Reader Optimizations
announceCompletion: true,
screenReaderText: 'New product launch: Revolutionary AI Assistant now available!',
// Visual Accessibility
cursorColor: 'currentColor',
});
useEffect(() => {
typewriter
.type('π New Product Launch: ', {
announceCompletion: false,
})
.pauseFor(500)
.type('Revolutionary AI Assistant', {
announceCompletion: false,
})
.pauseFor(500)
.type(' now available!', {
announceCompletion: true,
})
.start();
}, []);
return (
<div
{...accessibilityProps}
tabIndex={0} // Make focusable for keyboard navigation
style={{
padding: '1rem',
border: '2px solid currentColor',
borderRadius: '4px',
backgroundColor: 'transparent',
}}
>
<div style={{ marginBottom: '0.5rem', fontSize: '0.875rem', opacity: 0.7 }}>
Keyboard controls: Space (pause/resume), Escape (skip), R (reset)
</div>
{elements}
{cursor}
{screenReaderAnnouncement}
</div>
);
}
π Accessibility Options Referenceβ
ARIA Configurationβ
Option | Type | Default | Description |
---|---|---|---|
ariaLive | 'polite' | 'assertive' | 'off' | 'polite' | Controls how screen readers announce content |
ariaLabel | string | undefined | Provides accessible name for the component |
ariaDescribedBy | string | undefined | References elements that describe the component |
role | 'status' | 'log' | 'alert' | 'marquee' | 'status' | Semantic role for the component |
Screen Reader Optionsβ
Option | Type | Default | Description |
---|---|---|---|
screenReaderText | string | undefined | Full text provided to screen readers when complete |
announceCompletion | boolean | false | Whether to announce when typing is complete |
Reduced Motion Optionsβ
Option | Type | Default | Description |
---|---|---|---|
respectReducedMotion | boolean | true | Whether to respect user's motion preferences |
reducedMotionFallback | 'instant' | 'none' | 'instant' | How to handle animation when motion is reduced |
Keyboard Navigation Optionsβ
Option | Type | Default | Description |
---|---|---|---|
enableKeyboardControls | boolean | false | Enable keyboard control functionality |
autoKeyboardHandling | boolean | false | Automatically handle keyboard events |
keyboardShortcuts | object | See below | Custom keyboard shortcuts |
manageFocus | boolean | false | Manage focus during animations |
focusOnComplete | boolean | false | Focus component when animation completes |
Default Keyboard Shortcutsβ
{
pause: ['Space', ' '],
resume: ['Space', ' '],
skip: ['Escape', 'Enter'],
reset: ['KeyR', 'r'],
}
ποΈ Accessibility Props Usageβ
The accessibilityProps
object contains all necessary ARIA attributes:
const { accessibilityProps } = useTypewriter({
ariaLabel: 'Status updates',
ariaLive: 'polite',
role: 'status',
});
// Apply to your container
<div {...accessibilityProps}>
{/* Your typewriter content */}
</div>
// Results in:
<div
aria-label="Status updates"
aria-live="polite"
role="status"
aria-busy={true} // Dynamic based on typing state
>
{/* Your typewriter content */}
</div>
π Screen Reader Announcementsβ
The library provides multiple levels of screen reader support:
1. Progressive Announcementsβ
Text is announced as it's being typed (configurable via ariaLive
).
2. Completion Announcementsβ
Full context is provided when animation completes.
3. Status Announcementsβ
Current state (typing, paused, completed) is announced.
4. Keyboard Control Instructionsβ
When keyboard controls are enabled, instructions are provided.
β¨οΈ Keyboard Navigationβ
Default Controlsβ
Key | Action | Description |
---|---|---|
Space | Pause/Resume | Toggle animation playback |
Escape | Skip | Skip to end of current animation |
R | Reset | Reset animation to beginning |
Custom Keyboard Shortcutsβ
const { typewriter } = useTypewriter({
enableKeyboardControls: true,
autoKeyboardHandling: true,
keyboardShortcuts: {
pause: ['KeyP', 'p'],
resume: ['KeyP', 'p'],
skip: ['KeyS', 's'],
reset: ['KeyR', 'r'],
},
});
Manual Keyboard Handlingβ
const { typewriter } = useTypewriter({
enableKeyboardControls: true,
autoKeyboardHandling: false, // Handle manually
});
useEffect(() => {
const handleKeyDown = (event) => {
switch (event.key) {
case ' ':
event.preventDefault();
if (typewriter.isPaused()) {
typewriter.resume();
} else {
typewriter.pause();
}
break;
case 'Escape':
event.preventDefault();
typewriter.skip();
break;
}
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, []); // typewriter object is stable, so we can use empty array
π¨ Reduced Motion Supportβ
Automatic Detectionβ
The library automatically detects the user's prefers-reduced-motion
preference:
const { typewriter } = useTypewriter({
respectReducedMotion: true, // Default: true
reducedMotionFallback: 'instant', // Show text immediately
});
Manual Controlβ
const { typewriter } = useTypewriter({
respectReducedMotion: false, // Ignore user preference
});
Dynamic Changesβ
The library responds to real-time changes in motion preferences:
// User changes system preference while page is open
// Library automatically adjusts behavior
π§ͺ Testing Accessibilityβ
Using the Built-in Test Utilitiesβ
import {
AccessibilityTestUtils,
AccessibilityTestHelpers,
} from 'use-typewriter-animation/test/AccessibilityTest';
// Test basic accessibility compliance
const auditResult = AccessibilityTestUtils.auditAccessibility(containerElement);
console.log(`Accessibility score: ${auditResult.percentage}%`);
// Test with Jest/Vitest
test('typewriter meets accessibility requirements', () => {
const { container } = render(<MyTypewriterComponent />);
AccessibilityTestHelpers.expectAccessibleTypewriter(container);
});
// Test keyboard navigation
test('keyboard navigation works', async () => {
const { container } = render(<MyTypewriterComponent />);
await AccessibilityTestHelpers.testKeyboardNavigation(container);
});
Manual Testing Checklistβ
- Screen reader announces content appropriately
- Reduced motion preference is respected
- Keyboard navigation works as expected
- High contrast mode displays correctly
- Focus management is appropriate
- ARIA attributes are present and correct
π Best Practicesβ
1. Always Provide Contextβ
// β
Good: Descriptive aria-label
ariaLabel: 'Product announcement typewriter';
// β Bad: Generic or missing label
ariaLabel: 'Typewriter';
2. Use Appropriate ARIA Live Regionsβ
// β
Good: Use 'polite' for most announcements
ariaLive: 'polite';
// β
Good: Use 'assertive' for urgent messages
ariaLive: 'assertive';
// β Bad: Overusing 'assertive'
ariaLive: 'assertive'; // for non-urgent content
3. Provide Full Context for Screen Readersβ
// β
Good: Complete text for screen readers
screenReaderText: "Welcome to our accessible website! We're committed to inclusive design.";
// β Bad: Incomplete or missing context
screenReaderText: 'Welcome';
4. Enable Keyboard Controls for Interactive Contentβ
// β
Good: Enable controls for long animations
enableKeyboardControls: true
// β
Good: Make container focusable
<div {...accessibilityProps} tabIndex={0}>
5. Respect User Preferencesβ
// β
Good: Always respect reduced motion
respectReducedMotion: true;
// β
Good: Use system colors for high contrast
cursorColor: 'currentColor';
π§ Troubleshootingβ
Screen Reader Not Announcing Contentβ
- Check that
ariaLive
is not set to'off'
- Ensure
screenReaderAnnouncement
element is rendered - Verify announcements are not empty
- Test with different screen readers (NVDA, JAWS, VoiceOver)
Reduced Motion Not Workingβ
- Check browser support for
prefers-reduced-motion
- Verify system settings have reduced motion enabled
- Ensure
respectReducedMotion
istrue
- Test the
reducedMotionFallback
setting
Keyboard Controls Not Respondingβ
- Verify
enableKeyboardControls
istrue
- Check that container is focusable (
tabIndex={0}
) - Ensure
autoKeyboardHandling
is enabled or manual handling is implemented - Test keyboard shortcut conflicts with browser/system shortcuts
π WCAG 2.1 Complianceβ
This library meets WCAG 2.1 AA standards:
- 1.4.3 Contrast (Minimum): Uses system colors for high contrast support
- 1.4.5 Images of Text: Text-based animations, not image-based
- 2.1.1 Keyboard: Full keyboard navigation support
- 2.2.2 Pause, Stop, Hide: Keyboard controls for animation control
- 2.4.3 Focus Order: Logical focus management
- 3.3.2 Labels or Instructions: Clear keyboard control instructions
- 4.1.2 Name, Role, Value: Comprehensive ARIA implementation
- 4.1.3 Status Messages: Proper use of ARIA live regions
π€ Contributing to Accessibilityβ
We welcome contributions to improve accessibility! Please:
- Test with real assistive technologies
- Follow WCAG 2.1 guidelines
- Include accessibility tests
- Update documentation
- Consider diverse user needs
π Supportβ
For accessibility-related questions or issues:
- Check this guide first
- Test with the built-in accessibility utilities
- Open an issue with detailed reproduction steps
- Include information about assistive technologies used
Remember: Accessibility is not just complianceβit's about creating inclusive experiences for all users. Every user deserves equal access to your content! π