Skip to content

Commit c2c69bc

Browse files
authored
Merge pull request #605 from it-at-m/603-progress-bar-for-screenreader-users
✨ progress bar for screenreader users
2 parents 89b960e + 61e492a commit c2c69bc

File tree

2 files changed

+69
-4
lines changed

2 files changed

+69
-4
lines changed

src/components/Stepper/MucStepper.vue

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,22 @@
33
<div class="container">
44
<div class="m-component__grid">
55
<div class="m-component__column">
6-
<ol class="m-form-steps">
6+
<ol
7+
class="m-form-steps"
8+
role="list"
9+
aria-label="Formularfortschritt"
10+
>
711
<template
8-
v-for="item in stepItems"
12+
v-for="(item, index) in stepItems"
913
:key="item.id"
1014
>
1115
<muc-stepper-item
1216
:item="item"
1317
:is-active="isActive(item.id)"
1418
:is-done="isDone(item.id)"
1519
:disabled="disabled(item.id)"
20+
:position="index + 1"
21+
:total="stepItems.length"
1622
@click="handleChange"
1723
/>
1824
</template>

src/components/Stepper/MucStepperItem.vue

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,49 @@
1111
class="m-form-step__icon"
1212
:class="{ disabled: disabled }"
1313
:tabindex="getTabindex"
14+
:aria-labelledby="ariaLabelledby"
1415
:aria-label="getAriaLabel"
16+
:aria-setsize="total"
17+
:aria-posinset="position"
18+
:aria-current="isActive ? 'step' : false"
1519
>
1620
<muc-icon :icon="getIcon" />
1721
</div>
1822
<div
1923
class="m-form-step__title"
2024
:class="{ disabled: disabled }"
2125
>
22-
<span aria-disabled="true"> {{ item.label }}</span>
26+
<span
27+
:id="labelId"
28+
aria-disabled="true"
29+
>
30+
{{ item.label }}</span
31+
>
32+
33+
<span
34+
class="visually-hidden"
35+
:id="prefixId"
36+
>
37+
Schritt {{ position }} von {{ total }}:
38+
</span>
39+
40+
<span
41+
v-if="isDone"
42+
class="visually-hidden"
43+
:id="statusId"
44+
>
45+
– erledigt
46+
</span>
2347
</div>
48+
<span
49+
v-if="isActive"
50+
class="visually-hidden"
51+
role="status"
52+
aria-live="polite"
53+
aria-atomic="true"
54+
:aria-labelledby="ariaLabelledby"
55+
>
56+
</span>
2457
</li>
2558
</template>
2659

@@ -30,7 +63,7 @@ import { computed } from "vue";
3063
import { MucIcon } from "../Icon";
3164
import { StepperItem } from "./MucStepperTypes";
3265
33-
const { item, isActive, isDone, disabled } = defineProps<{
66+
const { item, isActive, isDone, disabled, position, total } = defineProps<{
3467
/**
3568
* Individual item to display inside the MucStepper component
3669
*/
@@ -50,6 +83,16 @@ const { item, isActive, isDone, disabled } = defineProps<{
5083
* Disabled stepper
5184
*/
5285
disabled: boolean;
86+
87+
/**
88+
* position of the item in the step sequence
89+
*/
90+
position: number;
91+
92+
/**
93+
* total number of steps
94+
*/
95+
total: number;
5396
}>();
5497
5598
const emit = defineEmits<{
@@ -81,6 +124,22 @@ const getAriaLabel = computed(() =>
81124
: "Zurück zu Schritt: " + item.label
82125
);
83126
127+
/**
128+
* Stable element IDs used to compose the accessible name via aria-labelledby
129+
*/
130+
const labelId = computed(() => `m-step-label-${item.id}`);
131+
const prefixId = computed(() => `m-step-prefix-${item.id}`);
132+
const statusId = computed(() => `m-step-status-${item.id}`);
133+
134+
/**
135+
* Compose the accessible name in order: prefix -> visible label -> optional status
136+
*/
137+
const ariaLabelledby = computed(() => {
138+
const ids = [prefixId.value, labelId.value];
139+
if (isDone) ids.push(statusId.value);
140+
return ids.join(" ");
141+
});
142+
84143
const handleClick = () => {
85144
if (isDone && !disabled) {
86145
emit("click", item.id);

0 commit comments

Comments
 (0)