diff --git a/front/src/components/About.vue b/front/src/components/About.vue
index 2c019918f7ef9630deacc8af4e3c9dc4e9182a71..d155caa59ae8dda41f2453bfa0b7f2bcabcb40f1 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 efa476c4ca54e804b09c6e9166ba6b2f4cfe5e7d..76426aac5c96f30ef2613444056ae04325b79909 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 fa72b2b799fe020aa8faba901bed407c2ad1f5e7..f10fd1da13269c65676ff116cfeae33389408f54 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 6333d9f57bd779c429ff78d55ce58f56846f62a6..64bd6dc21db9625b94c87e8f26efa2ae859e90cb 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 2383f3a53c2e2be1336cfc10d3a95e927dc0deed..8dc0aa984f545962f81202a287eeef41ded8e58d 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 2e5c24bc2acf72eaeec2cb1a6ad2dd5a0c2b8ffb..d0d7b27f1e9236045fc4e154e1a3dfbd571ffe11 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 79e8e0043da3bd5f625163935d46f81d9529e674..a6663a9f3b9dac71719a9c7aa4d5d86c70714834 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 fa471238791dde9833e528bfb0d2e35cd4295083..66de6ecdf11d5209366d66ecda46cb043a47c0cb 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 211c1e924132b0a7557838f4bd7ae8530c626aa2..ab6738390820d5c50308f21840dbc9f04a3fd581 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 f094ff96af59ab15051e7bee7f6a44e7dfccf605..5e58c885a0a1a4dd1127b334ba057ca32f457aac 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 50412501ad65341434ecbc8bc2e6053a9c3809c1..c7a2c0b758022b368a379eaf328915f1d2b45507 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 a5d127135b85af67b49611b46c1a1ef7c6123064..f1848f74fe6d687bbd9d1026dfb24a5fd28132c7 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 ce72ab9ddf7f19d7f294e637c02c622b42bf569b..d254ac645c1c919233ca428f2f21af6262ac46a2 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 8abb6927dd6ac598dec2928327fcb63993383ffc..dbbb2c46fe3b64fb7e309c72b5e07f3cf760c15d 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 abf5cadc40bf537dcdac5155c7cfd632fb4ddbe1..6323ab87b40f9841e525b6bdbb4d48d64b0ca39c 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 2fcba86cf261fa62eb55f72630a27c49f1539b63..43a9069f81c3c25b73850d9714bcc0bc83d2672a 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 a011e87534cd7f3409236c806a05f612315f6a12..ee0aec6ac8e1812ee334143f851eb7dceda1b8d3 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 b22ade7af6c00c4882a5a590525f7faec58aec25..2b90d2e85aa878139ab6a09b6c741ec1ae011468 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 1700de08c46cd710554d710df3d5267fd7cdd33b..34449bd1acc4364693b390fd8dc97529e40dbe2d 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 3f6eb0edb1a6720f4eed19b6a38a87d988f5391c..6d19b81cf5b0a0953ee4393bed46c5db59f5ffa9 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 0ba249db8dde8cbf114298afe171429d9194caaa..f07d0dcf347de11c8a22d8569c7083dc9a5ef631 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 c44a51299b5cfc3141089c8cc9ca832c9e0df4b3..d037da3c343fdce882a01aa07bcda203a75822ed 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 4c1cb6b41bbbac467e460e89ca347a4cd9ddaac6..74622f1fa8c395db0e0e0547e01c77411c5fe28d 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 20a6a2f15f16316ecc19164db7419ee9c4b9e387..4e6d89d52d07754de8c85913c543f4b408149224 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 2d0543ea653cb293af084690e9c8969553a1b69e..a56bc717c92520d434167cf9f3670da8e647941d 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 6aabb88171780e167509079bbbe2abf60788ccdc..b43ec41f4f1cf7bf554fdf9bd0df52ded1022a85 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 bab61858ae57dfd862972f07679e23c027e0390d..3ee0c93404f29cb6efdf261561f4a37fe31658f5 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 2e4f227a59f879006d97846ae2d7a99002e85ec8..98c1097f6d26f119d9ae18b50c1976d495a5e6e0 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 4f9bcd8b2b25a1418f648c5d42345374ee6e258e..3e622515a0a48818e379c6fa0ff2792616a0f196 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 d01d0a9ddfb824650aa5b46b53137dbcb1cc2b30..4bd8266554464b4de192335f9b1923dc6c40dd4a 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 3960863bc4df33fb800b47708934b02800afddab..e4dd6a4a72432fd503fa26ebf9e55f1f39b3036b 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 fb046719225f14b19e9b2370b16e4d506aea9f52..8fd6142ac1f58c73939c9074b644982ea9405a99 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 5b2fa0c96e7c9d333a42cc15e213de683987eea1..e9db226057f40f1ceb9494198680555837e7c772 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 93b64e17d64b1ea79ffafee61f492182b94ae170..6410942ba1ac1ab0ee918ecb30f0efd5cccb5438 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 d6ff9f5259e248cd7fdb45b440d2a7dc75e9dd49..d583c5cb4aea387aa51f93df3ea82f8493c41d29 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 af6b15ab49eace521dfde8c86adcb85a2cf68d50..1a70c084710ff9229d00412d2f3b79aff3948a99 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 794124cdb1a2bd9b0b0cb95f632b4524e8091d73..04d6a9fdd25bba93058ca9266111648f3cc8fb76 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 72f5aee4c5e283faa291f354eea0298814a7060b..cd71fc02e221bd43623b0cda4b59d256cee09b17 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 3790d92e0e26ddb83c7e811f885b0175f1f5745b..5babb6f53d7f798d5559d7177eeb08065edd1892 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 abf9985544c939f514b42e18eb80b1ebf6f1e722..01196238e775ab3cdaa7d6babf99fac519734cf9 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 e3d487774486bd32214e92963fc9b2dc24a6fff6..3cfb74739a38aa7f839a1a17f9c33c7696b79042 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 a746d3e9b29391f3f74a42aaddf080c4efa0ccfc..60fa74eacc847f420f85fb862da883d735c9aa78 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 d44558da920d7ed73e0123141df02f54f03631ac..80d8c6efeb9b9fc54a5e1364ae54c9100373a438 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 36cd31b50cb93ab35933bdfeead57f0530c510f5..1063b3a4724a9a7364069843b5bd92cce7f8d16a 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 c613c496800c1cc327fecf6557d8162553093819..9b6782fc61bca38828dff21b2f8070b773f789f0 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 94a067bda30b242647d9b261b864100979382ac9..c4d96d6e39c8ba992ea76b1ae0003260d574f0af 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 55d37168b5e2b70ce86bc93ed385dfaf5166ab1e..f8a0e88e222a03314225e468fdcd4bb021073016 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 be5a92c7b61af1b858e5c1e06ee61f621c992d48..ca72e4a5c321efe92141d1bf297854a63ed9a314 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 78afeae4ebcc108ae7890d009c0b7268c15c3fd1..910cff0eaec17d65ce46046938b13b58ac1c77ee 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 caa9a85c9e957dde32a363087ef1f6a69416ab94..2bf06ff424c43dc8dd47c117df1e77f581660e98 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 855c3018f7c98bcd0f1d5b0d34ea9d85f837f13c..757703dfd4879bfaa631405d92a20c43f689575d 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 e22f234fbc9ec2008438c7dfc830ef23e267f291..9a66d76bb1dfa06277a086378296cbff190c4d33 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 a811c5b43e7ed7a6645640cda02e65337500a1da..d3bdf8f13a1cf992bb65ef9a071df21f3c6d4844 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 3cc853ddaefc3d2fee535d7ece5dea7658af97e3..47bc0fe1a98fea02d2cf7e44b21fc95d8162367e 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 1f054d08151412b0eb0b481ed758ff831f8e4fe5..95997ca56aaa1e416f8c7e87b7e0875cc7b41ae9 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 dba9c2eefb76d7648708e501c56093d3fd90718d..749a9bbefa9e36c1591fbe18a3b1374747527172 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 0000000000000000000000000000000000000000..872c39938c3ef89e9952b31006909de67b6dc6e8
--- /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 0000000000000000000000000000000000000000..c201c6044ee2b208b111a0d8654ece0c367d1bd6
--- /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 9983d1fd36087118b822495e10df5373ecc59db7..1620204f9b9afa1447f35c3f2c21505e0e765919 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 90aa2742b138ca22cdb70e6adb1a379ff96f254d..b7cfab2a76f17bf78bcd9d97c3f4c6be337e85c6 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 dd632dd53946501607eb2c4bf9ef5e767dc5db7d..76274ac31236b4e6959adf6b28f7fcc7e8d15812 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 c0f6a69897c7e1954d4283f897b225bc4c2a2802..ab2de0038b39a9fead91c267627171bb486eeb04 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 0ac74162d42831ae51c417c91265ae528cffc5b3..002628603ff340da14997e2812dd39b53a557de5 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 b04910205b179cb627cf15e51177392d8c111806..caf02f2292f8c58e9f59726eb2337b65260831ea 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 486ea8722e738a202cc5bc05127878854864c5ba..34bff4d92a47ce08b9639fe7a9e4f675ed8037af 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 2833ac05c5fc9260a68de974e8391a935bfa3b90..5660c01d5ce68d9a788c27fe5f3f1197befefdb7 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 9217832469f509b8896d2ea614606a48a0e921e9..614694fd2f0d8340899a2dd84be9607f50288963 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 4e255ffb9c454f0eb2f7fdb55af4343e93c926d0..aec373d143ef7afc936945723550d3714ef7d62b 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 0a7ff836fba29629253ecab163fcfbaaa7657705..583ff2e2a45042a0b98755be23a7b104496fc5ec 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 d9471d6fef43ac37089ad3f304a6f2ca7ca57efc..a10d664f801291c2aca1b54c7ff374978c3b9572 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 bd893c9672f029e3ccbc99901b80cf3b4407b510..d378547d6841539a8ceafb0374019bfc7d84189b 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 3d43e6cb26cacfdf8585f2e6d950e2d321334737..c8a74a53a712fe4663ed0850aeba9930734aaeb7 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 fdf47024b2e3167f27d1d4810abb45d864c25aba..bd61d63af3084a7572ffccb77b5f1457b87b5da7 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 512b890a472d38456b233aebcd3f2018407ed2c9..ca8e53e25482392eec67b5f649bffdcf8347a9fe 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 f91485b6228a955101c396ba6c7e68569497827a..deb0804a679c391eea92978a28dfd8d316814b3e 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 023073ca86e3ec80d07ac7f362c660abfb2d99b8..0dafdf1557b8c67a1f3ccdfc380ec3bd191ea23f 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 9a491bf54743ae2646b8de970baa2f09c52bb2c2..59e720cdfe88a5e5ce77d1ddf47b3802ad0e6c61 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 4d317928caf7989b3450db0df5fd38d2fa662169..f1836c726d27cc7253ce12c8d2bef19781cd35d9 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 d52404d1bf41b2c00ae4b58ef981ea171f8b8896..af5fa6e40b829b2c07b4d61af65d21f1e8ee6eb2 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 ffbd66f047c4ca98c415e5b7db83b2bc598576d9..7f82ee61e34c5ca60d70e6f11bf8b92342a80200 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 83b29897f67b2d0b3c40b90033f585c96eb1a69e..ddfabd6fb4bcacfd08aa7335fabaf95f2a16e4c1 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>