|
| 1 | +# Accessibility Guide for VueJS Tour |
| 2 | + |
| 3 | +## Current State |
| 4 | + |
| 5 | +VueJS Tour now includes comprehensive WCAG 2.1 AA accessibility features, including keyboard navigation, ARIA attributes, focus management, and screen reader support. |
| 6 | + |
| 7 | +**⚠️ Note:** Accessibility features are **disabled by default** (as of v2.5.0) pending further testing and validation. Enable them by setting `enableA11y: true` on the component. |
| 8 | + |
| 9 | +## Implemented Accessibility Features |
| 10 | + |
| 11 | +### 1. **ARIA Attributes** ✅ |
| 12 | + |
| 13 | +Implemented: |
| 14 | + |
| 15 | +- ✅ `aria-label` on tooltip (configurable via `ariaLabel` prop or per-step `ariaLabel`) |
| 16 | +- ✅ `aria-describedby` for step content |
| 17 | +- ✅ `aria-live="polite"` region for step announcements |
| 18 | +- ✅ `aria-modal="true"` when `enableA11y` is enabled |
| 19 | +- ✅ `role="dialog"` for proper semantic structure |
| 20 | +- ✅ Enhanced `aria-label` attributes on all buttons |
| 21 | + |
| 22 | +### 2. **Keyboard Navigation** ✅ |
| 23 | + |
| 24 | +Implemented: |
| 25 | + |
| 26 | +- ✅ `Escape` key to close tour |
| 27 | +- ✅ `ArrowRight` or `Enter` to go to next step |
| 28 | +- ✅ `ArrowLeft` to go to previous step |
| 29 | +- ✅ Keyboard navigation can be disabled via `keyboardNav: false` |
| 30 | +- ⚠️ Focus trap not yet implemented (planned for future release) |
| 31 | + |
| 32 | +### 3. **Focus Management** ✅ |
| 33 | + |
| 34 | +Implemented: |
| 35 | + |
| 36 | +- ✅ Tooltip receives focus when opened (when `enableA11y` is true) |
| 37 | +- ✅ Previous focus restored when tour ends |
| 38 | +- ✅ `tabindex="0"` on tooltip for keyboard accessibility |
| 39 | + |
| 40 | +### 4. **Screen Reader Support** ✅ |
| 41 | + |
| 42 | +Implemented: |
| 43 | + |
| 44 | +- ✅ Step progress announced ("Step 2 of 5") |
| 45 | +- ✅ Descriptive button labels with step context |
| 46 | +- ✅ Content changes announced via aria-live region |
| 47 | +- ✅ Screen reader only content via `.vjt-sr-only` CSS class |
| 48 | + |
| 49 | +### 5. **Semantic Structure** ✅ |
| 50 | + |
| 51 | +Implemented: |
| 52 | + |
| 53 | +- ✅ `role="dialog"` on tooltip (changed from `role="tooltip"` for better modal semantics) |
| 54 | +- ✅ Step counter announced to screen readers |
| 55 | +- ✅ Proper ARIA attributes on all interactive elements |
| 56 | + |
| 57 | +## Usage |
| 58 | + |
| 59 | +### Enabling Accessibility Features |
| 60 | + |
| 61 | +Accessibility features are disabled by default. To enable them: |
| 62 | + |
| 63 | +```vue |
| 64 | +<VTour |
| 65 | + :steps="steps" |
| 66 | + :enableA11y="true" |
| 67 | + :keyboardNav="true" |
| 68 | + ariaLabel="Product tour" |
| 69 | +/> |
| 70 | +``` |
| 71 | + |
| 72 | +### Available Props |
| 73 | + |
| 74 | +```typescript |
| 75 | +interface VTourProps { |
| 76 | + // ... other props |
| 77 | + |
| 78 | + /** Enable accessibility features (default: false) */ |
| 79 | + readonly enableA11y?: boolean; |
| 80 | + |
| 81 | + /** Enable keyboard navigation (default: true, only active when enableA11y is true) */ |
| 82 | + readonly keyboardNav?: boolean; |
| 83 | + |
| 84 | + /** Custom aria-label for the tour (default: "Guided tour") */ |
| 85 | + readonly ariaLabel?: string; |
| 86 | +} |
| 87 | + |
| 88 | +interface ITourStep { |
| 89 | + // ... other props |
| 90 | + |
| 91 | + /** Descriptive label for screen readers */ |
| 92 | + readonly ariaLabel?: string; |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +### Example: Per-Step Accessibility Labels |
| 97 | + |
| 98 | +```vue |
| 99 | +<script setup> |
| 100 | +const steps = [ |
| 101 | + { |
| 102 | + target: '#welcome', |
| 103 | + content: 'Welcome to our app!', |
| 104 | + ariaLabel: 'Welcome step: Introduction to the application', |
| 105 | + }, |
| 106 | + { |
| 107 | + target: '#profile', |
| 108 | + content: 'View your profile here', |
| 109 | + ariaLabel: 'Profile step: Learn about your user profile', |
| 110 | + }, |
| 111 | +]; |
| 112 | +</script> |
| 113 | +
|
| 114 | +<template> |
| 115 | + <VTour :steps="steps" :enableA11y="true" /> |
| 116 | +</template> |
| 117 | +``` |
| 118 | + |
| 119 | +## Implementation Details |
| 120 | + |
| 121 | +### Features Already Implemented ✅ |
| 122 | + |
| 123 | +1. **ARIA Live Region** - Announces step changes to screen readers |
| 124 | + |
| 125 | + ```vue |
| 126 | + <div |
| 127 | + v-if="enableA11y" |
| 128 | + role="status" |
| 129 | + aria-live="polite" |
| 130 | + aria-atomic="true" |
| 131 | + class="vjt-sr-only" |
| 132 | + > |
| 133 | + Step {{ currentStepIndex + 1 }} of {{ props.steps.length }} |
| 134 | + </div> |
| 135 | + ``` |
| 136 | + |
| 137 | +2. **Enhanced Tooltip Semantics** - Proper dialog role and ARIA attributes |
| 138 | + |
| 139 | + ```vue |
| 140 | + <div |
| 141 | + :id="tooltipId" |
| 142 | + role="dialog" |
| 143 | + :aria-modal="enableA11y ? 'true' : undefined" |
| 144 | + :aria-label="getCurrentStep?.ariaLabel || ariaLabel" |
| 145 | + :aria-describedby="`${tooltipId}-content`" |
| 146 | + :tabindex="enableA11y ? '0' : undefined" |
| 147 | + /> |
| 148 | + ``` |
| 149 | + |
| 150 | +3. **Keyboard Navigation** - Arrow keys, Enter, and Escape support |
| 151 | + |
| 152 | + ```typescript |
| 153 | + const onKeydown = (event: KeyboardEvent): void => { |
| 154 | + if (!tourVisible.value || !props.enableA11y || !props.keyboardNav) return; |
| 155 | + |
| 156 | + switch (event.key) { |
| 157 | + case 'Escape': |
| 158 | + endTour(); |
| 159 | + event.preventDefault(); |
| 160 | + break; |
| 161 | + case 'ArrowRight': |
| 162 | + case 'Enter': |
| 163 | + nextStep(); |
| 164 | + event.preventDefault(); |
| 165 | + break; |
| 166 | + case 'ArrowLeft': |
| 167 | + if (currentStepIndex.value > 0) { |
| 168 | + lastStep(); |
| 169 | + event.preventDefault(); |
| 170 | + } |
| 171 | + break; |
| 172 | + } |
| 173 | + }; |
| 174 | + ``` |
| 175 | + |
| 176 | +4. **Focus Management** - Stores and restores focus |
| 177 | + |
| 178 | + ```typescript |
| 179 | + let previousFocus: HTMLElement | null = null; |
| 180 | + |
| 181 | + const startTour = async () => { |
| 182 | + if (props.enableA11y && typeof document !== 'undefined') { |
| 183 | + previousFocus = document.activeElement as HTMLElement; |
| 184 | + } |
| 185 | + // ... tour starts |
| 186 | + if (props.enableA11y) { |
| 187 | + await nextTick(); |
| 188 | + _Tooltip.value?.focus(); |
| 189 | + } |
| 190 | + }; |
| 191 | + |
| 192 | + const stopTour = () => { |
| 193 | + if (props.enableA11y && previousFocus) { |
| 194 | + previousFocus.focus(); |
| 195 | + previousFocus = null; |
| 196 | + } |
| 197 | + }; |
| 198 | + ``` |
| 199 | + |
| 200 | +5. **Enhanced Button Labels** - Descriptive aria-labels for all actions |
| 201 | + |
| 202 | + ```vue |
| 203 | + <button |
| 204 | + type="button" |
| 205 | + @click.prevent="nextStep" |
| 206 | + :aria-label=" |
| 207 | + enableA11y |
| 208 | + ? isLastStep |
| 209 | + ? 'Finish tour' |
| 210 | + : `Go to next step, step ${nextStepIndex + 1} of ${props.steps.length}` |
| 211 | + : undefined |
| 212 | + " |
| 213 | + > |
| 214 | + {{ getNextLabel }} |
| 215 | + </button> |
| 216 | + ``` |
| 217 | + |
| 218 | +### Future Enhancements ⚠️ |
| 219 | + |
| 220 | +These features are planned for future releases: |
| 221 | + |
| 222 | +1. **Focus Trap** - Trap focus within modal when backdrop is active |
| 223 | +2. **Customizable Keyboard Shortcuts** - Allow users to configure key bindings |
| 224 | +3. **Visual Progress Indicators** - Show step progress visually |
| 225 | +4. **Skip to Content** - Quick navigation option |
| 226 | + |
| 227 | +## Backward Compatibility |
| 228 | + |
| 229 | +All accessibility features: |
| 230 | + |
| 231 | +- ⚠️ Default to **disabled** (`enableA11y: false`) as of v2.4.3 pending further testing |
| 232 | +- Are opt-in via `enableA11y: true` prop |
| 233 | +- Do not break existing implementations when disabled |
| 234 | +- Add ~3KB to bundle size (minimal impact) |
| 235 | + |
| 236 | +## Testing Requirements |
| 237 | + |
| 238 | +1. **Keyboard-only navigation** |
| 239 | + - Can navigate all steps without mouse |
| 240 | + - Can dismiss tour with Escape |
| 241 | + - Focus visible at all times |
| 242 | + |
| 243 | +2. **Screen reader testing** |
| 244 | + - NVDA (Windows) |
| 245 | + - JAWS (Windows) |
| 246 | + - VoiceOver (macOS/iOS) |
| 247 | + - TalkBack (Android) |
| 248 | + |
| 249 | +3. **Automated testing** |
| 250 | + - axe-core integration |
| 251 | + - ARIA validity checks |
| 252 | + - Keyboard interaction tests |
| 253 | + |
| 254 | +## Resources |
| 255 | + |
| 256 | +- [WAI-ARIA Authoring Practices Guide - Dialog](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/) |
| 257 | +- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/) |
| 258 | +- [focus-trap library](https://github.com/focus-trap/focus-trap) |
| 259 | +- [Vue A11y Best Practices](https://vue-a11y.com/) |
| 260 | + |
| 261 | +## Testing Status |
| 262 | + |
| 263 | +**Implemented (v2.4.3):** ✅ |
| 264 | + |
| 265 | +- ✅ Keyboard navigation tests (28 test cases) |
| 266 | +- ✅ ARIA attributes validation |
| 267 | +- ✅ Focus management tests |
| 268 | +- ✅ Screen reader announcement tests |
| 269 | + |
| 270 | +**Pending:** |
| 271 | + |
| 272 | +1. **Manual testing with screen readers** |
| 273 | + - NVDA (Windows) |
| 274 | + - JAWS (Windows) |
| 275 | + - VoiceOver (macOS/iOS) |
| 276 | + - TalkBack (Android) |
| 277 | + |
| 278 | +2. **Real-world validation** |
| 279 | + - User testing with keyboard-only users |
| 280 | + - Screen reader user feedback |
| 281 | + - Production environment testing |
| 282 | + |
| 283 | +3. **Automated accessibility testing** |
| 284 | + - axe-core integration |
| 285 | + - Lighthouse accessibility audits |
| 286 | + - pa11y or similar tools |
| 287 | + |
| 288 | +## Status |
| 289 | + |
| 290 | +✅ **Phase 1 (Critical) features implemented** - All essential WCAG AA compliance features are in place and tested. Features are disabled by default pending further validation. |
| 291 | + |
| 292 | +**Next steps:** |
| 293 | + |
| 294 | +1. Community testing and feedback |
| 295 | +2. Screen reader validation |
| 296 | +3. Enable by default once vetted |
| 297 | +4. Implement Phase 2 features (focus trap, custom shortcuts) |
0 commit comments