Skip to content

Commit 2f97f14

Browse files
committed
feat: add aria prop, improve accessibility with aria
1 parent 6cc9912 commit 2f97f14

File tree

3 files changed

+22
-5
lines changed

3 files changed

+22
-5
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ Teleport the menu outside of the component DOM tree. You can pass a valid string
102102

103103
**Note**: top and left properties are calculated using a ref on the `.vue-select` with a `container.getBoundingClientRect()`.
104104

105+
**aria**: `{ labelledby?: string }` (default: `undefined`)
106+
107+
Aria attributes to be passed to the select control to improve accessibility.
108+
105109
**getOptionLabel**: `(option: Option) => string` (default: `option => option.label`)
106110

107111
A function to get the label of an option. This is useful when you want to use a property different from `label` as the label of the option.

src/MenuOption.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ watch(
3030
</script>
3131

3232
<template>
33-
<button
33+
<div
3434
ref="option"
35-
type="button"
36-
class="menu-option"
3735
tabindex="-1"
36+
role="option"
3837
:class="{ focused: isFocused, selected: isSelected }"
38+
:aria-disabled="false"
3939
@click="emit('select')"
4040
>
4141
<slot />
42-
</button>
42+
</div>
4343
</template>

src/Select.vue

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ const props = withDefaults(
4545
* JavaScript, instead of using CSS absolute & relative positioning.
4646
*/
4747
teleport?: string;
48+
/**
49+
* ARIA attributes to describe the select component. This is useful for accessibility.
50+
*/
51+
aria?: {
52+
labelledby?: string;
53+
};
4854
/**
4955
* A function to get the label of an option. By default, it assumes the option is an
5056
* object with a `label` property. Used to display the selected option in the input &
@@ -70,6 +76,7 @@ const props = withDefaults(
7076
isMulti: false,
7177
closeOnSelect: true,
7278
teleport: undefined,
79+
aria: undefined,
7380
getOptionLabel: (option: Option) => option.label,
7481
getMultiValueLabel: (option: Option) => option.label,
7582
},
@@ -261,7 +268,10 @@ onBeforeUnmount(() => {
261268
:class="{ multi: isMulti }"
262269
role="combobox"
263270
:aria-expanded="menuOpen"
264-
:aria-label="placeholder"
271+
:aria-describedby="placeholder"
272+
:aria-description="placeholder"
273+
:aria-labelledby="aria?.labelledby"
274+
:aria-label="selectedOptions.length ? selectedOptions.map(getOptionLabel).join(', ') : ''"
265275
>
266276
<div
267277
v-if="!props.isMulti && selectedOptions[0]"
@@ -336,6 +346,9 @@ onBeforeUnmount(() => {
336346
<div
337347
v-if="menuOpen"
338348
class="menu"
349+
role="listbox"
350+
:aria-label="aria?.labelledby"
351+
:aria-multiselectable="isMulti"
339352
:style="{
340353
width: props.teleport ? `${container?.getBoundingClientRect().width}px` : '100%',
341354
top: props.teleport ? calculateMenuPosition().top : 'unset',

0 commit comments

Comments
 (0)