Skip to content

Commit 0e1ae71

Browse files
committed
docs(a11y): add a11y documentation page
1 parent 69f152c commit 0e1ae71

File tree

2 files changed

+304
-0
lines changed

2 files changed

+304
-0
lines changed

docs/.vitepress/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export default defineConfig({
3939
{ text: "Slots", link: "/slots" },
4040
{ text: "Events", link: "/events" },
4141
{ text: "Styling", link: "/styling" },
42+
{ text: "Accessibility", link: "/accessibility" },
4243
{ text: "TypeScript Guide", link: "/typescript" },
4344
],
4445
},

docs/accessibility.md

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
# Accessibility
2+
3+
This component follows the [WAI-ARIA Combobox Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/) to ensure a fully accessible experience for all users, including those using assistive technologies like screen readers.
4+
5+
## Overview
6+
7+
The component implements comprehensive accessibility features including:
8+
9+
- **ARIA attributes** for proper semantic markup
10+
- **Keyboard navigation** for complete keyboard-only operation
11+
- **Focus management** for intuitive interaction flow
12+
- **Screen reader support** with descriptive labels and states
13+
14+
## ARIA Attributes
15+
16+
### Combobox Role
17+
18+
The main container uses the `combobox` role to identify itself as an interactive input control:
19+
20+
```html
21+
<div
22+
role="combobox"
23+
aria-expanded="true"
24+
aria-controls="vue-select-{uid}-listbox"
25+
aria-owns="vue-select-{uid}-listbox"
26+
aria-haspopup="true"
27+
>
28+
```
29+
30+
**Attributes:**
31+
- `aria-expanded`: Indicates whether the dropdown menu is open (`true`) or closed (`false`)
32+
- `aria-controls`: References the ID of the listbox element that this combobox controls
33+
- `aria-owns`: Indicates ownership of the listbox element
34+
- `aria-haspopup`: Signals that the combobox can trigger a popup menu
35+
36+
### Listbox Role
37+
38+
The dropdown menu uses the `listbox` role to identify itself as a list of selectable options:
39+
40+
```html
41+
<div
42+
role="listbox"
43+
aria-multiselectable="true"
44+
aria-label="Select options"
45+
>
46+
```
47+
48+
**Attributes:**
49+
- `aria-multiselectable`: Set to `true` when `isMulti` prop is enabled, indicating multiple selections are allowed
50+
- `aria-label`: Provides an accessible name for the listbox (derived from the `aria.labelledby` prop)
51+
52+
### Option Role
53+
54+
Each menu option uses the `option` role with appropriate state attributes:
55+
56+
```html
57+
<div
58+
role="option"
59+
aria-selected="true"
60+
aria-disabled="false"
61+
>
62+
```
63+
64+
**Attributes:**
65+
- `aria-selected`: Indicates whether the option is currently selected
66+
- `aria-disabled`: Indicates whether the option is disabled and cannot be selected
67+
68+
### Search Input
69+
70+
The search input includes autocomplete attributes for better screen reader support:
71+
72+
```html
73+
<input
74+
aria-autocomplete="list"
75+
aria-labelledby="vue-select-{uid}-combobox"
76+
autocomplete="off"
77+
autocorrect="off"
78+
autocapitalize="none"
79+
spellcheck="false"
80+
>
81+
```
82+
83+
**Attributes:**
84+
- `aria-autocomplete="list"`: Indicates that autocomplete suggestions appear in a listbox
85+
- `aria-labelledby`: Associates the input with the combobox container for proper labeling
86+
- Standard HTML attributes to disable browser autocomplete features that might interfere with the component
87+
88+
### Clear Button
89+
90+
The clear button in multi-value tags includes an accessible label:
91+
92+
```html
93+
<button
94+
type="button"
95+
aria-label="Remove {option-label}"
96+
>
97+
```
98+
99+
## Configurable ARIA Props
100+
101+
You can customize ARIA attributes through the `aria` prop:
102+
103+
```vue
104+
<VueSelect
105+
:aria="{
106+
labelledby: 'my-custom-label',
107+
required: true
108+
}"
109+
/>
110+
```
111+
112+
**Available options:**
113+
- `labelledby`: ID of an element that labels the select component
114+
- `required`: Indicates whether selecting an option is required
115+
116+
**Example:**
117+
118+
```vue
119+
<template>
120+
<div>
121+
<label id="country-label">Select your country</label>
122+
<VueSelect
123+
v-model="selectedCountry"
124+
:options="countries"
125+
:aria="{ labelledby: 'country-label', required: true }"
126+
/>
127+
</div>
128+
</template>
129+
```
130+
131+
## Keyboard Navigation
132+
133+
The component provides full keyboard support following WAI-ARIA best practices.
134+
135+
### When Menu is Closed
136+
137+
| Key | Action |
138+
|-----|--------|
139+
| <kbd>Space</kbd> | Opens the dropdown menu |
140+
| <kbd>Enter</kbd> | Opens the dropdown menu |
141+
| <kbd>↓</kbd> (Arrow Down) | Opens the dropdown menu and focuses first option |
142+
| <kbd>↑</kbd> (Arrow Up) | Opens the dropdown menu and focuses first option |
143+
| <kbd>Tab</kbd> | Moves focus to next focusable element |
144+
145+
### When Menu is Open
146+
147+
| Key | Action |
148+
|-----|--------|
149+
| <kbd>↓</kbd> (Arrow Down) | Moves focus to next non-disabled option (wraps to first option when reaching the end) |
150+
| <kbd>↑</kbd> (Arrow Up) | Moves focus to previous non-disabled option (wraps to last option when reaching the start) |
151+
| <kbd>Enter</kbd> | Selects the focused option and closes menu (if `closeOnSelect` is true) |
152+
| <kbd>Space</kbd> | Selects the focused option when search input is empty |
153+
| <kbd>Escape</kbd> | Closes the menu without selecting |
154+
| <kbd>Tab</kbd> | Closes the menu and moves focus to next element |
155+
| <kbd>PageDown</kbd> | Jumps focus to the last non-disabled option |
156+
| <kbd>PageUp</kbd> | Jumps focus to the first non-disabled option |
157+
| <kbd>Backspace</kbd> | When search is empty and there are selected values, removes the last selected option (multi-select) or the selected option (single-select) |
158+
159+
### Search Behavior
160+
161+
When `isSearchable` is enabled:
162+
- Typing automatically opens the menu if closed
163+
- Typing filters the available options in real-time
164+
- The focused option updates to the first matching result
165+
166+
### Focus on Blur
167+
168+
By default, when the component loses focus with the menu open, the focused option is automatically selected. You can disable this behavior with the `selectOnBlur` prop:
169+
170+
```vue
171+
<VueSelect
172+
v-model="selected"
173+
:options="options"
174+
:select-on-blur="false"
175+
/>
176+
```
177+
178+
## Focus Management
179+
180+
### Auto-focus Behavior
181+
182+
When the menu opens, the component automatically focuses the first non-disabled option. You can disable this with the `shouldAutofocusOption` prop:
183+
184+
```vue
185+
<VueSelect
186+
v-model="selected"
187+
:options="options"
188+
:should-autofocus-option="false"
189+
/>
190+
```
191+
192+
### Disabled Options
193+
194+
Options with the `disabled` property are skipped during keyboard navigation and cannot be selected:
195+
196+
```vue
197+
<VueSelect
198+
v-model="selected"
199+
:options="[
200+
{ label: 'Available', value: '1' },
201+
{ label: 'Unavailable', value: '2', disabled: true }
202+
]"
203+
/>
204+
```
205+
206+
### Visual Focus Indicator
207+
208+
Focused options receive the `.focused` CSS class, which applies visual styling (default: light blue background). You can customize this through CSS variables:
209+
210+
```css
211+
:root {
212+
--vs-option-focused-background-color: #dbeafe;
213+
--vs-option-focused-text-color: #18181b;
214+
}
215+
```
216+
217+
### Viewport Scrolling
218+
219+
When navigating with keyboard, the component automatically scrolls focused options into view if they're outside the menu's visible area. This ensures users never lose track of the focused option.
220+
221+
## Screen Reader Support
222+
223+
### Announced Changes
224+
225+
Screen readers announce:
226+
- When the menu opens/closes via `aria-expanded`
227+
- The current selection via `aria-label` on the combobox
228+
- The focused option as users navigate with keyboard
229+
- Whether options are selected via `aria-selected`
230+
- Whether options are disabled via `aria-disabled`
231+
- The number of available options (via the listbox)
232+
233+
### Value Container Label
234+
235+
The combobox's `aria-label` dynamically updates to reflect the current selection:
236+
237+
```html
238+
<!-- Single select: "Option Label" -->
239+
<div aria-label="Apple">
240+
241+
<!-- Multi-select: "Option 1, Option 2, Option 3" -->
242+
<div aria-label="Apple, Banana, Cherry">
243+
```
244+
245+
### Non-searchable Mode
246+
247+
When `isSearchable` is `false`, the search input receives additional attributes to hide it from screen readers:
248+
249+
```html
250+
<input
251+
readonly
252+
tabindex="-1"
253+
aria-hidden="true"
254+
>
255+
```
256+
257+
This prevents screen readers from announcing the input, as it serves no purpose when search is disabled.
258+
259+
## Input Attributes
260+
261+
You can pass additional HTML attributes to the search input via the `inputAttrs` prop for enhanced accessibility:
262+
263+
```vue
264+
<VueSelect
265+
v-model="selected"
266+
:options="options"
267+
:input-attrs="{
268+
'aria-describedby': 'helper-text',
269+
'aria-invalid': hasError,
270+
'aria-errormessage': hasError ? 'error-message' : undefined
271+
}"
272+
/>
273+
```
274+
275+
See the [Input Attributes guide](/guides/input-attributes) for more details.
276+
277+
## Unique IDs
278+
279+
Each select instance automatically generates a unique ID (via the `uid` prop) to ensure proper ARIA relationships between elements:
280+
281+
```html
282+
<!-- Combobox -->
283+
<div id="vue-select-1-combobox" role="combobox" aria-controls="vue-select-1-listbox">
284+
285+
<!-- Listbox -->
286+
<div id="vue-select-1-listbox" role="listbox">
287+
```
288+
289+
You can provide a custom UID if needed:
290+
291+
```vue
292+
<VueSelect
293+
v-model="selected"
294+
:options="options"
295+
uid="custom-select-id"
296+
/>
297+
```
298+
299+
## Resources
300+
301+
- [WAI-ARIA Combobox Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/)
302+
- [WAI-ARIA Listbox Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/)
303+
- [ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/)

0 commit comments

Comments
 (0)