From 33ba92a0d62db49599f623e742d27b6fcf89a8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciar=C3=A1n=20Ainsworth?= <cda@rootkey.co.uk> Date: Sat, 1 Aug 2020 11:11:51 +0200 Subject: [PATCH] Contrast and labelling fixes --- front/src/components/About.vue | 4 +-- front/src/components/Footer.vue | 18 ++++++---- front/src/components/Home.vue | 10 +++--- front/src/components/Pagination.vue | 6 ++-- front/src/components/Queue.vue | 3 +- front/src/components/SetInstanceModal.vue | 6 ++-- front/src/components/Sidebar.vue | 22 ++++++++----- front/src/components/admin/SettingsGroup.vue | 3 +- .../components/admin/SignupFormBuilder.vue | 4 +-- front/src/components/audio/ChannelForm.vue | 8 ++--- front/src/components/audio/PlayButton.vue | 2 +- front/src/components/audio/Player.vue | 2 +- front/src/components/audio/SearchBar.vue | 5 +-- front/src/components/audio/VolumeControl.vue | 4 ++- front/src/components/audio/album/Widget.vue | 2 +- front/src/components/audio/track/Table.vue | 6 ++-- front/src/components/audio/track/Widget.vue | 2 +- front/src/components/auth/ApplicationEdit.vue | 8 ++--- front/src/components/auth/ApplicationForm.vue | 8 ++--- front/src/components/auth/LoginForm.vue | 7 ++-- front/src/components/auth/Settings.vue | 33 +++++++++---------- front/src/components/auth/SignupForm.vue | 16 +++++---- .../src/components/auth/SubsonicTokenForm.vue | 5 +++ front/src/components/common/ActionTable.vue | 22 ++++++++----- .../src/components/common/AttachmentInput.vue | 4 +-- front/src/components/favorites/List.vue | 12 +++---- .../components/federation/LibraryWidget.vue | 2 +- front/src/components/forms/PasswordInput.vue | 3 +- front/src/components/library/Albums.vue | 18 +++++----- front/src/components/library/Artists.vue | 24 +++++++------- front/src/components/library/FileUpload.vue | 4 +-- front/src/components/library/Radios.vue | 18 +++++----- front/src/components/library/TagsSelector.vue | 2 +- front/src/components/library/TrackBase.vue | 2 +- .../src/components/library/radios/Builder.vue | 8 ++--- .../src/components/library/radios/Filter.vue | 12 ++++--- front/src/components/manage/ChannelsTable.vue | 25 +++++++------- .../components/manage/library/AlbumsTable.vue | 17 ++++++---- .../manage/library/ArtistsTable.vue | 16 ++++----- .../manage/library/EditsCardList.vue | 16 ++++----- .../manage/library/LibrariesTable.vue | 16 ++++----- .../components/manage/library/TagsTable.vue | 12 +++---- .../components/manage/library/TracksTable.vue | 12 +++---- .../manage/library/UploadsTable.vue | 20 +++++------ .../manage/moderation/AccountsTable.vue | 12 +++---- .../manage/moderation/DomainsTable.vue | 16 ++++----- .../manage/users/InvitationForm.vue | 4 +-- .../manage/users/InvitationsTable.vue | 12 +++---- .../components/manage/users/UsersTable.vue | 12 +++---- front/src/components/playlists/Editor.vue | 2 +- front/src/components/playlists/Form.vue | 10 +++--- .../components/playlists/PlaylistModal.vue | 10 +++--- front/src/components/playlists/Widget.vue | 2 +- front/src/components/radios/Card.vue | 2 +- front/src/style/_main.scss | 2 ++ front/src/style/_vars.scss | 10 +++--- .../style/components/_attachment_input.scss | 8 +++++ front/src/style/components/_builder.scss | 9 +++++ front/src/style/components/_form.scss | 14 ++++++++ front/src/style/globals/_layout.scss | 17 ++++++++++ front/src/style/pages/_artists.scss | 15 ++++++++- front/src/views/Notifications.vue | 12 +++---- front/src/views/Search.vue | 4 ++- .../views/admin/moderation/AccountsDetail.vue | 3 +- .../views/admin/moderation/DomainsList.vue | 6 ++-- .../views/admin/moderation/ReportsList.vue | 16 ++++----- .../views/admin/moderation/RequestsList.vue | 16 ++++----- front/src/views/auth/EmailConfirm.vue | 4 +-- front/src/views/auth/PasswordReset.vue | 3 +- front/src/views/auth/PasswordResetConfirm.vue | 4 +-- front/src/views/auth/ProfileActivity.vue | 3 ++ front/src/views/auth/ProfileBase.vue | 2 +- front/src/views/auth/ProfileOverview.vue | 3 +- front/src/views/content/libraries/Card.vue | 2 +- .../views/content/libraries/FilesTable.vue | 20 ++++++----- front/src/views/content/libraries/Form.vue | 12 +++---- front/src/views/content/libraries/Home.vue | 2 +- front/src/views/content/remote/Card.vue | 6 ++-- front/src/views/content/remote/ScanForm.vue | 10 +++--- front/src/views/library/DetailBase.vue | 18 +++++----- front/src/views/playlists/List.vue | 18 +++++----- 81 files changed, 441 insertions(+), 329 deletions(-) create mode 100644 front/src/style/components/_attachment_input.scss create mode 100644 front/src/style/components/_builder.scss diff --git a/front/src/components/About.vue b/front/src/components/About.vue index 2c019918f..d155caa59 100644 --- a/front/src/components/About.vue +++ b/front/src/components/About.vue @@ -20,9 +20,9 @@ <div class="ui mobile reversed stackable grid"> <div class="ten wide column"> <div class="ui text container"> - <h3 class="ui header" id="description"> + <h2 class="ui header" id="description"> <translate translate-context="Content/About/Header">About this pod</translate> - </h3> + </h2> <div v-html="markdown.makeHtml(longDescription)" v-if="longDescription"></div> <p v-else> <translate translate-context="Content/Home/Paragraph">No description available.</translate> diff --git a/front/src/components/Footer.vue b/front/src/components/Footer.vue index efa476c4c..76426aac5 100644 --- a/front/src/components/Footer.vue +++ b/front/src/components/Footer.vue @@ -10,20 +10,26 @@ <span v-translate="{instanceUrl: instanceHostname}" translate-context="Footer/About/Title">About %{instanceUrl}</span> </h4> <div class="ui list"> - <router-link class="link item" to="/about"> + <router-link v-if="this.$route.path != '/about'" class="link item" to="/about"> <translate translate-context="Footer/About/List item.Link">About page</translate> + </router-link> + <router-link v-else-if="this.$route.path == '/about' && $store.state.auth.authenticated" class="link item" to="/library"> + <translate translate-context="Footer/*/List item.Link">Go to Library</translate> + </router-link> + <router-link v-else class="link item" to="/"> + <translate translate-context="Footer/*/List item.Link">Home Page</translate> </router-link> <a v-if="version" class="link item" href="https://docs.funkwhale.audio/changelog.html" target="_blank"> <translate translate-context="Footer/*/List item" :translate-params="{version: version}" >Version %{version}</translate> </a> - <a href="" class="link item" @click.prevent="$emit('show:set-instance-modal')" > + <a role="button" href="" class="link item" @click.prevent="$emit('show:set-instance-modal')" > <translate translate-context="Footer/*/List item.Link">Use another instance</translate> </a> </div> <div class="ui form"> <div class="ui field"> - <label><translate translate-context="Footer/Settings/Dropdown.Label/Short, Verb">Change language</translate></label> - <select class="ui dropdown" :value="$language.current" @change="$store.dispatch('ui/currentLanguage', $event.target.value)"> + <label for="language-select"><translate translate-context="Footer/Settings/Dropdown.Label/Short, Verb">Change language</translate></label> + <select id="language-select" class="ui dropdown" :value="$language.current" @change="$store.dispatch('ui/currentLanguage', $event.target.value)"> <option v-for="(language, key) in $language.available" :key="key" :value="key">{{ language }}</option> </select> </div> @@ -38,8 +44,8 @@ </div> <div class="ui form"> <div class="ui field"> - <label><translate translate-context="Footer/Settings/Dropdown.Label/Short, Verb">Change theme</translate></label> - <select class="ui dropdown" :value="$store.state.ui.theme" @change="$store.dispatch('ui/theme', $event.target.value)"> + <label for="theme-select"><translate translate-context="Footer/Settings/Dropdown.Label/Short, Verb">Change theme</translate></label> + <select id="theme-select" class="ui dropdown" :value="$store.state.ui.theme" @change="$store.dispatch('ui/theme', $event.target.value)"> <option v-for="theme in themes" :key="theme.key" :value="theme.key">{{ theme.name }}</option> </select> </div> diff --git a/front/src/components/Home.vue b/front/src/components/Home.vue index fa72b2b79..f10fd1da1 100644 --- a/front/src/components/Home.vue +++ b/front/src/components/Home.vue @@ -18,9 +18,9 @@ <section class="ui vertical stripe segment"> <div class="ui stackable grid"> <div class="ten wide column"> - <h3 class="header"> + <h2 class="header"> <translate translate-context="Content/Home/Header">About this Funkwhale pod</translate> - </h3> + </h2> <div class="ui raised segment" id="pod"> <div class="ui stackable grid"> <div class="eight wide column"> @@ -77,7 +77,7 @@ </div> <div class="six wide column"> - <img class="ui image" src="../assets/network.png" /> + <img class="ui image" src="../assets/network.png" alt=""/> </div> </div> <div class="ui hidden divider"></div> @@ -98,7 +98,7 @@ <h3 class="header"> <translate translate-context="Head/Login/Title">Log In</translate> </h3> - <login-form button-classes="basic success" :show-signup="false"></login-form> + <login-form button-classes="success" :show-signup="false"></login-form> <div class="ui hidden clearing divider"></div> </div> <div class="four wide column"> @@ -112,7 +112,7 @@ <p v-if="defaultUploadQuota"> <translate translate-context="Content/Home/Paragraph" :translate-params="{quota: humanSize(defaultUploadQuota * 1000 * 1000)}">Users on this pod also get %{ quota } of free storage to upload their own content!</translate> </p> - <signup-form button-classes="basic success" :show-login="false"></signup-form> + <signup-form button-classes="success" :show-login="false"></signup-form> </template> <div v-else> <p translate-context="Content/Home/Paragraph">Registrations are closed on this pod. You can signup on another pod using the link below.</p> diff --git a/front/src/components/Pagination.vue b/front/src/components/Pagination.vue index 6333d9f57..64bd6dc21 100644 --- a/front/src/components/Pagination.vue +++ b/front/src/components/Pagination.vue @@ -2,7 +2,8 @@ <div v-if='maxPage > 1' class="ui pagination menu component-pagination" role="navigation" :aria-label="labels.pagination"> <a href :disabled="current - 1 < 1" - :title="labels.previousPage" + role="button" + :aria-label="labels.previousPage" @click.prevent.stop="selectPage(current - 1)" :class="[{'disabled': current - 1 < 1}, 'item']"><i class="angle left icon"></i></a> <template v-if="!compact"> @@ -19,7 +20,8 @@ </template> <a href :disabled="current + 1 > maxPage" - :title="labels.nextPage" + role="button" + :aria-label="labels.nextPage" @click.prevent.stop="selectPage(current + 1)" :class="[{'disabled': current + 1 > maxPage}, 'item']"><i class="angle right icon"></i></a> </div> diff --git a/front/src/components/Queue.vue b/front/src/components/Queue.vue index 2383f3a53..8dc0aa984 100644 --- a/front/src/components/Queue.vue +++ b/front/src/components/Queue.vue @@ -180,7 +180,7 @@ <template v-if="$store.getters['favorites/isFavorite'](track.id)"> <i class="pink heart icon"></i> </template> - <button :title="labels.removeFromQueue" @click.stop="cleanTrack(index)" :class="['ui', 'really', 'tiny', 'basic', 'circular', 'icon', 'button']"> + <button :aria-label="labels.removeFromQueue" :title="labels.removeFromQueue" @click.stop="cleanTrack(index)" :class="['ui', 'really', 'tiny', 'basic', 'circular', 'icon', 'button']"> <i class="x icon"></i> </button> </td> @@ -272,6 +272,7 @@ export default { return { queue: this.$pgettext('*/*/*', 'Queue'), duration: this.$pgettext('*/*/*', 'Duration'), + addArtistContentFilter: this.$pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Hide content from this artist…') } }, timeLeft () { diff --git a/front/src/components/SetInstanceModal.vue b/front/src/components/SetInstanceModal.vue index 2e5c24bc2..d0d7b27f1 100644 --- a/front/src/components/SetInstanceModal.vue +++ b/front/src/components/SetInstanceModal.vue @@ -17,9 +17,9 @@ <translate translate-context="Popup/Instance/Paragraph">To continue, please select the Funkwhale instance you want to connect to. Enter the address directly, or select one of the suggested choices.</translate> </p> <div class="field"> - <label><translate translate-context="Popup/Instance/Input.Label/Noun">Instance URL</translate></label> + <label for="instance-picker"><translate translate-context="Popup/Instance/Input.Label/Noun">Instance URL</translate></label> <div class="ui action input"> - <input type="text" v-model="instanceUrl" placeholder="https://funkwhale.server"> + <input id ="instance-picker" type="text" v-model="instanceUrl" placeholder="https://funkwhale.server"> <button type="submit" :class="['ui', 'icon', {loading: isLoading}, 'button']"> <translate translate-context="*/*/Button.Label/Verb">Submit</translate> </button> @@ -29,7 +29,7 @@ <div class="ui hidden divider"></div> <form class="ui form" @submit.prevent=""> <div class="field"> - <label><translate translate-context="Popup/Instance/List.Label">Suggested choices</translate></label> + <h4><translate translate-context="Popup/Instance/List.Label">Suggested choices</translate></h4> <button v-for="url in suggestedInstances" @click="checkAndSwitch(url)" class="ui basic button">{{ url }}</button> </div> </form> diff --git a/front/src/components/Sidebar.vue b/front/src/components/Sidebar.vue index 79e8e0043..a6663a9f3 100644 --- a/front/src/components/Sidebar.vue +++ b/front/src/components/Sidebar.vue @@ -4,6 +4,7 @@ <router-link :title="'Funkwhale'" :to="{name: logoUrl}"> <i class="logo bordered inverted vibrant big icon"> <logo class="logo"></logo> + <span class="visually-hidden">Home</span> </i> </router-link> <router-link v-if="!$store.state.auth.authenticated" class="logo-wrapper" :to="{name: logoUrl}" :title="'Funkwhale'"> @@ -64,13 +65,16 @@ <router-link class="item" v-if="$store.state.auth.authenticated" - :title="labels.addContent" - :to="{name: 'content.index'}"><i class="upload icon"></i></router-link> - - <router-link class="item" v-if="$store.state.auth.authenticated" :title="labels.notifications" :to="{name: 'notifications'}"> - <i class="bell icon"></i><div - v-if="$store.state.ui.notifications.inbox + additionalNotifications > 0" - :class="['ui', 'accent', 'mini', 'bottom floating', 'circular', 'label']">{{ $store.state.ui.notifications.inbox + additionalNotifications }}</div> + :to="{name: 'content.index'}"> + <i class="upload icon"></i> + <span class="visually-hidden">{{ labels.addContent }}</span> + </router-link> + <router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'notifications'}"> + <i class="bell icon"></i> + <div v-if="$store.state.ui.notifications.inbox + additionalNotifications > 0" :class="['ui', 'accent', 'mini', 'bottom floating', 'circular', 'label']"> + {{ $store.state.ui.notifications.inbox + additionalNotifications }} + </div> + <span v-else class="visually-hidden">{{ labels.notifications }}</span> </router-link> <div class="item"> <div class="ui user-dropdown dropdown" > @@ -108,10 +112,10 @@ <section :class="['ui', 'bottom', 'attached', {active: selectedTab === 'library'}, 'tab']" :aria-label="labels.mainMenu"> <nav class="ui vertical large fluid inverted menu" role="navigation" :aria-label="labels.mainMenu"> <div :class="[{collapsed: !exploreExpanded}, 'collaspable item']"> - <h3 class="header" @click="exploreExpanded = true" tabindex="0" @focus="exploreExpanded = true"> + <h2 class="header" @click="exploreExpanded = true" tabindex="0" @focus="exploreExpanded = true"> <translate translate-context="*/*/*/Verb">Explore</translate> <i class="angle right icon" v-if="!exploreExpanded"></i> - </h3> + </h2> <div class="menu"> <router-link class="item" :exact="true" :to="{name: 'library.index'}"><i class="music icon"></i><translate translate-context="Sidebar/Navigation/List item.Link/Verb">Browse</translate></router-link> <router-link class="item" :to="{name: 'library.albums.browse'}"><i class="compact disc icon"></i><translate translate-context="*/*/*">Albums</translate></router-link> diff --git a/front/src/components/admin/SettingsGroup.vue b/front/src/components/admin/SettingsGroup.vue index fa4712387..66de6ecdf 100644 --- a/front/src/components/admin/SettingsGroup.vue +++ b/front/src/components/admin/SettingsGroup.vue @@ -61,6 +61,7 @@ <p v-if="setting.help_text">{{ setting.help_text }}</p> </div> <select + :id="setting.identifier" v-else-if="setting.field.class === 'MultipleChoiceField'" v-model="values[setting.identifier]" multiple @@ -68,7 +69,7 @@ <option v-for="v in setting.additional_data.choices" :value="v[0]">{{ v[1] }}</option> </select> <div v-else-if="setting.field.widget.class === 'ImageWidget'"> - <input type="file" :ref="setting.identifier"> + <input :id="setting.identifier" type="file" :ref="setting.identifier"> <div v-if="values[setting.identifier]"> <div class="ui hidden divider"></div> <h3 class="ui header"><translate translate-context="Content/Settings/Title/Noun">Current image</translate></h3> diff --git a/front/src/components/admin/SignupFormBuilder.vue b/front/src/components/admin/SignupFormBuilder.vue index 211c1e924..ab6738390 100644 --- a/front/src/components/admin/SignupFormBuilder.vue +++ b/front/src/components/admin/SignupFormBuilder.vue @@ -49,7 +49,7 @@ <th> <translate translate-context="*/*/Form-builder,Help">Required</translate> </th> - <th></th> + <th><span class="visually-hidden"><translate translate-context="*/*/Form-builder,Help">Actions</translate></span></th> </tr> </thead> <tbody> @@ -86,7 +86,7 @@ <i :disabled="idx >= local.fields.length - 1" @click="move(idx, 1)" rel="button" - :title="labels.up" + :title="labels.down" :class="['down', 'arrow', {disabled: idx >= local.fields.length - 1}, 'icon']"></i> <i @click="remove(idx)" rel="button" :title="labels.delete" class="x icon"></i> </td> diff --git a/front/src/components/audio/ChannelForm.vue b/front/src/components/audio/ChannelForm.vue index f094ff96a..5e58c885a 100644 --- a/front/src/components/audio/ChannelForm.vue +++ b/front/src/components/audio/ChannelForm.vue @@ -7,10 +7,10 @@ </ul> </div> <template v-if="metadataChoices"> - <div v-if="creating && step === 1" class="ui grouped channel-type required field"> - <label> + <fieldset v-if="creating && step === 1" class="ui grouped channel-type required field"> + <legend> <translate translate-context="Content/Channel/Paragraph">What this channel will be used for?</translate> - </label> + </legend> <div class="ui hidden divider"></div> <div class="field"> <div :class="['ui', 'radio', 'checkbox', {selected: choice.value == newValues.content_category}]" v-for="choice in categoryChoices"> @@ -23,7 +23,7 @@ </label> </div> </div> - </div> + </fieldset> <template v-if="!creating || step === 2"> <div class="ui required field"> <label for="channel-name"> diff --git a/front/src/components/audio/PlayButton.vue b/front/src/components/audio/PlayButton.vue index 50412501a..c7a2c0b75 100644 --- a/front/src/components/audio/PlayButton.vue +++ b/front/src/components/audio/PlayButton.vue @@ -2,9 +2,9 @@ <span :title="title" :class="['ui', {'tiny': discrete}, {'icon': !discrete}, {'buttons': !dropdownOnly && !iconOnly}, 'play-button component-play-button']"> <button v-if="!dropdownOnly" - :title="labels.replacePlay" @click.stop.prevent="replacePlay" :disabled="!playable" + :aria-label="labels.replacePlay" :class="buttonClasses.concat(['ui', {loading: isLoading}, {'mini': discrete}, {disabled: !playable}])"> <i :class="[playIconClass, 'icon']"></i> <template v-if="!discrete && !iconOnly"> <slot><translate translate-context="*/Queue/Button.Label/Short, Verb">Play</translate></slot></template> diff --git a/front/src/components/audio/Player.vue b/front/src/components/audio/Player.vue index a5d127135..f1848f74f 100644 --- a/front/src/components/audio/Player.vue +++ b/front/src/components/audio/Player.vue @@ -158,7 +158,7 @@ </div> <div class="group"> <div class="fake-dropdown"> - <span class="position control desktop-and-up" role="button" @click.stop="toggleMobilePlayer"> + <span class="position control desktop-and-up" role="button" @click.stop="toggleMobilePlayer" aria-expanded="true"> <i class="stream icon"></i> <translate translate-context="Sidebar/Queue/Text" :translate-params="{index: queue.currentIndex + 1, length: queue.tracks.length}"> %{ index } of %{ length } diff --git a/front/src/components/audio/SearchBar.vue b/front/src/components/audio/SearchBar.vue index ce72ab9dd..d254ac645 100644 --- a/front/src/components/audio/SearchBar.vue +++ b/front/src/components/audio/SearchBar.vue @@ -1,7 +1,7 @@ <template> <div class="ui fluid category search"> <slot></slot><div class="ui icon input"> - <input ref="search" type="search" class="prompt" name="search" :placeholder="labels.placeholder" @keydown.esc="$event.target.blur()"> + <input :aria-label="labels.searchContent" ref="search" type="search" class="prompt" name="search" :placeholder="labels.placeholder" @keydown.esc="$event.target.blur()"> <i class="search icon"></i> </div> <div class="results"></div> @@ -25,7 +25,8 @@ export default { computed: { labels () { return { - placeholder: this.$pgettext('Sidebar/Search/Input.Placeholder', 'Search for artists, albums, tracks…') + placeholder: this.$pgettext('Sidebar/Search/Input.Placeholder', 'Search for artists, albums, tracks…'), + searchContent: this.$pgettext('Sidebar/Search/Input.Label', 'Search for content') } } }, diff --git a/front/src/components/audio/VolumeControl.vue b/front/src/components/audio/VolumeControl.vue index 8abb6927d..dbbb2c46f 100644 --- a/front/src/components/audio/VolumeControl.vue +++ b/front/src/components/audio/VolumeControl.vue @@ -25,7 +25,9 @@ <i class="volume up icon"></i> </span> <div class="popup"> + <label for="volume-slider" class="visually-hidden">{{ labels.slider }}</label> <input + id="volume-slider" type="range" step="0.05" min="0" @@ -57,7 +59,7 @@ export default { return { unmute: this.$pgettext('Sidebar/Player/Icon.Tooltip/Verb', "Unmute"), mute: this.$pgettext('Sidebar/Player/Icon.Tooltip/Verb', "Mute"), - + slider: this.$pgettext('Sidebar/Player/Icon.Tooltip/Verb', "Adjust volume") } } }, diff --git a/front/src/components/audio/album/Widget.vue b/front/src/components/audio/album/Widget.vue index abf5cadc4..6323ab87b 100644 --- a/front/src/components/audio/album/Widget.vue +++ b/front/src/components/audio/album/Widget.vue @@ -1,6 +1,6 @@ <template> <div class="wrapper"> - <h3 class="ui header"> + <h3 v-if="!!this.$slots.title" class="ui header"> <slot name="title"></slot> <span v-if="showCount" class="ui tiny circular label">{{ count }}</span> </h3> diff --git a/front/src/components/audio/track/Table.vue b/front/src/components/audio/track/Table.vue index 2fcba86cf..43a9069f8 100644 --- a/front/src/components/audio/track/Table.vue +++ b/front/src/components/audio/track/Table.vue @@ -7,13 +7,13 @@ <table v-else :class="['ui', 'compact', 'very', 'basic', {loading: isLoading}, 'unstackable', 'table']"> <thead> <tr> - <th></th> - <th></th> + <th><span class="visually-hidden"><translate translate-context="*/*/*/Noun">Play</translate></span></th> + <th><span class="visually-hidden"><translate translate-context="*/*/*/Noun">Track Art</translate></span></th> <th colspan="6"><translate translate-context="*/*/*/Noun">Title</translate></th> <th colspan="4"><translate translate-context="*/*/*/Noun">Artist</translate></th> <th colspan="4"><translate translate-context="*/*/*">Album</translate></th> <th colspan="4"><translate translate-context="Content/*/*">Duration</translate></th> - <th colspan="2" v-if="displayActions"></th> + <th colspan="2" v-if="displayActions"><span class="visually hidden"><translate translate-context="*/*/*/Noun">Actions</translate></span></th> </tr> </thead> <tbody> diff --git a/front/src/components/audio/track/Widget.vue b/front/src/components/audio/track/Widget.vue index a011e8753..ee0aec6ac 100644 --- a/front/src/components/audio/track/Widget.vue +++ b/front/src/components/audio/track/Widget.vue @@ -1,6 +1,6 @@ <template> <div class="component-track-widget"> - <h3 class="ui header"> + <h3 v-if="!!this.$slots.title"> <slot name="title"></slot> <span v-if="showCount" class="ui tiny circular label">{{ count }}</span> </h3> diff --git a/front/src/components/auth/ApplicationEdit.vue b/front/src/components/auth/ApplicationEdit.vue index b22ade7af..2b90d2e85 100644 --- a/front/src/components/auth/ApplicationEdit.vue +++ b/front/src/components/auth/ApplicationEdit.vue @@ -19,12 +19,12 @@ </translate> </p> <div class="field"> - <label><translate translate-context="Content/Applications/Label">Application ID</translate></label> - <copy-input :value="application.client_id" /> + <label for="copy-id"><translate translate-context="Content/Applications/Label">Application ID</translate></label> + <copy-input id="copy-id" :value="application.client_id" /> </div> <div class="field"> - <label><translate translate-context="Content/Applications/Label">Application secret</translate></label> - <copy-input :value="application.client_secret" /> + <label for="copy-secret"><translate translate-context="Content/Applications/Label">Application secret</translate></label> + <copy-input id="copy-secret" :value="application.client_secret" /> </div> </div> <h2 class="ui header"> diff --git a/front/src/components/auth/ApplicationForm.vue b/front/src/components/auth/ApplicationForm.vue index 1700de08c..34449bd1a 100644 --- a/front/src/components/auth/ApplicationForm.vue +++ b/front/src/components/auth/ApplicationForm.vue @@ -8,12 +8,12 @@ </ul> </div> <div class="ui field"> - <label><translate translate-context="*/*/*/Noun">Name</translate></label> - <input name="name" required type="text" v-model="fields.name" /> + <label for="application-name"><translate translate-context="*/*/*/Noun">Name</translate></label> + <input id="application-name" name="name" required type="text" v-model="fields.name" /> </div> <div class="ui field"> - <label><translate translate-context="Content/Applications/Input.Label/Noun">Redirect URI</translate></label> - <input name="redirect_uris" type="text" v-model="fields.redirect_uris" /> + <label for="redirect-uris"><translate translate-context="Content/Applications/Input.Label/Noun">Redirect URI</translate></label> + <input id="redirect-uris" name="redirect_uris" type="text" v-model="fields.redirect_uris" /> <p class="help"> <translate translate-context="Content/Applications/Help Text"> Use "urn:ietf:wg:oauth:2.0:oob" as a redirect URI if your application is not served on the web. diff --git a/front/src/components/auth/LoginForm.vue b/front/src/components/auth/LoginForm.vue index 3f6eb0edb..6d19b81cf 100644 --- a/front/src/components/auth/LoginForm.vue +++ b/front/src/components/auth/LoginForm.vue @@ -14,7 +14,7 @@ </div> <template v-if="$store.getters['instance/appDomain'] === $store.getters['instance/domain']" > <div class="field"> - <label> + <label for="username-field"> <translate translate-context="Content/Login/Input.Label/Noun">Username or email</translate> <template v-if="showSignup"> | @@ -28,19 +28,20 @@ required name="username" type="text" + id="username-field" autofocus :placeholder="labels.usernamePlaceholder" v-model="credentials.username" > </div> <div class="field"> - <label> + <label for="password-field"> <translate translate-context="*/*/*">Password</translate> | <router-link :to="{name: 'auth.password-reset', query: {email: credentials.username}}"> <translate translate-context="*/Login/*/Verb">Reset your password</translate> </router-link> </label> - <password-input required v-model="credentials.password" /> + <password-input field-id="password-field" required v-model="credentials.password" /> </div> </template> diff --git a/front/src/components/auth/Settings.vue b/front/src/components/auth/Settings.vue index 0ba249db8..f07d0dcf3 100644 --- a/front/src/components/auth/Settings.vue +++ b/front/src/components/auth/Settings.vue @@ -18,12 +18,12 @@ </ul> </div> <div class="field" v-for="f in orderedSettingsFields"> - <label>{{ sharedLabels.fields[f.id].label }}</label> + <label :for="f.id">{{ sharedLabels.fields[f.id].label }}</label> <p v-if="f.help">{{ sharedLabels.fields[f.id].help }}</p> - <select v-if="f.type === 'dropdown'" class="ui dropdown" v-model="f.value"> + <select :id="f.id" v-if="f.type === 'dropdown'" class="ui dropdown" v-model="f.value"> <option :value="c" v-for="c in f.choices">{{ sharedLabels.fields[f.id].choices[c] }}</option> </select> - <content-form v-if="f.type === 'content'" v-model="f.value.text"></content-form> + <content-form :field-id="f.id" v-if="f.type === 'content'" v-model="f.value.text"></content-form> </div> <button :class="['ui', {'loading': isLoading}, 'button']" type="submit"> <translate translate-context="Content/Settings/Button.Label/Verb">Update settings</translate> @@ -72,13 +72,12 @@ </ul> </div> <div class="field"> - <label><translate translate-context="Content/Settings/Input.Label">Old password</translate></label> - <password-input required v-model="old_password" /> - + <label for="old-password-field"><translate translate-context="Content/Settings/Input.Label">Old password</translate></label> + <password-input field-id="old-password-field" required v-model="old_password" /> </div> <div class="field"> - <label><translate translate-context="Content/Settings/Input.Label">New password</translate></label> - <password-input required v-model="new_password" /> + <label for="new-password-field"><translate translate-context="Content/Settings/Input.Label">New password</translate></label> + <password-input field-id="new-password-field" required v-model="new_password" /> </div> <dangerous-button :class="['ui', {'loading': isLoading}, 'warning', 'button']" @@ -111,7 +110,7 @@ <button @click="$store.dispatch('moderation/fetchContentFilters')" - class="ui basic icon button"> + class="ui icon button"> <i class="refresh icon"></i> <translate translate-context="Content/*/Button.Label/Short, Verb">Refresh</translate> </button> @@ -156,7 +155,7 @@ <p><translate translate-context="Content/Settings/Paragraph">This is the list of applications that have access to your account data.</translate></p> <button @click="fetchApps()" - class="ui basic icon button"> + class="ui icon button"> <i class="refresh icon"></i> <translate translate-context="Content/*/Button.Label/Short, Verb">Refresh</translate> </button> @@ -178,7 +177,7 @@ </td> <td> <dangerous-button - class="ui tiny basic danger button" + class="ui tiny danger button" @confirm="revokeApp(app.client_id)"> <translate translate-context="*/*/*/Verb">Revoke</translate> <p slot="modal-header" v-translate="{application: app.name}" translate-context="Popup/Settings/Title">Revoke access for application "%{ application }"?</p> @@ -207,7 +206,7 @@ </div> </h2> <p><translate translate-context="Content/Settings/Paragraph">This is the list of applications that you have created.</translate></p> - <router-link class="ui basic success button" :to="{name: 'settings.applications.new'}"> + <router-link class="ui success button" :to="{name: 'settings.applications.new'}"> <translate translate-context="Content/Settings/Button.Label">Create a new application</translate> </router-link> <table v-if="ownedApps.length > 0" class="ui compact very basic unstackable table"> @@ -233,11 +232,11 @@ <human-date :date="app.created" /> </td> <td> - <router-link class="ui basic tiny success button" :to="{name: 'settings.applications.edit', params: {id: app.client_id}}"> + <router-link class="ui tiny success button" :to="{name: 'settings.applications.edit', params: {id: app.client_id}}"> <translate translate-context="Content/*/Button.Label/Verb">Edit</translate> </router-link> <dangerous-button - class="ui tiny basic danger button" + class="ui tiny danger button" @confirm="deleteApp(app.client_id)"> <translate translate-context="*/*/*/Verb">Delete</translate> <p slot="modal-header" v-translate="{application: app.name}" translate-context="Popup/Settings/Title">Delete application "%{ application }"?</p> @@ -267,7 +266,7 @@ </div> </h2> <p><translate translate-context="Content/Settings/Paragraph">Use plugins to extend Funkwhale and get additional features.</translate></p> - <router-link class="ui basic success button" :to="{name: 'settings.plugins'}"> + <router-link class="ui success button" :to="{name: 'settings.plugins'}"> <translate translate-context="Content/Settings/Button.Label">Manage plugins</translate> </router-link> </section> @@ -293,8 +292,8 @@ </ul> </div> <div class="field"> - <label><translate translate-context="*/*/*">Password</translate></label> - <password-input required v-model="password" /> + <label for="current-password-field"><translate translate-context="*/*/*">Password</translate></label> + <password-input field-id="current-password-field" required v-model="password" /> </div> <dangerous-button :class="['ui', {'loading': isDeletingAccount}, {disabled: !password}, 'danger', 'button']" diff --git a/front/src/components/auth/SignupForm.vue b/front/src/components/auth/SignupForm.vue index c44a51299..d037da3c3 100644 --- a/front/src/components/auth/SignupForm.vue +++ b/front/src/components/auth/SignupForm.vue @@ -32,19 +32,21 @@ </ul> </div> <div class="required field"> - <label><translate translate-context="Content/*/*">Username</translate></label> + <label for="username-field"><translate translate-context="Content/*/*">Username</translate></label> <input ref="username" name="username" required + id="username-field" type="text" autofocus :placeholder="labels.usernamePlaceholder" v-model="username"> </div> <div class="required field"> - <label><translate translate-context="Content/*/*/Noun">Email</translate></label> + <label for="email-field"><translate translate-context="Content/*/*/Noun">Email</translate></label> <input + id="email-field" ref="email" name="email" required @@ -53,12 +55,13 @@ v-model="email"> </div> <div class="required field"> - <label><translate translate-context="*/*/*">Password</translate></label> - <password-input v-model="password" /> + <label for="password-field"><translate translate-context="*/*/*">Password</translate></label> + <password-input field-id="password-field" v-model="password" /> </div> <div class="required field" v-if="!$store.state.instance.settings.users.registration_enabled.value"> - <label><translate translate-context="Content/*/Input.Label">Invitation code</translate></label> + <label for="invitation-code"><translate translate-context="Content/*/Input.Label">Invitation code</translate></label> <input + id="invitation-code" required type="text" name="invitation" @@ -70,10 +73,11 @@ <label :for="`custom-field-${idx}`">{{ field.label }}</label> <textarea v-if="field.input_type === 'long_text'" + :id="`custom-field-${idx}`" :value="customFields[field.label]" :required="field.required" @input="$set(customFields, field.label, $event.target.value)" rows="5"></textarea> - <input v-else type="text" :value="customFields[field.label]" :required="field.required" @input="$set(customFields, field.label, $event.target.value)"> + <input v-else :id="`custom-field-${idx}`" type="text" :value="customFields[field.label]" :required="field.required" @input="$set(customFields, field.label, $event.target.value)"> </div> </template> <button :class="['ui', buttonClasses, {'loading': isLoading}, ' right floated button']" type="submit"> diff --git a/front/src/components/auth/SubsonicTokenForm.vue b/front/src/components/auth/SubsonicTokenForm.vue index 4c1cb6b41..74622f1fa 100644 --- a/front/src/components/auth/SubsonicTokenForm.vue +++ b/front/src/components/auth/SubsonicTokenForm.vue @@ -24,7 +24,9 @@ </div> <template v-if="subsonicEnabled"> <div v-if="token" class="field"> + <label for="subsonic-password" class="visually-hidden">{{ labels.subsonicField }}</label> <password-input + field-id="subsonic-password" ref="passwordInput" v-model="token" :key="token" @@ -131,6 +133,9 @@ export default { computed: { subsonicEnabled () { return this.$store.state.instance.settings.subsonic.enabled.value + }, + labels () { + subsonicField: this.$pgettext("Content/Password/Input.label", "Your subsonic API password") } } } diff --git a/front/src/components/common/ActionTable.vue b/front/src/components/common/ActionTable.vue index 20a6a2f15..4e6d89d52 100644 --- a/front/src/components/common/ActionTable.vue +++ b/front/src/components/common/ActionTable.vue @@ -6,7 +6,7 @@ <th colspan="1000"> <div v-if="refreshable" class="right floated"> <span v-if="needsRefresh"> - <translate translate-context="Content/*/Button.Help text.Paragraph">Content have been updated, click refresh to see up-to-date content</translate> + <translate translate-context="Content/*/Button.Help text.Paragraph">Content has been updated, click refresh to see up-to-date content</translate> </span> <button @click="$emit('refresh')" @@ -20,8 +20,8 @@ <div class="ui small left floated form" v-if="actionUrl && actions.length > 0"> <div class="ui inline fields"> <div class="field"> - <label><translate translate-context="Content/*/*/Noun">Actions</translate></label> - <select class="ui dropdown" v-model="currentActionName"> + <label for="actions-select"><translate translate-context="Content/*/*/Noun">Actions</translate></label> + <select id="actions-select" class="ui dropdown" v-model="currentActionName"> <option v-for="action in actions" :value="action.name"> {{ action.label }} </option> @@ -31,7 +31,7 @@ <dangerous-button v-if="selectAll || currentAction.isDangerous" :class="['ui', {disabled: checked.length === 0}, {'loading': actionLoading}, 'button']" :confirm-color="currentAction.confirmColor || 'success'" - @confirm="launchAction"> + @confirm="launchAction" :aria-label="labels.performAction"> <translate translate-context="Content/*/Button.Label/Short, Verb">Go</translate> <p slot="modal-header"> <translate translate-context="Modal/*/Title" @@ -46,12 +46,13 @@ <template v-if="currentAction.confirmationMessage">{{ currentAction.confirmationMessage }}</template> <translate v-else translate-context="Modal/*/Paragraph">This may affect a lot of elements or have irreversible consequences, please double check this is really what you want.</translate> </p> - <div slot="modal-confirm"><translate translate-context="Modal/*/Button.Label/Short, Verb">Launch</translate></div> + <div :aria-label="labels.performAction" slot="modal-confirm"><translate translate-context="Modal/*/Button.Label/Short, Verb">Launch</translate></div> </dangerous-button> <div v-else @click="launchAction" :disabled="checked.length === 0" + :aria-label="labels.performAction" :class="['ui', {disabled: checked.length === 0}, {'loading': actionLoading}, 'button']"> <translate translate-context="Content/*/Button.Label/Short, Verb">Go</translate></div> </div> @@ -118,8 +119,9 @@ <input type="checkbox" @change="toggleCheckAll" + :aria-label="labels.selectAllItems" :disabled="checkable.length === 0" - :checked="checkable.length > 0 && checked.length === checkable.length"><label> </label> + :checked="checkable.length > 0 && checked.length === checkable.length"> </div> </th> <slot name="header-cells"></slot> @@ -130,9 +132,10 @@ <td v-if="actions.length > 0" class="collapsing"> <input type="checkbox" + :aria-label="labels.selectItem + ' ' + obj.track.title" :disabled="checkable.indexOf(getId(obj)) === -1" @click="toggleCheck($event, getId(obj), index)" - :checked="checked.indexOf(getId(obj)) > -1"><label> </label> + :checked="checked.indexOf(getId(obj)) > -1"> </td> <slot name="row-cells" :obj="obj"></slot> </tr> @@ -271,7 +274,10 @@ export default { }, labels () { return { - refresh: this.$pgettext('Content/*/Button.Tooltip/Verb', 'Refresh table content') + refresh: this.$pgettext('Content/*/Button.Tooltip/Verb', 'Refresh table content'), + selectAllItems: this.$pgettext('Content/*/Select/Verb', 'Select all items'), + performAction: this.$pgettext('Content/*/Button.Label', 'Perform actions'), + selectItem: this.$pgettext('Content/*/Select/Verb', 'Select') } }, affectedObjectsCount () { diff --git a/front/src/components/common/AttachmentInput.vue b/front/src/components/common/AttachmentInput.vue index 2d0543ea6..a56bc717c 100644 --- a/front/src/components/common/AttachmentInput.vue +++ b/front/src/components/common/AttachmentInput.vue @@ -7,9 +7,9 @@ </ul> </div> <div class="ui field"> - <label :for="attachmentId"> + <span id="avatarLabel"> <slot name="label"></slot> - </label> + </span> <div class="ui stackable grid row"> <div class="three wide column"> <img :class="['ui', imageClass, 'image']" v-if="value && value === initialValue" :src="$store.getters['instance/absoluteUrl'](`api/v1/attachments/${value}/proxy?next=medium_square_crop`)" /> diff --git a/front/src/components/favorites/List.vue b/front/src/components/favorites/List.vue index 6aabb8817..b43ec41f4 100644 --- a/front/src/components/favorites/List.vue +++ b/front/src/components/favorites/List.vue @@ -22,23 +22,23 @@ <div :class="['ui', {'loading': isLoading}, 'form']"> <div class="fields"> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="favorites-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="favorites-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="favorites-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label> + <select id="favorites-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label> - <select class="ui dropdown" v-model="paginateBy"> + <label for="favorites-results"><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label> + <select id="favorites-results" class="ui dropdown" v-model="paginateBy"> <option :value="parseInt(12)">12</option> <option :value="parseInt(25)">25</option> <option :value="parseInt(50)">50</option> diff --git a/front/src/components/federation/LibraryWidget.vue b/front/src/components/federation/LibraryWidget.vue index bab61858a..3ee0c9340 100644 --- a/front/src/components/federation/LibraryWidget.vue +++ b/front/src/components/federation/LibraryWidget.vue @@ -1,6 +1,6 @@ <template> <div class="wrapper"> - <h3 class="ui header"> + <h3 v-if="!!this.$slots.title" class="ui header"> <slot name="title"></slot> </h3> <p v-if="!isLoading && libraries.length > 0" class="ui subtitle"><slot name="subtitle"></slot></p> diff --git a/front/src/components/forms/PasswordInput.vue b/front/src/components/forms/PasswordInput.vue index 2e4f227a5..98c1097f6 100644 --- a/front/src/components/forms/PasswordInput.vue +++ b/front/src/components/forms/PasswordInput.vue @@ -5,6 +5,7 @@ name="password" :type="passwordInputType" @input="$emit('input', $event.target.value)" + :id="fieldId" :value="value"> <span @click="showPassword = !showPassword" :title="labels.title" class="ui icon button"> <i class="eye icon"></i> @@ -29,7 +30,7 @@ function copyStringToClipboard (str) { } export default { - props: ['value', 'defaultShow', 'copyButton'], + props: ['value', 'defaultShow', 'copyButton', 'fieldId'], data () { return { showPassword: this.defaultShow || false, diff --git a/front/src/components/library/Albums.vue b/front/src/components/library/Albums.vue index 4f9bcd8b2..3e622515a 100644 --- a/front/src/components/library/Albums.vue +++ b/front/src/components/library/Albums.vue @@ -7,38 +7,38 @@ <form :class="['ui', {'loading': isLoading}, 'form']" @submit.prevent="updatePage();updateQueryString();fetchData()"> <div class="fields"> <div class="field"> - <label> + <label for="albums-search"> <translate translate-context="Content/Search/Input.Label/Noun">Search</translate> </label> <div class="ui action input"> - <input type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> + <input id="albums-search" type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> <button class="ui icon button" type="submit" :aria-label="$pgettext('Content/Search/Input.Label/Noun', 'Search')"> <i class="search icon"></i> </button> </div> </div> <div class="field"> - <label><translate translate-context="*/*/*/Noun">Tags</translate></label> + <label for="tags-search"><translate translate-context="*/*/*/Noun">Tags</translate></label> <tags-selector v-model="tags"></tags-selector> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="album-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="album-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="album-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> + <select id="album-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label> - <select class="ui dropdown" v-model="paginateBy"> + <label for="album-results"><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label> + <select id="album-results" class="ui dropdown" v-model="paginateBy"> <option :value="parseInt(12)">12</option> <option :value="parseInt(25)">25</option> <option :value="parseInt(50)">50</option> diff --git a/front/src/components/library/Artists.vue b/front/src/components/library/Artists.vue index d01d0a9dd..4bd826655 100644 --- a/front/src/components/library/Artists.vue +++ b/front/src/components/library/Artists.vue @@ -7,48 +7,48 @@ <form :class="['ui', {'loading': isLoading}, 'form']" @submit.prevent="updatePage();updateQueryString();fetchData()"> <div class="fields"> <div class="field"> - <label> + <label for="artist-search"> <translate translate-context="Content/Search/Input.Label/Noun">Search</translate> </label> <div class="ui action input"> - <input type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> + <input id="artist-search" type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> <button class="ui icon button" type="submit" :aria-label="$pgettext('Content/Search/Input.Label/Noun', 'Search')"> <i class="search icon"></i> </button> </div> </div> <div class="field"> - <label><translate translate-context="*/*/*/Noun">Tags</translate></label> + <label for="tags-search"><translate translate-context="*/*/*/Noun">Tags</translate></label> <tags-selector v-model="tags"></tags-selector> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="artist-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="artist-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="artist-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> + <select id="artist-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label> - <select class="ui dropdown" v-model="paginateBy"> + <label for="artist-results"><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label> + <select id="artist-results" class="ui dropdown" v-model="paginateBy"> <option :value="parseInt(12)">12</option> <option :value="parseInt(30)">30</option> <option :value="parseInt(50)">50</option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Checkbox/Noun">Exclude Compilation Artists</translate></label> - <div id="excludeCompilation" class="ui fitted toggle checkbox"> + <span id="excludeHeader">Exclude Compilation Artists</span> + <div id="excludeCompilation" class="ui toggle checkbox"> <input id="exclude-compilation" v-model="excludeCompilation" true-value="true" false-value="null" type="checkbox"> - <label></label> + <label for="exclude-compilation" class="visually-hidden"><translate translate-context="Content/Search/Checkbox/Noun">Exclude Compilation Artists</translate></label> </div> </div> </div> diff --git a/front/src/components/library/FileUpload.vue b/front/src/components/library/FileUpload.vue index 3960863bc..e4dd6a4a7 100644 --- a/front/src/components/library/FileUpload.vue +++ b/front/src/components/library/FileUpload.vue @@ -48,9 +48,9 @@ <form class="ui form" @submit.prevent="currentTab = 'uploads'"> <div class="fields"> <div class="ui field"> - <label><translate translate-context="Content/Library/Input.Label/Noun">Import reference</translate></label> + <label for="import-reference"><translate translate-context="Content/Library/Input.Label/Noun">Import reference</translate></label> <p><translate translate-context="Content/Library/Paragraph">This reference will be used to group imported files together.</translate></p> - <input name="import-ref" type="text" v-model="importReference" /> + <input id="import-reference" name="import-ref" type="text" v-model="importReference" /> </div> </div> diff --git a/front/src/components/library/Radios.vue b/front/src/components/library/Radios.vue index fb0467192..8fd6142ac 100644 --- a/front/src/components/library/Radios.vue +++ b/front/src/components/library/Radios.vue @@ -21,32 +21,32 @@ <h3 class="ui header"> <translate translate-context="Content/Radio/Title">User radios</translate> </h3> - <router-link class="ui success basic button" to="/library/radios/build" exact> + <router-link class="ui success button" to="/library/radios/build" exact> <translate translate-context="Content/Radio/Button.Label/Verb">Create your own radio</translate> </router-link> <div class="ui hidden divider"></div> <form :class="['ui', {'loading': isLoading}, 'form']" @submit.prevent="updateQueryString();fetchData()"> <div class="fields"> <div class="field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <label for="radios-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <div class="ui action input"> - <input type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> + <input id ="radios-search" type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> <button class="ui icon button" type="submit" :aria-label="$pgettext('Content/Search/Input.Label/Noun', 'Search')"> <i class="search icon"></i> </button> </div> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="radios-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="radios-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="radios-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label> + <select id="radios-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"> <translate translate-context="Content/Search/Dropdown">Ascending</translate> </option> @@ -56,8 +56,8 @@ </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label> - <select class="ui dropdown" v-model="paginateBy"> + <label for="radios-results"><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label> + <select id="radios-results" class="ui dropdown" v-model="paginateBy"> <option :value="parseInt(12)">12</option> <option :value="parseInt(25)">25</option> <option :value="parseInt(50)">50</option> diff --git a/front/src/components/library/TagsSelector.vue b/front/src/components/library/TagsSelector.vue index 5b2fa0c96..e9db22605 100644 --- a/front/src/components/library/TagsSelector.vue +++ b/front/src/components/library/TagsSelector.vue @@ -2,7 +2,7 @@ <div ref="dropdown" class="ui multiple search selection dropdown"> <input type="hidden"> <i class="dropdown icon"></i> - <input type="text" class="search"> + <input id="tags-search" type="text" class="search"> <div class="default text"> <translate translate-context="*/Dropdown/Placeholder/Verb">Search for tags…</translate> </div> diff --git a/front/src/components/library/TrackBase.vue b/front/src/components/library/TrackBase.vue index 93b64e17d..6410942ba 100644 --- a/front/src/components/library/TrackBase.vue +++ b/front/src/components/library/TrackBase.vue @@ -23,7 +23,7 @@ <track-favorite-icon v-if="$store.state.auth.authenticated" :border="true" :track="track"></track-favorite-icon> <track-playlist-icon class="circular" v-if="$store.state.auth.authenticated" :border="true" :track="track"></track-playlist-icon> - <a v-if="upload" :href="downloadUrl" target="_blank" class="ui basic circular icon button" :title="labels.download"> + <a role="button" :aria-label="labels.download" v-if="upload" :href="downloadUrl" target="_blank" class="ui basic circular icon button" :title="labels.download"> <i class="download icon"></i> </a> <modal v-if="publicLibraries.length > 0" :show.sync="showEmbedModal"> diff --git a/front/src/components/library/radios/Builder.vue b/front/src/components/library/radios/Builder.vue index d6ff9f525..d583c5cb4 100644 --- a/front/src/components/library/radios/Builder.vue +++ b/front/src/components/library/radios/Builder.vue @@ -38,17 +38,15 @@ </div> </div> <div class="ui form"> - <p> - <translate translate-context="Content/Radio/Paragraph">Add filters to customize your radio</translate> - </p> <div class="inline field"> - <select class="ui dropdown" v-model="currentFilterType"> + <label id="radioFilterLabel" for="radio-filters"><translate translate-context="Content/Radio/Paragraph">Add filters to customize your radio</translate></label> + <select id="radio-filters" class="ui dropdown" v-model="currentFilterType"> <option value=""> <translate translate-context="Content/Radio/Dropdown.Placeholder/Verb">Select a filter</translate> </option> <option v-for="f in availableFilters" :value="f.type">{{ f.label }}</option> </select> - <button :disabled="!currentFilterType" @click="add" class="ui button"> + <button id="addFilter" :disabled="!currentFilterType" @click="add" class="ui button"> <translate translate-context="Content/Radio/Button.Label/Verb">Add filter</translate> </button> </div> diff --git a/front/src/components/library/radios/Filter.vue b/front/src/components/library/radios/Filter.vue index af6b15ab4..1a70c0847 100644 --- a/front/src/components/library/radios/Filter.vue +++ b/front/src/components/library/radios/Filter.vue @@ -3,8 +3,10 @@ <td>{{ filter.label }}</td> <td> <div class="ui toggle checkbox"> - <input name="public" type="checkbox" v-model="exclude" @change="$emit('update-config', index, 'not', exclude)"> - <label></label> + <input id="exclude-filter" name="public" type="checkbox" v-model="exclude" @change="$emit('update-config', index, 'not', exclude)"> + <label for="exclude-filter" class="visually-hidden"> + <translate translate-context="Popup/Radio/Title/Noun">Exclude</translate> + </label> </div> </td> <td> @@ -16,7 +18,7 @@ <div :class="['ui', 'search', 'selection', 'dropdown', {'autocomplete': f.autocomplete}, {'multiple': f.type === 'list'}]"> <i class="dropdown icon"></i> <div class="default text">{{ f.placeholder }}</div> - <input v-if="f.type === 'list' && config[f.name]" :value="config[f.name].join(',')" type="hidden"> + <input :id="f.name" v-if="f.type === 'list' && config[f.name]" :value="config[f.name].join(',')" type="hidden"> <div v-if="config[f.name]" class="ui menu"> <div v-if="f.type === 'list'" @@ -49,14 +51,14 @@ </div> </div> <div class="actions"> - <div class="ui basic deny button"> + <div class="ui deny button"> <translate translate-context="*/*/Button.Label/Verb">Cancel</translate> </div> </div> </modal> </td> <td> - <button @click="$emit('delete', index)" class="ui basic danger button"><translate translate-context="Content/Radio/Button.Label/Verb">Remove</translate></button> + <button @click="$emit('delete', index)" class="ui danger button"><translate translate-context="Content/Radio/Button.Label/Verb">Remove</translate></button> </td> </tr> </template> diff --git a/front/src/components/manage/ChannelsTable.vue b/front/src/components/manage/ChannelsTable.vue index 794124cdb..04d6a9fdd 100644 --- a/front/src/components/manage/ChannelsTable.vue +++ b/front/src/components/manage/ChannelsTable.vue @@ -3,14 +3,14 @@ <div class="ui inline form"> <div class="fields"> <div class="ui six wide field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <label for="channel-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <form @submit.prevent="search.query = $refs.search.value"> - <input name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> + <input id="channel-search" name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> </form> </div> <div class="field"> - <label><translate translate-context="*/*/*">Category</translate></label> - <select class="ui dropdown" @change="addSearchToken('category', $event.target.value)" :value="getTokenValue('category', '')"> + <label for="channel-category"><translate translate-context="*/*/*">Category</translate></label> + <select id="channel-category" class="ui dropdown" @change="addSearchToken('category', $event.target.value)" :value="getTokenValue('category', '')"> <option value=""><translate translate-context="Content/*/Dropdown">All</translate></option> <option value="podcast">{{ sharedLabels.fields.content_category.choices.podcast }}</option> <option value="music">{{ sharedLabels.fields.content_category.choices.music }}</option> @@ -18,16 +18,16 @@ </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="channel-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="channel-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="channel-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> + <select id="channel-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> @@ -60,15 +60,17 @@ <td> <router-link :to="{name: 'manage.moderation.accounts.detail', params: {id: scope.obj.attributed_to.full_username }}"> <i class="wrench icon"></i> + <span class="visually-hidden">{{ labels.openModeration }}</span> </router-link> - <span role="button" class="discrete link" @click="addSearchToken('account', scope.obj.attributed_to.full_username)" :title="scope.obj.attributed_to.full_username">{{ scope.obj.attributed_to.preferred_username }}</span> + <span role="button" class="discrete link" @click="addSearchToken('account', scope.obj.attributed_to.full_username)">{{ scope.obj.attributed_to.preferred_username }}</span> </td> <td> <template v-if="!scope.obj.is_local"> <router-link :to="{name: 'manage.moderation.domains.detail', params: {id: scope.obj.attributed_to.domain }}"> <i class="wrench icon"></i> + <span class="visually-hidden">{{ labels.openModeration }}</span> </router-link> - <span role="button" class="discrete link" @click="addSearchToken('domain', scope.obj.attributed_to.domain)" :title="scope.obj.attributed_to.domain">{{ scope.obj.attributed_to.domain }}</span> + <span role="button" class="discrete link" @click="addSearchToken('domain', scope.obj.attributed_to.domain)">{{ scope.obj.attributed_to.domain }}</span> </template> <span role="button" v-else class="ui tiny accent icon link label" @click="addSearchToken('domain', scope.obj.attributed_to.domain)"> <i class="home icon"></i> @@ -177,7 +179,8 @@ export default { computed: { labels () { return { - searchPlaceholder: this.$pgettext('Content/Search/Input.Placeholder', 'Search by domain, name, account…') + searchPlaceholder: this.$pgettext('Content/Search/Input.Placeholder', 'Search by domain, name, account…'), + openModeration: this.$pgettext('Content/Moderation/Verb', 'Open in moderation interface') } }, actionFilters () { diff --git a/front/src/components/manage/library/AlbumsTable.vue b/front/src/components/manage/library/AlbumsTable.vue index 72f5aee4c..cd71fc02e 100644 --- a/front/src/components/manage/library/AlbumsTable.vue +++ b/front/src/components/manage/library/AlbumsTable.vue @@ -3,21 +3,21 @@ <div class="ui inline form"> <div class="fields"> <div class="ui six wide field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <label for="albums-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <form @submit.prevent="search.query = $refs.search.value"> - <input name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> + <input id="albums-search" name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> </form> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="albums-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="albums-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> + <label for="albums-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> <select class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> @@ -51,6 +51,7 @@ <td> <router-link :to="{name: 'manage.library.artists.detail', params: {id: scope.obj.artist.id }}"> <i class="wrench icon"></i> + <span class="visually-hidden">{{ labels.openModeration }}</span> </router-link> <span role="button" class="discrete link" @click="addSearchToken('artist', scope.obj.artist.name)" :title="scope.obj.artist.name">{{ scope.obj.artist.name }}</span> </td> @@ -58,8 +59,9 @@ <template v-if="!scope.obj.is_local"> <router-link :to="{name: 'manage.moderation.domains.detail', params: {id: scope.obj.domain }}"> <i class="wrench icon"></i> + <span class="visually-hidden">{{ labels.openModeration }}</span> </router-link> - <span role="button" class="discrete link" @click="addSearchToken('domain', scope.obj.domain)" :title="scope.obj.domain">{{ scope.obj.domain }}</span> + <span role="button" class="discrete link" @click="addSearchToken('domain', scope.obj.domain)">{{ scope.obj.domain }}</span> </template> <span role="button" v-else class="ui tiny accent icon link label" @click="addSearchToken('domain', scope.obj.domain)"> <i class="home icon"></i> @@ -171,7 +173,8 @@ export default { computed: { labels () { return { - searchPlaceholder: this.$pgettext('Content/Search/Input.Placeholder', 'Search by domain, title, artist, MusicBrainz ID…') + searchPlaceholder: this.$pgettext('Content/Search/Input.Placeholder', 'Search by domain, title, artist, MusicBrainz ID…'), + openModeration: this.$pgettext('Content/Moderation/Verb', 'Open in moderation interface') } }, actionFilters () { diff --git a/front/src/components/manage/library/ArtistsTable.vue b/front/src/components/manage/library/ArtistsTable.vue index 3790d92e0..5babb6f53 100644 --- a/front/src/components/manage/library/ArtistsTable.vue +++ b/front/src/components/manage/library/ArtistsTable.vue @@ -3,14 +3,14 @@ <div class="ui inline form"> <div class="fields"> <div class="ui six wide field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <label for="artists-serarch"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <form @submit.prevent="search.query = $refs.search.value"> - <input name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> + <input id="artists-search" name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> </form> </div> <div class="field"> - <label><translate translate-context="*/*/*">Category</translate></label> - <select class="ui dropdown" @change="addSearchToken('category', $event.target.value)" :value="getTokenValue('category', '')"> + <label for="artists-category"><translate translate-context="*/*/*">Category</translate></label> + <select id="artists-category" class="ui dropdown" @change="addSearchToken('category', $event.target.value)" :value="getTokenValue('category', '')"> <option value=""><translate translate-context="Content/*/Dropdown">All</translate></option> <option value="podcast">{{ sharedLabels.fields.content_category.choices.podcast }}</option> <option value="music">{{ sharedLabels.fields.content_category.choices.music }}</option> @@ -18,16 +18,16 @@ </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="artists-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="artists-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="artists-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> + <select id="artists-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> diff --git a/front/src/components/manage/library/EditsCardList.vue b/front/src/components/manage/library/EditsCardList.vue index abf998554..01196238e 100644 --- a/front/src/components/manage/library/EditsCardList.vue +++ b/front/src/components/manage/library/EditsCardList.vue @@ -4,14 +4,14 @@ <div class="ui inline form"> <div class="fields"> <div class="ui field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <label for="search-edits"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <form @submit.prevent="search.query = $refs.search.value"> - <input name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> + <input id="search-edits" name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> </form> </div> <div class="field"> - <label><translate translate-context="*/*/*">Status</translate></label> - <select class="ui dropdown" @change="addSearchToken('is_approved', $event.target.value)" :value="getTokenValue('is_approved', '')"> + <label for="edit-status"><translate translate-context="*/*/*">Status</translate></label> + <select id="edit-status" class="ui dropdown" @change="addSearchToken('is_approved', $event.target.value)" :value="getTokenValue('is_approved', '')"> <option value=""> <translate translate-context="Content/*/Dropdown">All</translate> </option> @@ -27,16 +27,16 @@ </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="edit-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="edit-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="edit-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label> + <select id="edit-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> diff --git a/front/src/components/manage/library/LibrariesTable.vue b/front/src/components/manage/library/LibrariesTable.vue index e3d487774..3cfb74739 100644 --- a/front/src/components/manage/library/LibrariesTable.vue +++ b/front/src/components/manage/library/LibrariesTable.vue @@ -3,14 +3,14 @@ <div class="ui inline form"> <div class="fields"> <div class="ui six wide field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <label for="libraries-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <form @submit.prevent="search.query = $refs.search.value"> - <input name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> + <input id="libraries-search" name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> </form> </div> <div class="field"> - <label><translate translate-context="*/*/*">Visibility</translate></label> - <select class="ui dropdown" @change="addSearchToken('privacy_level', $event.target.value)" :value="getTokenValue('privacy_level', '')"> + <label for="libraries-visibility"><translate translate-context="*/*/*">Visibility</translate></label> + <select id="libraries-visibility" class="ui dropdown" @change="addSearchToken('privacy_level', $event.target.value)" :value="getTokenValue('privacy_level', '')"> <option value=""><translate translate-context="Content/*/Dropdown">All</translate></option> <option value="me">{{ sharedLabels.fields.privacy_level.shortChoices.me }}</option> <option value="instance">{{ sharedLabels.fields.privacy_level.shortChoices.instance }}</option> @@ -18,16 +18,16 @@ </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="libraries-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="libraries-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="libraries-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> + <select id="libraries-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> diff --git a/front/src/components/manage/library/TagsTable.vue b/front/src/components/manage/library/TagsTable.vue index a746d3e9b..60fa74eac 100644 --- a/front/src/components/manage/library/TagsTable.vue +++ b/front/src/components/manage/library/TagsTable.vue @@ -3,22 +3,22 @@ <div class="ui inline form"> <div class="fields"> <div class="ui six wide field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <label for="tags-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <form @submit.prevent="search.query = $refs.search.value"> - <input name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> + <input id="tags-search" name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> </form> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="tags-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="tags-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="tags-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> + <select id="tags-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> diff --git a/front/src/components/manage/library/TracksTable.vue b/front/src/components/manage/library/TracksTable.vue index d44558da9..80d8c6efe 100644 --- a/front/src/components/manage/library/TracksTable.vue +++ b/front/src/components/manage/library/TracksTable.vue @@ -3,22 +3,22 @@ <div class="ui inline form"> <div class="fields"> <div class="ui six wide field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <label for="tracks-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <form @submit.prevent="search.query = $refs.search.value"> - <input name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> + <input id="tracks-search" name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> </form> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="tracks-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="tracks-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="tracks-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> + <select id="tracks-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> diff --git a/front/src/components/manage/library/UploadsTable.vue b/front/src/components/manage/library/UploadsTable.vue index 36cd31b50..1063b3a47 100644 --- a/front/src/components/manage/library/UploadsTable.vue +++ b/front/src/components/manage/library/UploadsTable.vue @@ -3,14 +3,14 @@ <div class="ui inline form"> <div class="fields"> <div class="ui six wide field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <label for="uploads-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <form @submit.prevent="search.query = $refs.search.value"> - <input name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> + <input id="uploads-search" name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> </form> </div> <div class="field"> - <label><translate translate-context="*/*/*">Visibility</translate></label> - <select class="ui dropdown" @change="addSearchToken('privacy_level', $event.target.value)" :value="getTokenValue('privacy_level', '')"> + <label for="uploads-visibility"><translate translate-context="*/*/*">Visibility</translate></label> + <select id="uploads-visibility" class="ui dropdown" @change="addSearchToken('privacy_level', $event.target.value)" :value="getTokenValue('privacy_level', '')"> <option value=""><translate translate-context="Content/*/Dropdown">All</translate></option> <option value="me">{{ sharedLabels.fields.privacy_level.shortChoices.me }}</option> <option value="instance">{{ sharedLabels.fields.privacy_level.shortChoices.instance }}</option> @@ -18,8 +18,8 @@ </select> </div> <div class="field"> - <label><translate translate-context="Content/*/*/Noun">Import status</translate></label> - <select class="ui dropdown" @change="addSearchToken('status', $event.target.value)" :value="getTokenValue('status', '')"> + <label for="uploads-status"><translate translate-context="Content/*/*/Noun">Import status</translate></label> + <select id="uploads-status" class="ui dropdown" @change="addSearchToken('status', $event.target.value)" :value="getTokenValue('status', '')"> <option value=""><translate translate-context="Content/*/Dropdown">All</translate></option> <option value="pending"><translate translate-context="Content/Library/*/Short">Pending</translate></option> <option value="skipped"><translate translate-context="Content/Library/*">Skipped</translate></option> @@ -28,16 +28,16 @@ </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="uploads-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="uploads-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="uploads-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> + <select id="uploads-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> diff --git a/front/src/components/manage/moderation/AccountsTable.vue b/front/src/components/manage/moderation/AccountsTable.vue index c613c4968..9b6782fc6 100644 --- a/front/src/components/manage/moderation/AccountsTable.vue +++ b/front/src/components/manage/moderation/AccountsTable.vue @@ -3,22 +3,22 @@ <div class="ui inline form"> <div class="fields"> <div class="ui six wide field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <label for="accounts-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <form @submit.prevent="search.query = $refs.search.value"> - <input name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> + <input id="accounts-search" name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> </form> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="accounts-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="accounts-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="accounts-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> + <select id="accounts-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> diff --git a/front/src/components/manage/moderation/DomainsTable.vue b/front/src/components/manage/moderation/DomainsTable.vue index 94a067bda..c4d96d6e3 100644 --- a/front/src/components/manage/moderation/DomainsTable.vue +++ b/front/src/components/manage/moderation/DomainsTable.vue @@ -3,28 +3,28 @@ <div class="ui inline form"> <div class="fields"> <div class="ui field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> - <input name="search" type="text" v-model="search" :placeholder="labels.searchPlaceholder" /> + <label for="domains-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <input id="domains-search" name="search" type="text" v-model="search" :placeholder="labels.searchPlaceholder" /> </div> <div class="field" v-if="allowListEnabled"> - <label><translate translate-context="Content/Moderation/*/Adjective">Is present on allow-list</translate></label> - <select class="ui dropdown" v-model="allowed"> + <label for="domains-allow-list"><translate translate-context="Content/Moderation/*/Adjective">Is present on allow-list</translate></label> + <select id="domains-allow-list" class="ui dropdown" v-model="allowed"> <option :value="null"><translate translate-context="Content/*/Dropdown">All</translate></option> <option :value="true"><translate translate-context="*/*/*">Yes</translate></option> <option :value="false"><translate translate-context="*/*/*">No</translate></option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="domains-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="domains-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="domains-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> + <select id="domains-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> diff --git a/front/src/components/manage/users/InvitationForm.vue b/front/src/components/manage/users/InvitationForm.vue index 55d37168b..f8a0e88e2 100644 --- a/front/src/components/manage/users/InvitationForm.vue +++ b/front/src/components/manage/users/InvitationForm.vue @@ -9,8 +9,8 @@ </div> <div class="inline fields"> <div class="ui field"> - <label><translate translate-context="Content/*/Input.Label">Invitation code</translate></label> - <input name="code" type="text" v-model="code" :placeholder="labels.placeholder" /> + <label for="invitation-code"><translate translate-context="Content/*/Input.Label">Invitation code</translate></label> + <input for="invitation-code" name="code" type="text" v-model="code" :placeholder="labels.placeholder" /> </div> <div class="ui field"> <button :class="['ui', {loading: isLoading}, 'button']" :disabled="isLoading" type="submit"> diff --git a/front/src/components/manage/users/InvitationsTable.vue b/front/src/components/manage/users/InvitationsTable.vue index be5a92c7b..ca72e4a5c 100644 --- a/front/src/components/manage/users/InvitationsTable.vue +++ b/front/src/components/manage/users/InvitationsTable.vue @@ -3,20 +3,20 @@ <div class="ui inline form"> <div class="fields"> <div class="ui field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> - <input name="search" type="text" v-model="search" :placeholder="labels.searchPlaceholder" /> + <label for="invitations-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <input id="invitations-search" name="search" type="text" v-model="search" :placeholder="labels.searchPlaceholder" /> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="invitations-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="invitations-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="*/*/*">Status</translate></label> - <select class="ui dropdown" v-model="isOpen"> + <label for="invitations-status"><translate translate-context="*/*/*">Status</translate></label> + <select id="invitations-status" class="ui dropdown" v-model="isOpen"> <option :value="null"><translate translate-context="Content/*/Dropdown">All</translate></option> <option :value="true"><translate translate-context="Content/Admin/Dropdown/Adjective">Open</translate></option> <option :value="false"><translate translate-context="Content/Admin/Dropdown/Adjective">Expired/used</translate></option> diff --git a/front/src/components/manage/users/UsersTable.vue b/front/src/components/manage/users/UsersTable.vue index 78afeae4e..910cff0ea 100644 --- a/front/src/components/manage/users/UsersTable.vue +++ b/front/src/components/manage/users/UsersTable.vue @@ -3,20 +3,20 @@ <div class="ui inline form"> <div class="fields"> <div class="ui field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> - <input name="search" type="text" v-model="search" :placeholder="labels.searchPlaceholder" /> + <label for="users-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <input id="users-search" name="search" type="text" v-model="search" :placeholder="labels.searchPlaceholder" /> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="users-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="users-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="users-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label> + <select id="users-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> diff --git a/front/src/components/playlists/Editor.vue b/front/src/components/playlists/Editor.vue index caa9a85c9..2bf06ff42 100644 --- a/front/src/components/playlists/Editor.vue +++ b/front/src/components/playlists/Editor.vue @@ -47,7 +47,7 @@ </translate> </div> - <dangerous-button :disabled="plts.length === 0" class="ui labeled right floated warning icon button" :action="clearPlaylist"> + <dangerous-button :disabled="plts.length === 0" class="ui labeled right floated danger icon button" :action="clearPlaylist"> <i class="eraser icon"></i> <translate translate-context="*/Playlist/Button.Label/Verb">Clear playlist</translate> <p slot="modal-header" v-translate="{playlist: playlist.name}" translate-context="Popup/Playlist/Title" :translate-params="{playlist: playlist.name}"> Do you want to clear the playlist "%{ playlist }"? diff --git a/front/src/components/playlists/Form.vue b/front/src/components/playlists/Form.vue index 855c3018f..757703dfd 100644 --- a/front/src/components/playlists/Form.vue +++ b/front/src/components/playlists/Form.vue @@ -19,17 +19,17 @@ </div> <div class="three fields"> <div class="field"> - <label><translate translate-context="Content/Playlist/Input.Label">Playlist name</translate></label> - <input name="name" v-model="name" required type="text" :placeholder="labels.placeholder" /> + <label for="playlist-name"><translate translate-context="Content/Playlist/Input.Label">Playlist name</translate></label> + <input id ="playlist-name" name="name" v-model="name" required type="text" :placeholder="labels.placeholder" /> </div> <div class="field"> - <label><translate translate-context="Content/Playlist/Dropdown.Label">Playlist visibility</translate></label> - <select class="ui dropdown" v-model="privacyLevel"> + <label for="playlist-visibility"><translate translate-context="Content/Playlist/Dropdown.Label">Playlist visibility</translate></label> + <select id="playlist-visibility" class="ui dropdown" v-model="privacyLevel"> <option :value="c.value" v-for="c in privacyLevelChoices">{{ c.label }}</option> </select> </div> <div class="field"> - <label> </label> + <span id="updatePlaylistLabel"></span> <button :class="['ui', 'fluid', {'loading': isLoading}, 'button']" type="submit"> <template v-if="playlist"><translate translate-context="Content/Playlist/Button.Label/Verb">Update playlist</translate></template> <template v-else><translate translate-context="Content/Playlist/Button.Label/Verb">Create playlist</translate></template> diff --git a/front/src/components/playlists/PlaylistModal.vue b/front/src/components/playlists/PlaylistModal.vue index e22f234fb..9a66d76bb 100644 --- a/front/src/components/playlists/PlaylistModal.vue +++ b/front/src/components/playlists/PlaylistModal.vue @@ -43,18 +43,17 @@ <div class="fields"> <div class="field"> <label for="playlist-name-filter"><translate translate-context="Popup/Playlist/Label">Filter</translate></label> - <input name="playlist-name-filter" v-model="playlistNameFilter" type="text" class="inline" :placeholder="labels.filterPlaylistField" /> + <input id="playlist-name-filter" v-model="playlistNameFilter" type="text" class="inline" :placeholder="labels.filterPlaylistField" /> </div> </div> </div> <table v-if="sortedPlaylists.length > 0" class="ui unstackable very basic table"> <thead> <tr> - <th></th> + <th><span class="visually-hidden"><translate translate-context="*/*/*/Verb">Edit</translate></span></th> <th><translate translate-context="*/*/*/Noun">Name</translate></th> <th class="sorted descending"><translate translate-context="Popup/Playlist/Table.Label/Short">Last modification</translate></th> <th><translate translate-context="*/*/*">Tracks</translate></th> - <th></th> </tr> </thead> <tbody> @@ -62,9 +61,10 @@ <td> <router-link class="ui icon basic small button" - :to="{name: 'library.playlists.detail', params: {id: playlist.id }, query: {mode: 'edit'}}"><i class="ui pencil icon"></i></router-link> + :to="{name: 'library.playlists.detail', params: {id: playlist.id }, query: {mode: 'edit'}}"><i class="ui pencil icon"></i> + <span class="visually-hidden"><translate translate-context="*/*/*/Verb">Edit</translate></span></router-link> </td> - <td :title="playlist.name"> + <td> <router-link v-on:click.native="update(false)" :to="{name: 'library.playlists.detail', params: {id: playlist.id }}">{{ playlist.name }}</router-link></td> <td><human-date :date="playlist.modification_date"></human-date></td> <td>{{ playlist.tracks_count }}</td> diff --git a/front/src/components/playlists/Widget.vue b/front/src/components/playlists/Widget.vue index a811c5b43..d3bdf8f13 100644 --- a/front/src/components/playlists/Widget.vue +++ b/front/src/components/playlists/Widget.vue @@ -1,6 +1,6 @@ <template> <div> - <h3 class="ui header"> + <h3 v-if="!!this.$slots.title" class="ui header"> <slot name="title"></slot> </h3> <div v-if="isLoading" class="ui inverted active dimmer"> diff --git a/front/src/components/radios/Card.vue b/front/src/components/radios/Card.vue index 3cc853dda..47bc0fe1a 100644 --- a/front/src/components/radios/Card.vue +++ b/front/src/components/radios/Card.vue @@ -18,7 +18,7 @@ <div class="ui hidden divider"></div> <radio-button class="right floated button" :type="type" :custom-radio-id="customRadioId" :object-id="objectId"></radio-button> <router-link - class="ui basic warning button right floated" + class="ui success button right floated" v-if="$store.state.auth.authenticated && type === 'custom' && radio.user.id === $store.state.auth.profile.id" :to="{name: 'library.radios.edit', params: {id: customRadioId }}"> <translate translate-context="Content/*/Button.Label/Verb">Edit</translate> diff --git a/front/src/style/_main.scss b/front/src/style/_main.scss index 1f054d081..95997ca56 100644 --- a/front/src/style/_main.scss +++ b/front/src/style/_main.scss @@ -16,7 +16,9 @@ $bottom-player-height: 4rem; @import "./components/_action_table.scss"; @import "./components/_album_card.scss"; +@import "./components/_attachment_input.scss"; @import "./components/_avatar.scss"; +@import "./components/_builder.scss"; @import "./components/_button.scss"; @import "./components/_card.scss"; @import "./components/_content_form.scss"; diff --git a/front/src/style/_vars.scss b/front/src/style/_vars.scss index dba9c2eef..749a9bbef 100644 --- a/front/src/style/_vars.scss +++ b/front/src/style/_vars.scss @@ -7,12 +7,12 @@ $vibrant-hover-color: #f26202 !default; $vibrant-active-color: #cf590c !default; $vibrant-focus-color: var(--vibrant-hover-color) !default; -$success-color: #21BA45 !default; -$success-hover-color: #16ab39 !default; +$success-color: #206b00 !default; +$success-hover-color: #3a9104 !default; $success-active-color: #198f35 !default; $success-focus-color: var(--$success-hover-color) !default; -$primary-color: #2185D0 !default; +$primary-color: #0050C6 !default; $primary-hover-color: #1678c2 !default; $primary-active-color: #1a69a4 !default; $primary-focus-color: var(--primary-hover-color) !default; @@ -32,9 +32,9 @@ $accent-hover-color: #009c95 !default; $accent-active-color: #00827c !default; $accent-focus-color: var(--accent-hover-color) !default; -$link-color: #4183C4 !default; +$link-color: #0d52bf !default; $link-text-decoration: none !default; -$link-hover-color: #1e70bf !default; +$link-hover-color: #002e99 !default; $link-hover-text-decoration: underline !default; $sidebar-background: #2D2F33 !default; diff --git a/front/src/style/components/_attachment_input.scss b/front/src/style/components/_attachment_input.scss new file mode 100644 index 000000000..872c39938 --- /dev/null +++ b/front/src/style/components/_attachment_input.scss @@ -0,0 +1,8 @@ +#avatarLabel { + display: block; + margin: 0 0 0.28571429rem 0; + color: var(--form-label-color); + font-size: 0.92857143em; + font-weight: bold; + text-transform: none; +} \ No newline at end of file diff --git a/front/src/style/components/_builder.scss b/front/src/style/components/_builder.scss new file mode 100644 index 000000000..c201c6044 --- /dev/null +++ b/front/src/style/components/_builder.scss @@ -0,0 +1,9 @@ +#radioFilterLabel { + display: block; + margin-top: 0.7rem; + margin-bottom: 0.7rem; +} + +#addFilter { + margin: 0 0 0 .85714286em; +} \ No newline at end of file diff --git a/front/src/style/components/_form.scss b/front/src/style/components/_form.scss index 9983d1fd3..1620204f9 100644 --- a/front/src/style/components/_form.scss +++ b/front/src/style/components/_form.scss @@ -1,6 +1,7 @@ .ui.checkbox label { cursor: pointer; + color: var(--text-color); } input + .help { @@ -28,3 +29,16 @@ input + .help { .ui.icon.input > .icon { color: var(--input-color); } + +#updatePlaylistLabel { + display: block; + margin: 0 0 0.28571429rem 0; + color: var(--form-label-color); + font-size: 0.92857143em; + font-weight: bold; + text-transform: none; +} + +#updatePlaylistLabel:before { + content: "\200b"; +} \ No newline at end of file diff --git a/front/src/style/globals/_layout.scss b/front/src/style/globals/_layout.scss index 90aa2742b..b7cfab2a7 100644 --- a/front/src/style/globals/_layout.scss +++ b/front/src/style/globals/_layout.scss @@ -165,3 +165,20 @@ .segment .ui.list .item { background-color: transparent; } + +.visually-hidden { + clip: rect(1px, 1px, 1px, 1px); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; +} + +.visually-hidden:focus { + clip: auto; + height: auto; + overflow: auto; + position: absolute; + width: auto; +} \ No newline at end of file diff --git a/front/src/style/pages/_artists.scss b/front/src/style/pages/_artists.scss index dd632dd53..76274ac31 100644 --- a/front/src/style/pages/_artists.scss +++ b/front/src/style/pages/_artists.scss @@ -1,7 +1,20 @@ #excludeCompilation { + margin-top: 0; +} + +#excludeCompilation input { margin-top: 0.7rem; } -.ui.multiple.search.dropdown > input.search { +#excludeHeader { + display: block; + margin: 0 0 0.28571429rem 0; + color: var(--form-label-color); + font-size: 0.92857143em; + font-weight: bold; + text-transform: none; +} + +.ui.multiple.search.dropdown>input.search { width: auto; } \ No newline at end of file diff --git a/front/src/views/Notifications.vue b/front/src/views/Notifications.vue index c0f6a6989..ab2de0038 100644 --- a/front/src/views/Notifications.vue +++ b/front/src/views/Notifications.vue @@ -15,10 +15,10 @@ <div class="ui bottom attached segment"> <form @submit.prevent="setDisplayDate('instance_support_message_display_date', instanceSupportMessageDelay)" class="ui inline form"> <div class="inline field"> - <label> + <label for="instance-reminder-delay"> <translate translate-context="Content/Notifications/Label">Remind me in:</translate> </label> - <select v-model="instanceSupportMessageDelay"> + <select id="instance-reminder-delay" v-model="instanceSupportMessageDelay"> <option :value="30"><translate translate-context="*/*/*">30 days</translate></option> <option :value="60"><translate translate-context="*/*/*">60 days</translate></option> <option :value="90"><translate translate-context="*/*/*">90 days</translate></option> @@ -49,10 +49,10 @@ <div class="ui bottom attached segment"> <form @submit.prevent="setDisplayDate('funkwhale_support_message_display_date', funkwhaleSupportMessageDelay)" class="ui inline form"> <div class="inline field"> - <label> + <label for="funkwhale-reminder-delay"> <translate translate-context="Content/Notifications/Label">Remind me in:</translate> </label> - <select v-model="funkwhaleSupportMessageDelay"> + <select id="funkwhale-reminder-delay" v-model="funkwhaleSupportMessageDelay"> <option :value="30"><translate translate-context="*/*/*">30 days</translate></option> <option :value="60"><translate translate-context="*/*/*">60 days</translate></option> <option :value="90"><translate translate-context="*/*/*">90 days</translate></option> @@ -69,8 +69,8 @@ </div> <h1 class="ui header"><translate translate-context="Content/Notifications/Title">Your notifications</translate></h1> <div class="ui toggle checkbox"> - <input v-model="filters.is_read" type="checkbox"> - <label><translate translate-context="Content/Notifications/Form.Label/Verb">Show read notifications</translate></label> + <input id="show-read-notifications" v-model="filters.is_read" type="checkbox"> + <label for="show-read-notifications"><translate translate-context="Content/Notifications/Form.Label/Verb">Show read notifications</translate></label> </div> <div v-if="filters.is_read === false && notifications.count > 0" diff --git a/front/src/views/Search.vue b/front/src/views/Search.vue index 0ac74162d..002628603 100644 --- a/front/src/views/Search.vue +++ b/front/src/views/Search.vue @@ -15,7 +15,7 @@ <div class="ui field"> <div class="ui action input"> <input class="ui input" id="query" name="query" type="text" v-model="query"> - <button type="submit" class="ui icon button"> + <button :aria-label="labels.submitSearch" type="submit" class="ui icon button"> <i class="search icon"></i> </button> </div> @@ -134,6 +134,7 @@ export default { }, computed: { labels() { + let submitSearch = this.$pgettext("Content/Search/Button.Label/Verb", "Submit Search Query") let title = this.$pgettext("Content/Search/Input.Label/Noun", "Search") if (this.initialId) { title = this.$pgettext('Head/Fetch/Title', "Search a remote object") @@ -143,6 +144,7 @@ export default { } return { title, + submitSearch } }, types () { diff --git a/front/src/views/admin/moderation/AccountsDetail.vue b/front/src/views/admin/moderation/AccountsDetail.vue index b04910205..caf02f229 100644 --- a/front/src/views/admin/moderation/AccountsDetail.vue +++ b/front/src/views/admin/moderation/AccountsDetail.vue @@ -151,9 +151,10 @@ <td> <div class="ui toggle checkbox" v-if="object.user.username != $store.state.auth.profile.username"> <input + id="is-active" @change="updateUser('is_active')" v-model="object.user.is_active" type="checkbox"> - <label> + <label for="is-active"> <translate v-if="object.user.is_active" key="1" translate-context="*/*/*/State of feature">Enabled</translate> <translate v-else key="2" translate-context="*/*/*/State of feature">Disabled</translate> </label> diff --git a/front/src/views/admin/moderation/DomainsList.vue b/front/src/views/admin/moderation/DomainsList.vue index 486ea8722..34bff4d92 100644 --- a/front/src/views/admin/moderation/DomainsList.vue +++ b/front/src/views/admin/moderation/DomainsList.vue @@ -11,8 +11,8 @@ </div> <div class="inline fields"> <div class="field"> - <label for="domain"><translate translate-context="Content/Moderation/Form.Label/Verb">Add a domain</translate></label> - <input type="text" name="domain" id="domain" v-model="domainName"> + <label for="add-domain"><translate translate-context="Content/Moderation/Form.Label/Verb">Add a domain</translate></label> + <input type="text" name="domain" id="add-domain" v-model="domainName"> </div> <div class="field" v-if="allowListEnabled"> <input type="checkbox" name="allowed" id="allowed" v-model="domainAllowed"> @@ -20,7 +20,7 @@ </div> <div class="field"> <button :class="['ui', {'loading': isCreating}, 'success', 'button']" type="submit" :disabled="isCreating"> - <label for="domain"><translate translate-context="Content/Moderation/Button/Verb">Add</translate></label> + <translate translate-context="Content/Moderation/Button/Verb">Add</translate> </button> </div> </div> diff --git a/front/src/views/admin/moderation/ReportsList.vue b/front/src/views/admin/moderation/ReportsList.vue index 2833ac05c..5660c01d5 100644 --- a/front/src/views/admin/moderation/ReportsList.vue +++ b/front/src/views/admin/moderation/ReportsList.vue @@ -6,14 +6,14 @@ <div class="ui inline form"> <div class="fields"> <div class="ui field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <label for="reports-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <form @submit.prevent="search.query = $refs.search.value"> - <input name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> + <input id="reports-search" name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> </form> </div> <div class="field"> - <label><translate translate-context="*/*/*">Status</translate></label> - <select class="ui dropdown" @change="addSearchToken('resolved', $event.target.value)" :value="getTokenValue('resolved', '')"> + <label for="reports-status"><translate translate-context="*/*/*">Status</translate></label> + <select id="reports-status" class="ui dropdown" @change="addSearchToken('resolved', $event.target.value)" :value="getTokenValue('resolved', '')"> <option value=""> <translate translate-context="Content/*/Dropdown">All</translate> </option> @@ -32,16 +32,16 @@ :label="true" :value="getTokenValue('category', '')"></report-category-dropdown> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="reports-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="reports-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="reports-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label> + <select id="reports-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> diff --git a/front/src/views/admin/moderation/RequestsList.vue b/front/src/views/admin/moderation/RequestsList.vue index 921783246..614694fd2 100644 --- a/front/src/views/admin/moderation/RequestsList.vue +++ b/front/src/views/admin/moderation/RequestsList.vue @@ -6,14 +6,14 @@ <div class="ui inline form"> <div class="fields"> <div class="ui field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <label for="requests-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <form @submit.prevent="search.query = $refs.search.value"> - <input name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> + <input id="requests-search" name="search" ref="search" type="text" :value="search.query" :placeholder="labels.searchPlaceholder" /> </form> </div> <div class="field"> - <label><translate translate-context="*/*/*">Status</translate></label> - <select class="ui dropdown" @change="addSearchToken('status', $event.target.value)" :value="getTokenValue('status', '')"> + <label for="requests-status"><translate translate-context="*/*/*">Status</translate></label> + <select id="requests-status" class="ui dropdown" @change="addSearchToken('status', $event.target.value)" :value="getTokenValue('status', '')"> <option value=""> <translate translate-context="Content/*/Dropdown">All</translate> </option> @@ -29,16 +29,16 @@ </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="requests-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="requests-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="requests-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label> + <select id="requests-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> diff --git a/front/src/views/auth/EmailConfirm.vue b/front/src/views/auth/EmailConfirm.vue index 4e255ffb9..aec373d14 100644 --- a/front/src/views/auth/EmailConfirm.vue +++ b/front/src/views/auth/EmailConfirm.vue @@ -11,8 +11,8 @@ </ul> </div> <div class="field"> - <label><translate translate-context="Content/Signup/Form.Label">Confirmation code</translate></label> - <input name="confirmation-code" type="text" required v-model="key" /> + <label for="confirmation-code"><translate translate-context="Content/Signup/Form.Label">Confirmation code</translate></label> + <input id="confirmation-code" name="confirmation-code" type="text" required v-model="key" /> </div> <router-link :to="{path: '/login'}"> <translate translate-context="Content/Signup/Link/Verb">Return to login</translate> diff --git a/front/src/views/auth/PasswordReset.vue b/front/src/views/auth/PasswordReset.vue index 0a7ff836f..583ff2e2a 100644 --- a/front/src/views/auth/PasswordReset.vue +++ b/front/src/views/auth/PasswordReset.vue @@ -12,8 +12,9 @@ </div> <p><translate translate-context="Content/Signup/Paragraph">Use this form to request a password reset. We will send an email to the given address with instructions to reset your password.</translate></p> <div class="field"> - <label><translate translate-context="Content/Signup/Input.Label">Account's email</translate></label> + <label for="account-email"><translate translate-context="Content/Signup/Input.Label">Account's email</translate></label> <input + id="account-email" required ref="email" type="email" diff --git a/front/src/views/auth/PasswordResetConfirm.vue b/front/src/views/auth/PasswordResetConfirm.vue index d9471d6fe..a10d664f8 100644 --- a/front/src/views/auth/PasswordResetConfirm.vue +++ b/front/src/views/auth/PasswordResetConfirm.vue @@ -12,8 +12,8 @@ </div> <template v-if="token && uid"> <div class="field"> - <label><translate translate-context="Content/Settings/Input.Label">New password</translate></label> - <password-input v-model="newPassword" /> + <label for="password-field"><translate translate-context="Content/Settings/Input.Label">New password</translate></label> + <password-input field-id="password-field" v-model="newPassword" /> </div> <router-link :to="{path: '/login'}"> <translate translate-context="Content/Signup/Link">Back to login</translate> diff --git a/front/src/views/auth/ProfileActivity.vue b/front/src/views/auth/ProfileActivity.vue index bd893c967..d378547d6 100644 --- a/front/src/views/auth/ProfileActivity.vue +++ b/front/src/views/auth/ProfileActivity.vue @@ -5,6 +5,7 @@ <h2 class="ui header"> <translate translate-context="Content/Home/Title">Recently listened</translate> </h2> + <div class="ui divider"></div> <track-widget @count="recentActivity = $event" :url="'history/listenings/'" @@ -16,6 +17,7 @@ <h2 class="ui header"> <translate translate-context="Content/Home/Title">Recently favorited</translate> </h2> + <div class="ui divider"></div> <track-widget :url="'favorites/tracks/'" :filters="{scope: `actor:${object.full_username}`, ordering: '-creation_date'}"></track-widget> </div> <div class="ui hidden divider"></div> @@ -23,6 +25,7 @@ <h2 class="ui header"> <translate translate-context="*/*/*">Playlists</translate> </h2> + <div class="ui divider"></div> <playlist-widget :url="'playlists/'" :filters="{scope: `actor:${object.full_username}`, playable: true, ordering: '-modification_date'}"> </playlist-widget> </div> diff --git a/front/src/views/auth/ProfileBase.vue b/front/src/views/auth/ProfileBase.vue index 3d43e6cb2..c8a74a53a 100644 --- a/front/src/views/auth/ProfileBase.vue +++ b/front/src/views/auth/ProfileBase.vue @@ -30,7 +30,7 @@ <img class="ui big circular image" v-else v-lazy="$store.getters['instance/absoluteUrl'](object.icon.square_crop)" /> <div class="ellispsis content"> <div class="ui very small hidden divider"></div> - <span :title="displayName">{{ displayName }}</span> + <span>{{ displayName }}</span> <div class="ui very small hidden divider"></div> <div class="sub header ellipsis" :title="object.full_username"> {{ object.full_username }} diff --git a/front/src/views/auth/ProfileOverview.vue b/front/src/views/auth/ProfileOverview.vue index fdf47024b..bd61d63af 100644 --- a/front/src/views/auth/ProfileOverview.vue +++ b/front/src/views/auth/ProfileOverview.vue @@ -28,10 +28,9 @@ <translate translate-context="Content/Profile/Button">Add new</translate> </router-link> </div> - </h2> <library-widget :url="`federation/actors/${object.full_username}/libraries/`"> - <translate translate-context="Content/Profile/Paragraph" slot="subtitle">This user shared the following libraries.</translate> + <translate translate-context="Content/Profile/Paragraph" slot="title">This user shared the following libraries...</translate> </library-widget> </div> diff --git a/front/src/views/content/libraries/Card.vue b/front/src/views/content/libraries/Card.vue index 512b890a4..ca8e53e25 100644 --- a/front/src/views/content/libraries/Card.vue +++ b/front/src/views/content/libraries/Card.vue @@ -46,7 +46,7 @@ <translate translate-context="Content/Library/Card.Button.Label/Verb">Upload</translate> </router-link> <router-link :to="{name: 'library.detail', params: {id: library.uuid}}" exact class="ui button"> - <translate translate-context="Content/Library/Card.Button.Label/Noun">Details</translate> + <translate translate-context="Content/Library/Card.Button.Label/Noun">Library Details</translate> </router-link> </div> </div> diff --git a/front/src/views/content/libraries/FilesTable.vue b/front/src/views/content/libraries/FilesTable.vue index f91485b62..deb0804a6 100644 --- a/front/src/views/content/libraries/FilesTable.vue +++ b/front/src/views/content/libraries/FilesTable.vue @@ -3,7 +3,7 @@ <div class="ui inline form"> <div class="fields"> <div class="ui six wide field"> - <label> + <label for="files-search"> <translate translate-context="Content/Search/Input.Label/Noun">Search</translate> </label> <form @submit.prevent="search.query = $refs.search.value"> @@ -11,16 +11,18 @@ name="search" ref="search" type="text" + id="files-search" :value="search.query" :placeholder="labels.searchPlaceholder" /> </form> </div> <div class="field"> - <label> + <label for="import-status"> <translate translate-context="Content/*/*/Noun">Import status</translate> </label> <select + id="import-status" class="ui dropdown" @change="addSearchToken('status', $event.target.value)" :value="getTokenValue('status', '')" @@ -46,10 +48,10 @@ </select> </div> <div class="field"> - <label> + <label for="ordering-select"> <translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate> </label> - <select class="ui dropdown" v-model="ordering"> + <select id="ordering-select" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]" @@ -57,10 +59,10 @@ </select> </div> <div class="field"> - <label> + <label for="ordering-direction"> <translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate> </label> - <select class="ui dropdown" v-model="orderingDirection"> + <select id="ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"> <translate translate-context="Content/Search/Dropdown">Ascending</translate> </option> @@ -131,7 +133,6 @@ <span class="discrete link" @click="addSearchToken('artist', scope.obj.track.artist.name)" - :title="scope.obj.track.artist.name" >{{ scope.obj.track.artist.name|truncate(20) }}</span> </td> <td> @@ -139,7 +140,6 @@ v-if="scope.obj.track.album" class="discrete link" @click="addSearchToken('album', scope.obj.track.album.title)" - :title="scope.obj.track.album.title" >{{ scope.obj.track.album.title|truncate(20) }}</span> </td> </template> @@ -160,6 +160,7 @@ <button class="ui tiny basic icon button" :title="sharedLabels.fields.import_status.detailTitle" + :aria-label="labels.showStatus" @click="detailedUpload = scope.obj; showUploadDetailModal = true" > <i class="question circle outline icon"></i> @@ -287,7 +288,8 @@ export default { searchPlaceholder: this.$pgettext( "Content/Library/Input.Placeholder", "Search by title, artist, album…" - ) + ), + showStatus: this.$pgettext('Content/Library/Button.Label/Verb', 'Show information about the upload status for this track') }; }, actionFilters() { diff --git a/front/src/views/content/libraries/Form.vue b/front/src/views/content/libraries/Form.vue index 023073ca8..0dafdf155 100644 --- a/front/src/views/content/libraries/Form.vue +++ b/front/src/views/content/libraries/Form.vue @@ -8,17 +8,17 @@ </ul> </div> <div class="required field"> - <label><translate translate-context="*/*/*/Noun">Name</translate></label> - <input name="name" v-model="currentName" :placeholder="labels.namePlaceholder" required maxlength="100"> + <label for="current-name"><translate translate-context="*/*/*/Noun">Name</translate></label> + <input id="current-name" name="name" v-model="currentName" :placeholder="labels.namePlaceholder" required maxlength="100"> </div> <div class="field"> - <label><translate translate-context="*/*/*/Noun">Description</translate></label> - <textarea v-model="currentDescription" :placeholder="labels.descriptionPlaceholder" maxlength="2000"></textarea> + <label for="current-description"><translate translate-context="*/*/*/Noun">Description</translate></label> + <textarea id="current-description" v-model="currentDescription" :placeholder="labels.descriptionPlaceholder" maxlength="2000"></textarea> </div> <div class="field"> - <label><translate translate-context="*/*/*">Visibility</translate></label> + <label for="visibility-level"><translate translate-context="*/*/*">Visibility</translate></label> <p><translate translate-context="Content/Library/Paragraph">You are able to share your library with other people, regardless of its visibility.</translate></p> - <select class="ui dropdown" v-model="currentVisibilityLevel"> + <select id="visibility-level" class="ui dropdown" v-model="currentVisibilityLevel"> <option :value="c" v-for="c in ['me', 'instance', 'everyone']">{{ sharedLabels.fields.privacy_level.choices[c] }}</option> </select> </div> diff --git a/front/src/views/content/libraries/Home.vue b/front/src/views/content/libraries/Home.vue index 9a491bf54..59e720cdf 100644 --- a/front/src/views/content/libraries/Home.vue +++ b/front/src/views/content/libraries/Home.vue @@ -9,7 +9,7 @@ <p v-if="libraries.length == 0"> <translate translate-context="Content/Library/Paragraph">Looks like you don't have a library, it's time to create one.</translate> </p> - <a @click="hiddenForm = !hiddenForm"> + <a :aria-expanded="!hiddenForm" @click="hiddenForm = !hiddenForm"> <i class="plus icon" v-if="hiddenForm" /> <i class="minus icon" v-else /> <translate translate-context="Content/Library/Link/Verb">Create a new library</translate> diff --git a/front/src/views/content/remote/Card.vue b/front/src/views/content/remote/Card.vue index 4d317928c..f1836c726 100644 --- a/front/src/views/content/remote/Card.vue +++ b/front/src/views/content/remote/Card.vue @@ -85,13 +85,13 @@ </div> </div> <div class="extra content"> - <actor-link :actor="library.actor" /> + <actor-link style="color: var(--link-color)" :actor="library.actor" /> </div> <div v-if="displayCopyFid" class="extra content"> <div class="ui form"> <div class="field"> - <label><translate translate-context="Content/Library/Title">Sharing link</translate></label> - <copy-input :button-classes="'basic'" :value="library.fid" /> + <label :for="library.fid"><translate translate-context="Content/Library/Title">Sharing link</translate></label> + <copy-input :id="library.fid" :button-classes="'basic'" :value="library.fid" /> </div> </div> </div> diff --git a/front/src/views/content/remote/ScanForm.vue b/front/src/views/content/remote/ScanForm.vue index d52404d1b..af5fa6e40 100644 --- a/front/src/views/content/remote/ScanForm.vue +++ b/front/src/views/content/remote/ScanForm.vue @@ -7,10 +7,10 @@ </ul> </div> <div class="ui field"> - <label><translate translate-context="Content/Library/Input.Label/Verb">Search a remote library</translate></label> + <label for="library-search"><translate translate-context="Content/Library/Input.Label/Verb">Search a remote library</translate></label> <div :class="['ui', 'action', {loading: isLoading}, 'input']"> - <input name="url" v-model="query" :placeholder="labels.placeholder" type="url"> - <button type="submit" :class="['ui', 'icon', {loading: isLoading}, 'button']"> + <input id="library-search" name="url" v-model="query" :placeholder="labels.placeholder" type="url"> + <button :aria-label="labels.submitLibrarySearch" type="submit" :class="['ui', 'icon', {loading: isLoading}, 'button']"> <i class="search icon"></i> </button> </div> @@ -47,9 +47,9 @@ export default { }, computed: { labels () { - let placeholder = this.$pgettext('Content/Library/Input.Placeholder', 'Enter a library URL') return { - placeholder + placeholder: this.$pgettext('Content/Library/Input.Placeholder', 'Enter a library URL'), + submitLibrarySearch: this.$pgettext('Content/Library/Input.Label', 'Submit search') } } } diff --git a/front/src/views/library/DetailBase.vue b/front/src/views/library/DetailBase.vue index ffbd66f04..7f82ee61e 100644 --- a/front/src/views/library/DetailBase.vue +++ b/front/src/views/library/DetailBase.vue @@ -27,7 +27,7 @@ <div class="ui hidden divider"></div> <div class="ellipsis content"> <i class="layer group small icon"></i> - <span :title="object.name">{{ object.name }}</span> + <span>{{ object.name }}</span> <div class="ui very small hidden divider"></div> <div class="sub header ellipsis" :title="object.full_username"> <actor-link :avatar="false" :actor="object.actor" :truncate-length="0"> @@ -74,13 +74,15 @@ :can-update="false"></rendered-description> <div class="ui hidden divider"></div> </template> - <h5 class="ui header"> - <label for="copy-input"> - <translate translate-context="Content/Library/Title">Sharing link</translate> - </label> - </h5> - <p><translate translate-context="Content/Library/Paragraph">Share this link with other users so they can request access to this library by copy-pasting it in their pod search bar.</translate></p> - <copy-input :value="object.fid" /> + <div class="ui form"> + <div class="field"> + <label for="copy-input"> + <translate translate-context="Content/Library/Title">Sharing link</translate> + </label> + <p><translate translate-context="Content/Library/Paragraph">Share this link with other users so they can request access to this library by copy-pasting it in their pod search bar.</translate></p> + <copy-input :value="object.fid" /> + </div> + </div> </div> <div class="ui eleven wide column"> <div class="ui head vertical stripe segment"> diff --git a/front/src/views/playlists/List.vue b/front/src/views/playlists/List.vue index 83b29897f..ddfabd6fb 100644 --- a/front/src/views/playlists/List.vue +++ b/front/src/views/playlists/List.vue @@ -5,38 +5,38 @@ <template v-if="$store.state.auth.authenticated"> <button @click="$store.commit('playlists/chooseTrack', null)" - class="ui basic success button"><translate translate-context="Content/Playlist/Button.Label/Verb">Manage your playlists</translate></button> + class="ui success button"><translate translate-context="Content/Playlist/Button.Label/Verb">Manage your playlists</translate></button> <div class="ui hidden divider"></div> </template> <form :class="['ui', {'loading': isLoading}, 'form']" @submit.prevent="updateQueryString();fetchData()"> <div class="fields"> <div class="field"> - <label><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> + <label for="playlists-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label> <div class="ui action input"> - <input type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> + <input id="playlists-search" stype="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> <button class="ui icon button" type="submit" :aria-label="$pgettext('Content/Search/Input.Label/Noun', 'Search')"> <i class="search icon"></i> </button> </div> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> - <select class="ui dropdown" v-model="ordering"> + <label for="playlists-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> + <select id="playlists-ordering" class="ui dropdown" v-model="ordering"> <option v-for="option in orderingOptions" :value="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label> - <select class="ui dropdown" v-model="orderingDirection"> + <label for="playlists-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label> + <select id="playlists-ordering-direction" class="ui dropdown" v-model="orderingDirection"> <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> </select> </div> <div class="field"> - <label><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label> - <select class="ui dropdown" v-model="paginateBy"> + <label for="playlists-results"><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label> + <select id="playlists-results" class="ui dropdown" v-model="paginateBy"> <option :value="parseInt(12)">12</option> <option :value="parseInt(25)">25</option> <option :value="parseInt(50)">50</option> -- GitLab