Various accessibility improvements

* Add announcement element with `aria-live`
* Add skip to main content element

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2021-10-10 16:24:12 +02:00
parent 6113836e29
commit eba3c70c9b
62 changed files with 687 additions and 175 deletions

View File

@@ -11,7 +11,7 @@
)
}}
</p>
<hr />
<hr role="presentation" />
<p class="content">
<span>
{{
@@ -33,7 +33,7 @@
"
/>
</p>
<hr />
<hr role="presentation" />
<p class="content">
{{
$t(

View File

@@ -137,6 +137,7 @@
ref="commentEditor"
v-model="newComment.text"
mode="comment"
:aria-label="$t('Comment body')"
/>
<b-button
:disabled="newComment.text.trim().length === 0"

View File

@@ -23,6 +23,7 @@
ref="commenteditor"
mode="comment"
v-model="newComment.text"
:aria-label="$t('Comment body')"
/>
</p>
<p class="help is-danger" v-if="emptyCommentError">
@@ -30,9 +31,11 @@
</p>
</div>
<div class="field notify-participants" v-if="isEventOrganiser">
<b-switch v-model="newComment.isAnnouncement">{{
$t("Notify participants")
}}</b-switch>
<b-switch
aria-labelledby="notify-participants-toggle"
v-model="newComment.isAnnouncement"
>{{ $t("Notify participants") }}</b-switch
>
</div>
</div>
</div>

View File

@@ -88,7 +88,7 @@
{{ $t("[This comment has been deleted by it's author]") }}
</div>
<form v-else class="edition" @submit.prevent="updateComment">
<editor v-model="updatedComment" />
<editor v-model="updatedComment" :aria-label="$t('Comment body')" />
<div class="buttons">
<b-button
native-type="submit"

View File

@@ -227,6 +227,8 @@ export default class EditorComponent extends Vue {
@Prop({ required: false, default: 100_000_000 }) maxSize!: number;
@Prop({ required: false }) ariaLabel!: string;
currentActor!: IPerson;
editor: Editor | null = null;
@@ -256,6 +258,13 @@ export default class EditorComponent extends Vue {
mounted(): void {
this.editor = new Editor({
editorProps: {
attributes: {
"aria-multiline": this.isShortMode.toString(),
"aria-label": this.ariaLabel,
role: "textbox",
},
},
extensions: [
StarterKit,
Document,

View File

@@ -103,9 +103,11 @@
:active="copied !== false"
always
>
<b-button @click="copyErrorToClipboard">{{
$t("Copy details to clipboard")
}}</b-button>
<b-button
@click="copyErrorToClipboard"
@keyup.enter="copyErrorToClipboard"
>{{ $t("Copy details to clipboard") }}</b-button
>
</b-tooltip>
</div>
</section>

View File

@@ -31,6 +31,7 @@
v-if="!gettingLocation"
icon-right="target"
@click="locateMe"
@keyup.enter="locateMe"
>{{ $t("Use my location") }}</b-button
>
<span v-else>{{ $t("Getting location") }}</span>

View File

@@ -7,25 +7,15 @@
<div class="address-wrapper">
<span v-if="!physicalAddress">{{ $t("No address defined") }}</span>
<div class="address" v-if="physicalAddress">
<div>
<address>
<p
class="addressDescription"
:title="physicalAddress.poiInfos.name"
>
{{ physicalAddress.poiInfos.name }}
</p>
<p class="has-text-grey-dark">
{{ physicalAddress.poiInfos.alternativeName }}
</p>
</address>
</div>
<span
<address-info :address="physicalAddress" />
<b-button
type="is-text"
class="map-show-button"
@click="showMap = !showMap"
v-if="physicalAddress.geom"
>{{ $t("Show map") }}</span
>
{{ $t("Show map") }}
</b-button>
</div>
</div>
</event-metadata-block>

View File

@@ -30,18 +30,22 @@ A button to set your participation
position="is-bottom-left"
v-if="participation && participation.role === ParticipantRole.PARTICIPANT"
>
<button class="button is-success is-large" type="button" slot="trigger">
<b-icon icon="check" />
<template>
<span>{{ $t("I participate") }}</span>
</template>
<b-icon icon="menu-down" />
</button>
<template #trigger="{ active }">
<b-button
type="is-success"
size="is-large"
icon-left="check"
:icon-right="active ? 'menu-up' : 'menu-down'"
>
{{ $t("I participate") }}
</b-button>
</template>
<b-dropdown-item
:value="false"
aria-role="listitem"
@click="confirmLeave"
@keyup.enter="confirmLeave"
class="has-text-danger"
>{{ $t("Cancel my participation…") }}</b-dropdown-item
>
@@ -73,6 +77,7 @@ A button to set your participation
:value="false"
aria-role="listitem"
@click="confirmLeave"
@keyup.enter="confirmLeave"
class="has-text-danger"
>{{ $t("Cancel my participation request…") }}</b-dropdown-item
>
@@ -101,17 +106,21 @@ A button to set your participation
position="is-bottom-left"
v-else-if="!participation && currentActor.id"
>
<button class="button is-primary is-large" type="button" slot="trigger">
<template>
<span>{{ $t("Participate") }}</span>
</template>
<b-icon icon="menu-down" />
</button>
<template #trigger="{ active }">
<b-button
type="is-primary"
size="is-large"
:icon-right="active ? 'menu-up' : 'menu-down'"
>
{{ $t("Participate") }}
</b-button>
</template>
<b-dropdown-item
:value="true"
aria-role="listitem"
@click="joinEvent(currentActor)"
@keyup.enter="joinEvent(currentActor)"
>
<div class="media">
<div class="media-left">
@@ -136,6 +145,7 @@ A button to set your participation
:value="false"
aria-role="listitem"
@click="joinModal"
@keyup.enter="joinModal"
v-if="identities.length > 1"
>{{ $t("with another identity…") }}</b-dropdown-item
>

View File

@@ -25,7 +25,12 @@
v-model="locale"
:placeholder="$t('Select a language')"
>
<option v-for="(language, lang) in langs" :value="lang" :key="lang">
<option
v-for="(language, lang) in langs"
:value="lang"
:key="lang"
:selected="isLangSelected(lang)"
>
{{ language }}
</option>
</b-select>
@@ -48,6 +53,9 @@
{{ $t("License") }}
</a>
</li>
<li>
<a href="#navbar">{{ $t("Back to top") }}</a>
</li>
</ul>
<div class="content has-text-centered">
<i18n
@@ -101,6 +109,10 @@ export default class Footer extends Vue {
this.locale = locale;
}
}
isLangSelected(lang: string): boolean {
return lang === this.locale;
}
}
</script>
<style lang="scss" scoped>
@@ -145,6 +157,13 @@ footer.footer {
color: $white;
text-decoration: underline;
text-decoration-color: $secondary;
&:focus {
background-color: #000;
color: #fff;
outline: 3px solid #000;
text-decoration: none;
}
}
::v-deep span.select {

View File

@@ -1,5 +1,6 @@
<template>
<b-navbar
id="navbar"
type="is-secondary"
wrapper-class="container"
:active.sync="mobileNavbarActive"
@@ -58,6 +59,7 @@
href="https://mediation.koena.net/framasoft/mobilizon/"
target="_blank"
rel="noopener"
hreflang="fr"
>
<img
src="/img/koena-a11y.svg"
@@ -75,6 +77,10 @@
v-if="currentActor.id && currentUser.isLoggedIn"
right
collapsible
ref="user-dropdown"
tabindex="0"
tag="span"
@keyup.enter="toggleMenu"
>
<template
slot="label"
@@ -109,8 +115,11 @@
v-else
:active="identity.id === currentActor.id"
:key="identity.id"
tabindex="0"
@click="setIdentity(identity)"
@keyup.enter="setIdentity(identity)"
>
<span @click="setIdentity(identity)">
<span>
<div class="media-left">
<figure class="image is-32x32" v-if="identity.avatar">
<img
@@ -131,7 +140,7 @@
</div>
</span>
<hr class="navbar-divider" />
<hr class="navbar-divider" role="presentation" />
</b-navbar-item>
<b-navbar-item
@@ -146,8 +155,13 @@
>{{ $t("Administration") }}</b-navbar-item
>
<b-navbar-item tag="span">
<span @click="logout">{{ $t("Log out") }}</span>
<b-navbar-item
tag="span"
tabindex="0"
@click="logout"
@keyup.enter="logout"
>
<span>{{ $t("Log out") }}</span>
</b-navbar-item>
</b-navbar-dropdown>

View File

@@ -71,7 +71,13 @@ import { IParticipant } from "../../types/participant.model";
import RouteName from "../../router/name";
import { CONFIRM_PARTICIPATION } from "../../graphql/event";
@Component
@Component({
metaInfo() {
return {
title: this.$t("Confirm participation") as string,
};
},
})
export default class ConfirmParticipation extends Vue {
@Prop({ type: String, required: true }) token!: string;

View File

@@ -28,6 +28,11 @@ import { IEvent } from "@/types/event.model";
},
},
},
metaInfo() {
return {
title: this.$t("Participation with account") as string,
};
},
})
export default class ParticipationWithAccount extends Vue {
@Prop({ type: String, required: true }) uuid!: string;

View File

@@ -155,6 +155,11 @@ import { ApolloCache, FetchResult } from "@apollo/client/core";
},
config: CONFIG,
},
metaInfo() {
return {
title: this.$t("Participation without account") as string,
};
},
})
export default class ParticipationWithoutAccount extends Vue {
@Prop({ type: String, required: true }) uuid!: string;

View File

@@ -130,6 +130,11 @@ import RouteName from "../../router/name";
},
config: CONFIG,
},
metaInfo() {
return {
title: this.$t("Unlogged participation") as string,
};
},
})
export default class UnloggedParticipation extends Vue {
@Prop({ type: String, required: true }) uuid!: string;

View File

@@ -38,7 +38,12 @@
</span>
</b-upload>
</b-field>
<b-button type="is-text" v-if="imageSrc" @click="removeOrClearPicture">
<b-button
type="is-text"
v-if="imageSrc"
@click="removeOrClearPicture"
@keyup.enter="removeOrClearPicture"
>
{{ $t("Clear") }}
</b-button>
</div>