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">&nbsp;<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>&nbsp;
           <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>&nbsp;
           <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>&nbsp;</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>&nbsp;</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 @@
               &nbsp;
               <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>&nbsp;</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>