diff --git a/changes/changelog.d/612.enhancement b/changes/changelog.d/612.enhancement
new file mode 100644
index 0000000000000000000000000000000000000000..092f3ec5d0a73087448a1a4e745244570c986369
--- /dev/null
+++ b/changes/changelog.d/612.enhancement
@@ -0,0 +1 @@
+Improved accessibility by using main/section/nav tags and aria-labels in most critical places (#612)
diff --git a/front/src/components/About.vue b/front/src/components/About.vue
index 438fed67db518c4b99a29b8cf7b3a870d8e5c412..16c58b173bb9d531dfb78c1de0854f60ac06dde1 100644
--- a/front/src/components/About.vue
+++ b/front/src/components/About.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="main pusher" v-title="labels.title">
-    <div class="ui vertical center aligned stripe segment">
+  <main class="main pusher" v-title="labels.title">
+    <section class="ui vertical center aligned stripe segment">
       <div class="ui text container">
         <h1 class="ui huge header">
             <translate v-if="instance.name.value" :translate-params="{instance: instance.name.value}">
@@ -10,8 +10,8 @@
         </h1>
         <stats></stats>
       </div>
-    </div>
-    <div class="ui vertical stripe segment">
+    </section>
+    <section class="ui vertical stripe segment">
       <p v-if="!instance.short_description.value && !instance.long_description.value">
         <translate>Unfortunately, owners of this instance did not yet take the time to complete this page.</translate>
       </p>
@@ -31,28 +31,28 @@
         class="ui middle aligned stackable text container"
         v-html="$options.filters.markdown(instance.long_description.value)">
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import {mapState} from 'vuex'
-import Stats from '@/components/instance/Stats'
+import { mapState } from "vuex"
+import Stats from "@/components/instance/Stats"
 
 export default {
   components: {
     Stats
   },
-  created () {
-    this.$store.dispatch('instance/fetchSettings')
+  created() {
+    this.$store.dispatch("instance/fetchSettings")
   },
   computed: {
     ...mapState({
       instance: state => state.instance.settings.instance
     }),
-    labels () {
+    labels() {
       return {
-        title: this.$gettext('About this instance')
+        title: this.$gettext("About this instance")
       }
     }
   }
diff --git a/front/src/components/Footer.vue b/front/src/components/Footer.vue
index 127f8be74a9ef27f361a0d393b0c5049126443ad..3b681d592e7a98d56a74a4e2cd1316286cee7f54 100644
--- a/front/src/components/Footer.vue
+++ b/front/src/components/Footer.vue
@@ -1,8 +1,8 @@
 <template>
-  <footer id="footer" class="ui vertical footer segment">
+  <footer id="footer" role="contentinfo" class="ui vertical footer segment">
     <div class="ui container">
       <div class="ui stackable equal height stackable grid">
-        <div class="four wide column">
+        <section class="four wide column">
           <h4 v-translate class="ui header">
             <translate :translate-params="{instanceName: instanceHostname}" >About %{instanceName}</translate>
           </h4>
@@ -25,24 +25,24 @@
               </select>
             </div>
           </div>
-        </div>
-        <div class="four wide column">
+        </section>
+        <section class="four wide column">
           <h4 v-translate class="ui header">Using Funkwhale</h4>
           <div class="ui link list">
             <a href="https://docs.funkwhale.audio" class="item" target="_blank"><translate>Documentation</translate></a>
             <a href="https://docs.funkwhale.audio/users/apps.html" class="item" target="_blank"><translate>Mobile and desktop apps</translate></a>
             <div role="button" class="item" @click="$emit('show:shortcuts-modal')"><translate>Keyboard shortcuts</translate></div>
           </div>
-        </div>
-        <div class="four wide column">
+        </section>
+        <section class="four wide column">
           <h4 v-translate class="ui header">Getting help</h4>
           <div class="ui link list">
             <a href="https://socialhub.network/c/projects/funkwhale" class="item" target="_blank"><translate>Support forum</translate></a>
             <a href="https://riot.im/app/#/room/#funkwhale-troubleshooting:matrix.org" class="item" target="_blank"><translate>Chat room</translate></a>
             <a href="https://code.eliotberriot.com/funkwhale/funkwhale/issues" class="item" target="_blank"><translate>Issue tracker</translate></a>
           </div>
-        </div>
-        <div class="four wide column">
+        </section>
+        <section class="four wide column">
           <h4 v-translate class="ui header">About Funkwhale</h4>
           <div class="ui link list">
             <a href="https://funkwhale.audio" class="item" target="_blank"><translate>Official website</translate></a>
@@ -53,45 +53,51 @@
           <p>
             <translate>The funkwhale logo was kindly designed and provided by Francis Gading.</translate>
           </p>
-        </div>
+        </section>
       </div>
     </div>
   </footer>
 </template>
 
 <script>
-import {mapState} from 'vuex'
-
+import { mapState } from "vuex"
 
 export default {
-  props: ['version'],
+  props: ["version"],
   methods: {
-    switchInstance () {
-      let confirm = window.confirm(this.$gettext('This will erase your local data and disconnect you, do you want to continue?'))
+    switchInstance() {
+      let confirm = window.confirm(
+        this.$gettext(
+          "This will erase your local data and disconnect you, do you want to continue?"
+        )
+      )
       if (confirm) {
-        this.$store.commit('instance/instanceUrl', null)
+        this.$store.commit("instance/instanceUrl", null)
       }
-    },
+    }
   },
   computed: {
     ...mapState({
       messages: state => state.ui.messages
     }),
-    instanceHostname () {
+    instanceHostname() {
       let url = this.$store.state.instance.instanceUrl
-      let parser = document.createElement('a');
+      let parser = document.createElement("a")
       parser.href = url
       return parser.hostname
     },
-    suggestedInstances () {
-      let instances = [this.$store.getters['instance/defaultUrl'](), 'https://demo.funkwhale.audio']
+    suggestedInstances() {
+      let instances = [
+        this.$store.getters["instance/defaultUrl"](),
+        "https://demo.funkwhale.audio"
+      ]
       return instances
-    },
+    }
   }
 }
 </script>
 <style scoped>
-  footer p {
-    color: grey;
-  }
+footer p {
+  color: grey;
+}
 </style>
diff --git a/front/src/components/Home.vue b/front/src/components/Home.vue
index 3448d10df6d7c8faf97e789f9db18daf2e76b686..800f77b9f82a7f887cc32ee35da87b5f00b1f0af 100644
--- a/front/src/components/Home.vue
+++ b/front/src/components/Home.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="main pusher" v-title="labels.title">
-    <div class="ui vertical center aligned stripe segment">
+  <main class="main pusher" v-title="labels.title">
+    <section class="ui vertical center aligned stripe segment">
       <div class="ui text container">
         <h1 class="ui huge header">
           <translate>Welcome on Funkwhale</translate>
@@ -15,8 +15,8 @@
           <i class="right arrow icon"></i>
         </router-link>
       </div>
-    </div>
-    <div class="ui vertical stripe segment">
+    </section>
+    <section class="ui vertical stripe segment">
       <div class="ui middle aligned stackable text container">
         <div class="ui grid">
           <div class="row">
@@ -136,22 +136,21 @@
           </div>
         </div>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-
 export default {
-  data () {
+  data() {
     return {
-      musicbrainzUrl: 'https://musicbrainz.org/'
+      musicbrainzUrl: "https://musicbrainz.org/"
     }
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        title: this.$gettext('Welcome')
+        title: this.$gettext("Welcome")
       }
     }
   }
diff --git a/front/src/components/PageNotFound.vue b/front/src/components/PageNotFound.vue
index 115bc5d023f1745f1399a868bff86c63c8d12c6a..ef4e66f456cb03e63ad4dfcf441a22bcc1536735 100644
--- a/front/src/components/PageNotFound.vue
+++ b/front/src/components/PageNotFound.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="main pusher" :v-title="labels.title">
-    <div class="ui vertical stripe segment">
+  <main class="main pusher" :v-title="labels.title">
+    <section class="ui vertical stripe segment">
       <div class="ui text container">
         <h1 class="ui huge header">
           <i class="warning icon"></i>
@@ -16,21 +16,21 @@
           <i class="right arrow icon"></i>
         </router-link>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
 export default {
-  data: function () {
+  data: function() {
     return {
       path: window.location.href
     }
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        title: this.$gettext('Page Not Found')
+        title: this.$gettext("Page Not Found")
       }
     }
   }
diff --git a/front/src/components/Pagination.vue b/front/src/components/Pagination.vue
index bdc20b53d9884f4f1a24e3ac1655c4d6f1bd531f..1064a827e8a7c82aaf4145b2db2540d454b96fc5 100644
--- a/front/src/components/Pagination.vue
+++ b/front/src/components/Pagination.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="ui pagination menu">
+  <div class="ui pagination menu" role="navigation" :aria-label="labels.pagination">
     <a href
       :disabled="current - 1 < 1"
       @click.prevent.stop="selectPage(current - 1)"
@@ -24,30 +24,42 @@
 </template>
 
 <script>
-import _ from 'lodash'
+import _ from "lodash"
 
 export default {
   props: {
-    current: {type: Number, default: 1},
-    paginateBy: {type: Number, default: 25},
-    total: {type: Number},
-    compact: {type: Boolean, default: false}
+    current: { type: Number, default: 1 },
+    paginateBy: { type: Number, default: 25 },
+    total: { type: Number },
+    compact: { type: Boolean, default: false }
   },
   computed: {
-    pages: function () {
+    labels() {
+      return {
+        pagination: this.$gettext("Pagination")
+      }
+    },
+    pages: function() {
       let range = 2
       let current = this.current
       let beginning = _.range(1, Math.min(this.maxPage, 1 + range))
-      let middle = _.range(Math.max(1, current - range + 1), Math.min(this.maxPage, current + range))
+      let middle = _.range(
+        Math.max(1, current - range + 1),
+        Math.min(this.maxPage, current + range)
+      )
       let end = _.range(this.maxPage, Math.max(1, this.maxPage - range))
       let allowed = beginning.concat(middle, end)
       allowed = _.uniq(allowed)
-      allowed = _.sortBy(allowed, [(e) => { return e }])
+      allowed = _.sortBy(allowed, [
+        e => {
+          return e
+        }
+      ])
       let final = []
       allowed.forEach(p => {
         let last = final.slice(-1)[0]
         let consecutive = true
-        if (last === 'skip') {
+        if (last === "skip") {
           consecutive = false
         } else {
           if (!last) {
@@ -59,25 +71,25 @@ export default {
         if (consecutive) {
           final.push(p)
         } else {
-          if (p !== 'skip') {
-            final.push('skip')
+          if (p !== "skip") {
+            final.push("skip")
             final.push(p)
           }
         }
       })
       return final
     },
-    maxPage: function () {
+    maxPage: function() {
       return Math.ceil(this.total / this.paginateBy)
     }
   },
   methods: {
-    selectPage: function (page) {
+    selectPage: function(page) {
       if (page > this.maxPage || page < 1) {
         return
       }
       if (this.current !== page) {
-        this.$emit('page-changed', page)
+        this.$emit("page-changed", page)
       }
     }
   }
@@ -87,6 +99,6 @@ export default {
 <!-- Add "scoped" attribute to limit CSS to this component only -->
 <style scoped>
 .ui.pagination.menu .item {
-    cursor: pointer;
+  cursor: pointer;
 }
 </style>
diff --git a/front/src/components/Sidebar.vue b/front/src/components/Sidebar.vue
index 44813c6c9f96ba316430bd80a33e93a4fa39f61a..d8029b1bd662ecd1ae75eb30e8e39530254a41e4 100644
--- a/front/src/components/Sidebar.vue
+++ b/front/src/components/Sidebar.vue
@@ -1,6 +1,6 @@
 <template>
-<div :class="['ui', 'vertical', 'left', 'visible', 'wide', {'collapsed': isCollapsed}, 'sidebar',]">
-  <div class="ui inverted segment header-wrapper">
+<aside :class="['ui', 'vertical', 'left', 'visible', 'wide', {'collapsed': isCollapsed}, 'sidebar',]">
+  <header class="ui inverted segment header-wrapper">
     <search-bar @search="isCollapsed = false">
       <router-link :title="'Funkwhale'" :to="{name: logoUrl}">
         <i class="logo bordered inverted orange big icon">
@@ -12,12 +12,12 @@
         :class="['ui', 'basic', 'big', {'inverted': isCollapsed}, 'orange', 'icon', 'collapse', 'button']">
           <i class="sidebar icon"></i></span>
     </search-bar>
-  </div>
+  </header>
 
   <div class="menu-area">
     <div class="ui compact fluid two item inverted menu">
-      <a class="active item" href @click.prevent.stop="selectedTab = 'library'" data-tab="library"><translate>Browse</translate></a>
-      <a class="item" href @click.prevent.stop="selectedTab = 'queue'" data-tab="queue">
+      <a class="active item" role="button" @click.prevent.stop="selectedTab = 'library'" data-tab="library"><translate>Browse</translate></a>
+      <a class="item" role="button" @click.prevent.stop="selectedTab = 'queue'" data-tab="queue">
         <translate>Queue</translate>&nbsp;
          <template v-if="queue.tracks.length === 0">
            <translate>(empty)</translate>
@@ -29,10 +29,10 @@
     </div>
   </div>
   <div class="tabs">
-    <div class="ui bottom attached active tab" data-tab="library">
-      <div class="ui inverted vertical large fluid menu">
+    <section class="ui bottom attached active tab" data-tab="library" :aria-label="labels.mainMenu">
+      <nav class="ui inverted vertical large fluid menu" role="navigation" :aria-label="labels.mainMenu">
         <div class="item">
-          <div class="header"><translate>My account</translate></div>
+          <header class="header"><translate>My account</translate></header>
           <div class="menu">
             <router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'profile', params: {username: $store.state.auth.username}}">
               <i class="user icon"></i>
@@ -61,7 +61,7 @@
           </div>
         </div>
         <div class="item">
-          <div class="header"><translate>Music</translate></div>
+          <header class="header"><translate>Music</translate></header>
           <div class="menu">
             <router-link class="item" :to="{path: '/library'}"><i class="sound icon"></i><translate>Browse library</translate></router-link>
             <router-link class="item" v-if="$store.state.auth.authenticated" :to="{path: '/favorites'}"><i class="heart icon"></i><translate>Favorites</translate></router-link>
@@ -77,7 +77,7 @@
           </div>
         </div>
         <div class="item" v-if="$store.state.auth.availablePermissions['settings']">
-          <div class="header"><translate>Administration</translate></div>
+          <header class="header"><translate>Administration</translate></header>
           <div class="menu">
             <router-link
               class="item"
@@ -91,8 +91,8 @@
             </router-link>
           </div>
         </div>
-      </div>
-    </div>
+      </nav>
+    </section>
     <div v-if="queue.previousQueue " class="ui black icon message">
       <i class="history icon"></i>
       <div class="content">
@@ -113,17 +113,21 @@
         </div>
       </div>
     </div>
-    <div class="ui bottom attached tab" data-tab="queue">
+    <section class="ui bottom attached tab" data-tab="queue">
       <table class="ui compact inverted very basic fixed single line unstackable table">
         <draggable v-model="tracks" element="tbody" @update="reorder">
-          <tr @click="$store.dispatch('queue/currentIndex', index)" v-for="(track, index) in tracks" :key="index" :class="[{'active': index === queue.currentIndex}]">
+          <tr
+              @click="$store.dispatch('queue/currentIndex', index)"
+              v-for="(track, index) in tracks"
+              :key="index"
+              :class="[{'active': index === queue.currentIndex}]">
               <td class="right aligned">{{ index + 1}}</td>
               <td class="center aligned">
                   <img class="ui mini image" v-if="track.album.cover && track.album.cover.original" :src="$store.getters['instance/absoluteUrl'](track.album.cover.small_square_crop)">
                   <img class="ui mini image" v-else src="../assets/audio/default-cover.png">
               </td>
               <td colspan="4">
-                  <button class="title reset ellipsis">
+                  <button class="title reset ellipsis" :aria-label="labels.selectTrack">
                     <strong>{{ track.title }}</strong><br />
                     {{ track.artist.name }}
                   </button>
@@ -134,7 +138,7 @@
                 </template>
               </td>
               <td>
-                  <button @click.stop="cleanTrack(index)" :class="['ui', {'inverted': index != queue.currentIndex}, 'really', 'tiny', 'basic', 'circular', 'icon', 'button']">
+                  <button :title="labels.removeFromQueue" @click.stop="cleanTrack(index)" :class="['ui', {'inverted': index != queue.currentIndex}, 'really', 'tiny', 'basic', 'circular', 'icon', 'button']">
                     <i class="trash icon"></i>
                   </button>
               </td>
@@ -150,44 +154,46 @@
           <div @click="$store.dispatch('radios/stop')" class="ui basic inverted red button"><translate>Stop radio</translate></div>
         </div>
       </div>
-    </div>
+    </section>
   </div>
   <player @next="scrollToCurrent" @previous="scrollToCurrent"></player>
-</div>
+</aside>
 </template>
 
 <script>
-import {mapState, mapActions} from 'vuex'
+import { mapState, mapActions } from "vuex"
 
-import Player from '@/components/audio/Player'
-import Logo from '@/components/Logo'
-import SearchBar from '@/components/audio/SearchBar'
-import backend from '@/audio/backend'
-import draggable from 'vuedraggable'
+import Player from "@/components/audio/Player"
+import Logo from "@/components/Logo"
+import SearchBar from "@/components/audio/SearchBar"
+import backend from "@/audio/backend"
+import draggable from "vuedraggable"
 
-import $ from 'jquery'
+import $ from "jquery"
 
 export default {
-  name: 'sidebar',
+  name: "sidebar",
   components: {
     Player,
     SearchBar,
     Logo,
     draggable
   },
-  data () {
+  data() {
     return {
-      selectedTab: 'library',
+      selectedTab: "library",
       backend: backend,
       tracksChangeBuffer: null,
       isCollapsed: true,
-      fetchInterval: null,
+      fetchInterval: null
     }
   },
-  mounted () {
-    $(this.$el).find('.menu .item').tab()
+  mounted() {
+    $(this.$el)
+      .find(".menu .item")
+      .tab()
   },
-  destroy () {
+  destroy() {
     if (this.fetchInterval) {
       clearInterval(this.fetchInterval)
     }
@@ -197,82 +203,92 @@ export default {
       queue: state => state.queue,
       url: state => state.route.path
     }),
-    labels () {
-      let pendingRequests = this.$gettext('Pending import requests')
-      let pendingFollows = this.$gettext('Pending follow requests')
+    labels() {
+      let mainMenu = this.$gettext("Main menu")
+      let selectTrack = this.$gettext("Play this track")
+      let pendingRequests = this.$gettext("Pending import requests")
+      let pendingFollows = this.$gettext("Pending follow requests")
       return {
         pendingRequests,
-        pendingFollows
+        pendingFollows,
+        mainMenu,
+        selectTrack
       }
     },
     tracks: {
-      get () {
+      get() {
         return this.$store.state.queue.tracks
       },
-      set (value) {
+      set(value) {
         this.tracksChangeBuffer = value
       }
     },
-    logoUrl () {
+    logoUrl() {
       if (this.$store.state.auth.authenticated) {
-        return 'library.index'
+        return "library.index"
       } else {
-        return 'index'
+        return "index"
       }
     }
   },
   methods: {
     ...mapActions({
-      cleanTrack: 'queue/cleanTrack'
+      cleanTrack: "queue/cleanTrack"
     }),
-    reorder: function (event) {
-      this.$store.commit('queue/reorder', {
-        tracks: this.tracksChangeBuffer, oldIndex: event.oldIndex, newIndex: event.newIndex})
+    reorder: function(event) {
+      this.$store.commit("queue/reorder", {
+        tracks: this.tracksChangeBuffer,
+        oldIndex: event.oldIndex,
+        newIndex: event.newIndex
+      })
     },
-    scrollToCurrent () {
+    scrollToCurrent() {
       let current = $(this.$el).find('[data-tab="queue"] .active')[0]
       if (!current) {
         return
       }
-      let container = $(this.$el).find('.tabs')[0]
+      let container = $(this.$el).find(".tabs")[0]
       // Position container at the top line then scroll current into view
       container.scrollTop = 0
       current.scrollIntoView(true)
       // Scroll back nothing if element is at bottom of container else do it
       // for half the height of the containers display area
-      var scrollBack = (container.scrollHeight - container.scrollTop <= container.clientHeight) ? 0 : container.clientHeight / 2
+      var scrollBack =
+        container.scrollHeight - container.scrollTop <= container.clientHeight
+          ? 0
+          : container.clientHeight / 2
       container.scrollTop = container.scrollTop - scrollBack
     }
   },
   watch: {
-    url: function () {
+    url: function() {
       this.isCollapsed = true
     },
-    selectedTab: function (newValue) {
-      if (newValue === 'queue') {
+    selectedTab: function(newValue) {
+      if (newValue === "queue") {
         this.scrollToCurrent()
       }
     },
-    '$store.state.queue.currentIndex': function () {
-      if (this.selectedTab !== 'queue') {
+    "$store.state.queue.currentIndex": function() {
+      if (this.selectedTab !== "queue") {
         this.scrollToCurrent()
       }
-    },
+    }
   }
 }
 </script>
 
 <!-- Add "scoped" attribute to limit CSS to this component only -->
 <style scoped lang="scss">
-@import '../style/vendor/media';
+@import "../style/vendor/media";
 
 $sidebar-color: #3d3e3f;
 
 .sidebar {
-	background: $sidebar-color;
+  background: $sidebar-color;
   @include media(">tablet") {
-    display:flex;
-    flex-direction:column;
+    display: flex;
+    flex-direction: column;
     justify-content: space-between;
   }
   @include media(">desktop") {
@@ -284,7 +300,9 @@ $sidebar-color: #3d3e3f;
     position: static !important;
     width: 100% !important;
     &.collapsed {
-      .menu-area, .player-wrapper, .tabs {
+      .menu-area,
+      .player-wrapper,
+      .tabs {
         display: none;
       }
     }
@@ -378,7 +396,9 @@ $sidebar-color: #3d3e3f;
 .ui.search {
   display: flex;
 
-  .collapse.button, .collapse.button:hover, .collapse.button:active {
+  .collapse.button,
+  .collapse.button:hover,
+  .collapse.button:active {
     box-shadow: none !important;
     margin: 0px;
     display: flex;
diff --git a/front/src/components/audio/Player.vue b/front/src/components/audio/Player.vue
index 618fcd7164b9c0e7348c388f366254eaa9aa6b47..83751d8d44d7d8578dde4352b22341d1d140c4ac 100644
--- a/front/src/components/audio/Player.vue
+++ b/front/src/components/audio/Player.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="ui inverted segment player-wrapper" :style="style">
+  <section class="ui inverted segment player-wrapper" :aria-label="labels.audioPlayer" :style="style">
     <div class="player">
       <audio-track
         ref="currentAudio"
@@ -213,18 +213,18 @@
         @keydown.s.prevent.exact="shuffle"
         />
     </div>
-  </div>
+  </section>
 </template>
 
 <script>
-import {mapState, mapGetters, mapActions} from 'vuex'
-import GlobalEvents from '@/components/utils/global-events'
-import ColorThief from '@/vendor/color-thief'
-import {Howl} from 'howler'
+import { mapState, mapGetters, mapActions } from "vuex"
+import GlobalEvents from "@/components/utils/global-events"
+import ColorThief from "@/vendor/color-thief"
+import { Howl } from "howler"
 
-import AudioTrack from '@/components/audio/Track'
-import TrackFavoriteIcon from '@/components/favorites/TrackFavoriteIcon'
-import TrackPlaylistIcon from '@/components/playlists/TrackPlaylistIcon'
+import AudioTrack from "@/components/audio/Track"
+import TrackFavoriteIcon from "@/components/favorites/TrackFavoriteIcon"
+import TrackPlaylistIcon from "@/components/playlists/TrackPlaylistIcon"
 
 export default {
   components: {
@@ -233,8 +233,13 @@ export default {
     GlobalEvents,
     AudioTrack
   },
-  data () {
-    let defaultAmbiantColors = [[46, 46, 46], [46, 46, 46], [46, 46, 46], [46, 46, 46]]
+  data() {
+    let defaultAmbiantColors = [
+      [46, 46, 46],
+      [46, 46, 46],
+      [46, 46, 46],
+      [46, 46, 46]
+    ]
     return {
       isShuffling: false,
       sliderVolume: this.volume,
@@ -245,7 +250,7 @@ export default {
       dummyAudio: null
     }
   },
-  mounted () {
+  mounted() {
     // we trigger the watcher explicitely it does not work otherwise
     this.sliderVolume = this.volume
     // this is needed to unlock audio playing under some browsers,
@@ -254,57 +259,57 @@ export default {
     this.dummyAudio = new Howl({
       preload: false,
       autoplay: false,
-      src: ['noop.webm', 'noop.mp3']
+      src: ["noop.webm", "noop.mp3"]
     })
   },
-  destroyed () {
+  destroyed() {
     this.dummyAudio.unload()
   },
   methods: {
     ...mapActions({
-      togglePlay: 'player/togglePlay',
-      mute: 'player/mute',
-      unmute: 'player/unmute',
-      clean: 'queue/clean',
-      updateProgress: 'player/updateProgress'
+      togglePlay: "player/togglePlay",
+      mute: "player/mute",
+      unmute: "player/unmute",
+      clean: "queue/clean",
+      updateProgress: "player/updateProgress"
     }),
-    shuffle () {
+    shuffle() {
       let disabled = this.queue.tracks.length === 0
       if (this.isShuffling || disabled) {
         return
       }
       let self = this
-      let msg = this.$gettext('Queue shuffled!')
+      let msg = this.$gettext("Queue shuffled!")
       this.isShuffling = true
       setTimeout(() => {
-        self.$store.dispatch('queue/shuffle', () => {
+        self.$store.dispatch("queue/shuffle", () => {
           self.isShuffling = false
-          self.$store.commit('ui/addMessage', {
+          self.$store.commit("ui/addMessage", {
             content: msg,
             date: new Date()
           })
         })
       }, 100)
     },
-    next () {
+    next() {
       let self = this
-      this.$store.dispatch('queue/next').then(() => {
-        self.$emit('next')
+      this.$store.dispatch("queue/next").then(() => {
+        self.$emit("next")
       })
     },
-    previous () {
+    previous() {
       let self = this
-      this.$store.dispatch('queue/previous').then(() => {
-        self.$emit('previous')
+      this.$store.dispatch("queue/previous").then(() => {
+        self.$emit("previous")
       })
     },
-    touchProgress (e) {
+    touchProgress(e) {
       let time
       let target = this.$refs.progress
-      time = e.layerX / target.offsetWidth * this.duration
+      time = (e.layerX / target.offsetWidth) * this.duration
       this.$refs.currentAudio.setCurrentTime(time)
     },
-    updateBackground () {
+    updateBackground() {
       if (!this.currentTrack.album.cover) {
         this.ambiantColors = this.defaultAmbiantColors
         return
@@ -312,9 +317,9 @@ export default {
       let image = this.$refs.cover
       this.ambiantColors = ColorThief.prototype.getPalette(image, 4).slice(0, 4)
     },
-    handleError ({sound, error}) {
-      this.$store.commit('player/isLoadingAudio', false)
-      this.$store.dispatch('player/trackErrored')
+    handleError({ sound, error }) {
+      this.$store.commit("player/isLoadingAudio", false)
+      this.$store.dispatch("player/trackErrored")
     }
   },
   computed: {
@@ -330,26 +335,34 @@ export default {
       queue: state => state.queue
     }),
     ...mapGetters({
-      currentTrack: 'queue/currentTrack',
-      hasNext: 'queue/hasNext',
-      emptyQueue: 'queue/isEmpty',
-      durationFormatted: 'player/durationFormatted',
-      currentTimeFormatted: 'player/currentTimeFormatted',
-      progress: 'player/progress'
+      currentTrack: "queue/currentTrack",
+      hasNext: "queue/hasNext",
+      emptyQueue: "queue/isEmpty",
+      durationFormatted: "player/durationFormatted",
+      currentTimeFormatted: "player/currentTimeFormatted",
+      progress: "player/progress"
     }),
-    labels () {
-      let previousTrack = this.$gettext('Previous track')
-      let play = this.$gettext('Play track')
-      let pause = this.$gettext('Pause track')
-      let next = this.$gettext('Next track')
-      let unmute = this.$gettext('Unmute')
-      let mute = this.$gettext('Mute')
-      let loopingDisabled = this.$gettext('Looping disabled. Click to switch to single-track looping.')
-      let loopingSingle = this.$gettext('Looping on a single track. Click to switch to whole queue looping.')
-      let loopingWhole = this.$gettext('Looping on whole queue. Click to disable looping.')
-      let shuffle = this.$gettext('Shuffle your queue')
-      let clear = this.$gettext('Clear your queue')
+    labels() {
+      let audioPlayer = this.$gettext("Media player")
+      let previousTrack = this.$gettext("Previous track")
+      let play = this.$gettext("Play track")
+      let pause = this.$gettext("Pause track")
+      let next = this.$gettext("Next track")
+      let unmute = this.$gettext("Unmute")
+      let mute = this.$gettext("Mute")
+      let loopingDisabled = this.$gettext(
+        "Looping disabled. Click to switch to single-track looping."
+      )
+      let loopingSingle = this.$gettext(
+        "Looping on a single track. Click to switch to whole queue looping."
+      )
+      let loopingWhole = this.$gettext(
+        "Looping on whole queue. Click to disable looping."
+      )
+      let shuffle = this.$gettext("Shuffle your queue")
+      let clear = this.$gettext("Clear your queue")
       return {
+        audioPlayer,
         previousTrack,
         play,
         pause,
@@ -363,29 +376,35 @@ export default {
         clear
       }
     },
-    style: function () {
+    style: function() {
       let style = {
-        'background': this.ambiantGradiant
+        background: this.ambiantGradiant
       }
       return style
     },
-    ambiantGradiant: function () {
+    ambiantGradiant: function() {
       let indexConf = [
-        {orientation: 330, percent: 100, opacity: 0.7},
-        {orientation: 240, percent: 90, opacity: 0.7},
-        {orientation: 150, percent: 80, opacity: 0.7},
-        {orientation: 60, percent: 70, opacity: 0.7}
+        { orientation: 330, percent: 100, opacity: 0.7 },
+        { orientation: 240, percent: 90, opacity: 0.7 },
+        { orientation: 150, percent: 80, opacity: 0.7 },
+        { orientation: 60, percent: 70, opacity: 0.7 }
       ]
-      let gradients = this.ambiantColors.map((e, i) => {
-        let [r, g, b] = e
-        let conf = indexConf[i]
-        return `linear-gradient(${conf.orientation}deg, rgba(${r}, ${g}, ${b}, ${conf.opacity}) 10%, rgba(255, 255, 255, 0) ${conf.percent}%)`
-      }).join(', ')
+      let gradients = this.ambiantColors
+        .map((e, i) => {
+          let [r, g, b] = e
+          let conf = indexConf[i]
+          return `linear-gradient(${
+            conf.orientation
+          }deg, rgba(${r}, ${g}, ${b}, ${
+            conf.opacity
+          }) 10%, rgba(255, 255, 255, 0) ${conf.percent}%)`
+        })
+        .join(", ")
       return gradients
     }
   },
   watch: {
-    currentTrack (newValue, oldValue) {
+    currentTrack(newValue, oldValue) {
       if (!this.isShuffling && newValue != oldValue) {
         this.audioKey = String(new Date())
       }
@@ -393,11 +412,11 @@ export default {
         this.ambiantColors = this.defaultAmbiantColors
       }
     },
-    volume (newValue) {
+    volume(newValue) {
       this.sliderVolume = newValue
     },
-    sliderVolume (newValue) {
-      this.$store.commit('player/volume', newValue)
+    sliderVolume(newValue) {
+      this.$store.commit("player/volume", newValue)
     }
   }
 }
@@ -405,7 +424,6 @@ export default {
 
 <!-- Add "scoped" attribute to limit CSS to this component only -->
 <style scoped lang="scss">
-
 .ui.progress {
   margin: 0.5rem 0 1rem;
 }
@@ -423,18 +441,21 @@ export default {
 .ui.item {
   .meta {
     font-size: 90%;
-    line-height: 1.2
+    line-height: 1.2;
   }
 }
 .timer.total {
-    text-align: right;
+  text-align: right;
 }
 .timer.start {
-    cursor: pointer
+  cursor: pointer;
 }
 .track-area {
   margin-top: 0;
-  .header, .meta, .artist, .album {
+  .header,
+  .meta,
+  .artist,
+  .album {
     color: white !important;
   }
 }
@@ -468,57 +489,57 @@ export default {
     left: 25%;
     cursor: pointer;
   }
-  input[type=range]:focus {
+  input[type="range"]:focus {
     outline: none;
   }
-  input[type=range]::-webkit-slider-runnable-track {
+  input[type="range"]::-webkit-slider-runnable-track {
     cursor: pointer;
   }
-  input[type=range]::-webkit-slider-thumb {
+  input[type="range"]::-webkit-slider-thumb {
     background: white;
     cursor: pointer;
     -webkit-appearance: none;
     border-radius: 3px;
     width: 10px;
   }
-  input[type=range]::-moz-range-track {
+  input[type="range"]::-moz-range-track {
     cursor: pointer;
     background: white;
     opacity: 0.3;
   }
-  input[type=range]::-moz-focus-outer {
+  input[type="range"]::-moz-focus-outer {
     border: 0;
   }
-  input[type=range]::-moz-range-thumb {
+  input[type="range"]::-moz-range-thumb {
     background: white;
     cursor: pointer;
     border-radius: 3px;
     width: 10px;
   }
-  input[type=range]::-ms-track {
+  input[type="range"]::-ms-track {
     cursor: pointer;
     background: transparent;
     border-color: transparent;
     color: transparent;
   }
-  input[type=range]::-ms-fill-lower {
+  input[type="range"]::-ms-fill-lower {
     background: white;
     opacity: 0.3;
   }
-  input[type=range]::-ms-fill-upper {
+  input[type="range"]::-ms-fill-upper {
     background: white;
     opacity: 0.3;
   }
-  input[type=range]::-ms-thumb {
+  input[type="range"]::-ms-thumb {
     background: white;
     cursor: pointer;
     border-radius: 3px;
     width: 10px;
   }
-  input[type=range]:focus::-ms-fill-lower {
+  input[type="range"]:focus::-ms-fill-lower {
     background: white;
   }
-  input[type=range]:focus::-ms-fill-upper {
+  input[type="range"]:focus::-ms-fill-upper {
     background: white;
   }
 }
@@ -545,14 +566,13 @@ export default {
   margin: 0;
 }
 
-
 @keyframes MOVE-BG {
-	from {
-		transform: translateX(0px);
-	}
-	to {
-		transform: translateX(46px);
-	}
+  from {
+    transform: translateX(0px);
+  }
+  to {
+    transform: translateX(46px);
+  }
 }
 
 .indicating.progress {
@@ -565,7 +585,7 @@ export default {
 
 .ui.inverted.progress .buffer.bar {
   position: absolute;
-  background-color:rgba(255, 255, 255, 0.15);
+  background-color: rgba(255, 255, 255, 0.15);
 }
 .indicating.progress .bar {
   left: -46px;
@@ -576,12 +596,12 @@ export default {
     grey 1px,
     grey 10px,
     transparent 10px,
-    transparent 20px,
-	) !important;
+    transparent 20px
+  ) !important;
 
   animation-name: MOVE-BG;
-	animation-duration: 2s;
-	animation-timing-function: linear;
-	animation-iteration-count: infinite;
+  animation-duration: 2s;
+  animation-timing-function: linear;
+  animation-iteration-count: infinite;
 }
 </style>
diff --git a/front/src/components/auth/Login.vue b/front/src/components/auth/Login.vue
index 88caa5afefccf12b0ac115613f45f94dec9046af..30cb15a3669f8ea0baef855cb9ea5a3f89a56354 100644
--- a/front/src/components/auth/Login.vue
+++ b/front/src/components/auth/Login.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="main pusher" v-title="labels.title">
-    <div class="ui vertical stripe segment">
+  <main class="main pusher" v-title="labels.title">
+    <section class="ui vertical stripe segment">
       <div class="ui small text container">
         <h2><translate>Log in to your Funkwhale account</translate></h2>
         <form class="ui form" @submit.prevent="submit()">
@@ -43,39 +43,39 @@
           </button>
         </form>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import PasswordInput from '@/components/forms/PasswordInput'
+import PasswordInput from "@/components/forms/PasswordInput"
 
 export default {
   props: {
-    next: {type: String, default: '/'}
+    next: { type: String, default: "/" }
   },
   components: {
     PasswordInput
   },
-  data () {
+  data() {
     return {
       // We need to initialize the component with any
       // properties that will be used in it
       credentials: {
-        username: '',
-        password: ''
+        username: "",
+        password: ""
       },
-      error: '',
+      error: "",
       isLoading: false
     }
   },
-  mounted () {
+  mounted() {
     this.$refs.username.focus()
   },
   computed: {
-    labels () {
-      let usernamePlaceholder = this.$gettext('Enter your username or email')
-      let title = this.$gettext('Log In')
+    labels() {
+      let usernamePlaceholder = this.$gettext("Enter your username or email")
+      let title = this.$gettext("Log In")
       return {
         usernamePlaceholder,
         title
@@ -83,30 +83,31 @@ export default {
     }
   },
   methods: {
-    submit () {
+    submit() {
       var self = this
       self.isLoading = true
-      this.error = ''
+      this.error = ""
       var credentials = {
         username: this.credentials.username,
         password: this.credentials.password
       }
-      this.$store.dispatch('auth/login', {
-        credentials,
-        next: '/library',
-        onError: error => {
-          if (error.response.status === 400) {
-            self.error = 'invalid_credentials'
-          } else {
-            self.error = 'unknown_error'
+      this.$store
+        .dispatch("auth/login", {
+          credentials,
+          next: "/library",
+          onError: error => {
+            if (error.response.status === 400) {
+              self.error = "invalid_credentials"
+            } else {
+              self.error = "unknown_error"
+            }
           }
-        }
-      }).then(e => {
-        self.isLoading = false
-      })
+        })
+        .then(e => {
+          self.isLoading = false
+        })
     }
   }
-
 }
 </script>
 
diff --git a/front/src/components/auth/Logout.vue b/front/src/components/auth/Logout.vue
index 5f60a9dbeb8aaee8554dbb5f9d08402a4ba322da..90495f5e0ed17fbb2485da7003a93d97a7bbf5a9 100644
--- a/front/src/components/auth/Logout.vue
+++ b/front/src/components/auth/Logout.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="main pusher" v-title="labels.title">
-    <div class="ui vertical stripe segment">
+  <main class="main pusher" v-title="labels.title">
+    <section class="ui vertical stripe segment">
       <div class="ui small text container">
         <h2>
           <translate>Are you sure you want to log out?</translate>
@@ -8,16 +8,16 @@
         <p v-translate="{username: $store.state.auth.username}">You are currently logged in as %{ username }</p>
         <button class="ui button" @click="$store.dispatch('auth/logout')"><translate>Yes, log me out!</translate></button>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
 export default {
   computed: {
-    labels () {
+    labels() {
       return {
-        title: this.$gettext('Log Out')
+        title: this.$gettext("Log Out")
       }
     }
   }
diff --git a/front/src/components/auth/Profile.vue b/front/src/components/auth/Profile.vue
index b480f7495d72d022f0f1ad82d6fcd226802d89e1..e1bdd416c6f2519361c0643f65010ee375aa25eb 100644
--- a/front/src/components/auth/Profile.vue
+++ b/front/src/components/auth/Profile.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="main pusher" v-title="labels.usernameProfile">
+  <main class="main pusher" v-title="labels.usernameProfile">
     <div v-if="isLoading" class="ui vertical segment">
       <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
     </div>
@@ -25,36 +25,37 @@
         </a>
       </div>
     </template>
-  </div>
+  </main>
 </template>
 
 <script>
-import {mapState} from 'vuex'
+import { mapState } from "vuex"
 
-const dateFormat = require('dateformat')
+const dateFormat = require("dateformat")
 
 export default {
-  props: ['username'],
-  created () {
-    this.$store.dispatch('auth/fetchProfile')
+  props: ["username"],
+  created() {
+    this.$store.dispatch("auth/fetchProfile")
   },
   computed: {
-
     ...mapState({
       profile: state => state.auth.profile
     }),
-    labels () {
-      let msg = this.$gettext('%{ username }\'s profile')
-      let usernameProfile = this.$gettextInterpolate(msg, {username: this.username})
+    labels() {
+      let msg = this.$gettext("%{ username }'s profile")
+      let usernameProfile = this.$gettextInterpolate(msg, {
+        username: this.username
+      })
       return {
         usernameProfile
       }
     },
-    signupDate () {
+    signupDate() {
       let d = new Date(this.profile.date_joined)
-      return dateFormat(d, 'longDate')
+      return dateFormat(d, "longDate")
     },
-    isLoading () {
+    isLoading() {
       return !this.profile
     }
   }
diff --git a/front/src/components/auth/Settings.vue b/front/src/components/auth/Settings.vue
index 0c27d9a9aecabdf9d20cc59db473e11096f5a58b..f4e357f28070e21e1e50f48f43c22de1769506d9 100644
--- a/front/src/components/auth/Settings.vue
+++ b/front/src/components/auth/Settings.vue
@@ -1,7 +1,7 @@
 <template>
-  <div class="main pusher" v-title="labels.title">
+  <main class="main pusher" v-title="labels.title">
     <div class="ui vertical stripe segment">
-      <div class="ui small text container">
+      <section class="ui small text container">
         <h2 class="ui header">
           <translate>Account settings</translate>
         </h2>
@@ -28,9 +28,9 @@
             <translate>Update settings</translate>
           </button>
         </form>
-      </div>
+      </section>
       <div class="ui hidden divider"></div>
-      <div class="ui small text container">
+      <section class="ui small text container">
         <h2 class="ui header">
           <translate>Avatar</translate>
         </h2>
@@ -61,9 +61,9 @@
             </div>
           </div>
         </div>
-      </div>
+      </section>
       <div class="ui hidden divider"></div>
-      <div class="ui small text container">
+      <section class="ui small text container">
         <h2 class="ui header">
           <translate>Change my password</translate>
         </h2>
@@ -107,18 +107,18 @@
         </form>
         <div class="ui hidden divider" />
         <subsonic-token-form />
-      </div>
+      </section>
     </div>
-  </div>
+  </main>
 </template>
 
 <script>
-import $ from 'jquery'
-import axios from 'axios'
-import logger from '@/logging'
-import PasswordInput from '@/components/forms/PasswordInput'
-import SubsonicTokenForm from '@/components/auth/SubsonicTokenForm'
-import TranslationsMixin from '@/components/mixins/Translations'
+import $ from "jquery"
+import axios from "axios"
+import logger from "@/logging"
+import PasswordInput from "@/components/forms/PasswordInput"
+import SubsonicTokenForm from "@/components/auth/SubsonicTokenForm"
+import TranslationsMixin from "@/components/mixins/Translations"
 
 export default {
   mixins: [TranslationsMixin],
@@ -126,14 +126,14 @@ export default {
     PasswordInput,
     SubsonicTokenForm
   },
-  data () {
+  data() {
     let d = {
       // We need to initialize the component with any
       // properties that will be used in it
-      old_password: '',
-      new_password: '',
+      old_password: "",
+      new_password: "",
       currentAvatar: this.$store.state.auth.profile.avatar,
-      passwordError: '',
+      passwordError: "",
       isLoading: false,
       isLoadingAvatar: false,
       avatarErrors: [],
@@ -141,12 +141,12 @@ export default {
       settings: {
         success: false,
         errors: [],
-        order: ['privacy_level'],
+        order: ["privacy_level"],
         fields: {
-          'privacy_level': {
-            type: 'dropdown',
+          privacy_level: {
+            type: "dropdown",
             initial: this.$store.state.auth.profile.privacy_level,
-            choices: ['me', 'instance']
+            choices: ["me", "instance"]
           }
         }
       }
@@ -157,108 +157,120 @@ export default {
     })
     return d
   },
-  mounted () {
-    $('select.dropdown').dropdown()
+  mounted() {
+    $("select.dropdown").dropdown()
   },
   methods: {
-    submitSettings () {
+    submitSettings() {
       this.settings.success = false
       this.settings.errors = []
       let self = this
       let payload = this.settingsValues
       let url = `users/users/${this.$store.state.auth.username}/`
-      return axios.patch(url, payload).then(response => {
-        logger.default.info('Updated settings successfully')
-        self.settings.success = true
-        return axios.get('users/users/me/').then((response) => {
-          self.$store.dispatch('auth/updateProfile', response.data)
-        })
-      }, error => {
-        logger.default.error('Error while updating settings')
-        self.isLoading = false
-        self.settings.errors = error.backendErrors
-      })
+      return axios.patch(url, payload).then(
+        response => {
+          logger.default.info("Updated settings successfully")
+          self.settings.success = true
+          return axios.get("users/users/me/").then(response => {
+            self.$store.dispatch("auth/updateProfile", response.data)
+          })
+        },
+        error => {
+          logger.default.error("Error while updating settings")
+          self.isLoading = false
+          self.settings.errors = error.backendErrors
+        }
+      )
     },
-    submitAvatar () {
+    submitAvatar() {
       this.isLoadingAvatar = true
       this.avatarErrors = []
       let self = this
       this.avatar = this.$refs.avatar.files[0]
       let formData = new FormData()
-      formData.append('avatar', this.avatar)
-      axios.patch(
-        `users/users/${this.$store.state.auth.username}/`,
-        formData,
-        {
+      formData.append("avatar", this.avatar)
+      axios
+        .patch(`users/users/${this.$store.state.auth.username}/`, formData, {
           headers: {
-            'Content-Type': 'multipart/form-data'
+            "Content-Type": "multipart/form-data"
           }
-        }
-      ).then(response => {
-        this.isLoadingAvatar = false
-        self.currentAvatar = response.data.avatar
-        self.$store.commit('auth/avatar', self.currentAvatar)
-      }, error => {
-        self.isLoadingAvatar = false
-        self.avatarErrors = error.backendErrors
-      })
+        })
+        .then(
+          response => {
+            this.isLoadingAvatar = false
+            self.currentAvatar = response.data.avatar
+            self.$store.commit("auth/avatar", self.currentAvatar)
+          },
+          error => {
+            self.isLoadingAvatar = false
+            self.avatarErrors = error.backendErrors
+          }
+        )
     },
-    removeAvatar () {
+    removeAvatar() {
       this.isLoadingAvatar = true
       let self = this
       this.avatar = null
-      axios.patch(
-        `users/users/${this.$store.state.auth.username}/`,
-        {avatar: null}
-      ).then(response => {
-        this.isLoadingAvatar = false
-        self.currentAvatar = {}
-        self.$store.commit('auth/avatar', self.currentAvatar)
-      }, error => {
-        self.isLoadingAvatar = false
-        self.avatarErrors = error.backendErrors
-      })
+      axios
+        .patch(`users/users/${this.$store.state.auth.username}/`, {
+          avatar: null
+        })
+        .then(
+          response => {
+            this.isLoadingAvatar = false
+            self.currentAvatar = {}
+            self.$store.commit("auth/avatar", self.currentAvatar)
+          },
+          error => {
+            self.isLoadingAvatar = false
+            self.avatarErrors = error.backendErrors
+          }
+        )
     },
-    submitPassword () {
+    submitPassword() {
       var self = this
       self.isLoading = true
-      this.error = ''
+      this.error = ""
       var credentials = {
         old_password: this.old_password,
         new_password1: this.new_password,
         new_password2: this.new_password
       }
-      let url = 'auth/registration/change-password/'
-      return axios.post(url, credentials).then(response => {
-        logger.default.info('Password successfully changed')
-        self.$router.push({
-          name: 'profile',
-          params: {
-            username: self.$store.state.auth.username
-          }})
-      }, error => {
-        if (error.response.status === 400) {
-          self.passwordError = 'invalid_credentials'
-        } else {
-          self.passwordError = 'unknown_error'
+      let url = "auth/registration/change-password/"
+      return axios.post(url, credentials).then(
+        response => {
+          logger.default.info("Password successfully changed")
+          self.$router.push({
+            name: "profile",
+            params: {
+              username: self.$store.state.auth.username
+            }
+          })
+        },
+        error => {
+          if (error.response.status === 400) {
+            self.passwordError = "invalid_credentials"
+          } else {
+            self.passwordError = "unknown_error"
+          }
+          self.isLoading = false
         }
-        self.isLoading = false
-      })
+      )
     }
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        title: this.$gettext('Account Settings')
+        title: this.$gettext("Account Settings")
       }
     },
-    orderedSettingsFields () {
+    orderedSettingsFields() {
       let self = this
       return this.settings.order.map(id => {
         return self.settings.fields[id]
       })
     },
-    settingsValues () {
+    settingsValues() {
       let self = this
       let s = {}
       this.settings.order.forEach(setting => {
@@ -268,7 +280,6 @@ export default {
       return s
     }
   }
-
 }
 </script>
 
diff --git a/front/src/components/auth/Signup.vue b/front/src/components/auth/Signup.vue
index 8d2e80470b63d1a651e383bcf546825a1f01cacb..01230a117784dda665edfaa6face83a9aac4c555 100644
--- a/front/src/components/auth/Signup.vue
+++ b/front/src/components/auth/Signup.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="main pusher" v-title="labels.title">
-    <div class="ui vertical stripe segment">
+  <main class="main pusher" v-title="labels.title">
+    <section class="ui vertical stripe segment">
       <div class="ui small text container">
         <h2><translate>Create a funkwhale account</translate></h2>
         <form
@@ -53,49 +53,51 @@
           </button>
         </form>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import axios from 'axios'
-import logger from '@/logging'
+import axios from "axios"
+import logger from "@/logging"
 
-import PasswordInput from '@/components/forms/PasswordInput'
+import PasswordInput from "@/components/forms/PasswordInput"
 
 export default {
   props: {
-    defaultInvitation: {type: String, required: false, default: null},
-    next: {type: String, default: '/'}
+    defaultInvitation: { type: String, required: false, default: null },
+    next: { type: String, default: "/" }
   },
   components: {
     PasswordInput
   },
-  data () {
+  data() {
     return {
-      username: '',
-      email: '',
-      password: '',
+      username: "",
+      email: "",
+      password: "",
       isLoadingInstanceSetting: true,
       errors: [],
       isLoading: false,
       invitation: this.defaultInvitation
     }
   },
-  created () {
+  created() {
     let self = this
-    this.$store.dispatch('instance/fetchSettings', {
-      callback: function () {
+    this.$store.dispatch("instance/fetchSettings", {
+      callback: function() {
         self.isLoadingInstanceSetting = false
       }
     })
   },
   computed: {
-    labels () {
-      let title = this.$gettext('Sign Up')
-      let placeholder = this.$gettext('Enter your invitation code (case insensitive)')
-      let usernamePlaceholder = this.$gettext('Enter your username')
-      let emailPlaceholder = this.$gettext('Enter your email')
+    labels() {
+      let title = this.$gettext("Sign Up")
+      let placeholder = this.$gettext(
+        "Enter your invitation code (case insensitive)"
+      )
+      let usernamePlaceholder = this.$gettext("Enter your username")
+      let emailPlaceholder = this.$gettext("Enter your email")
       return {
         title,
         usernamePlaceholder,
@@ -105,7 +107,7 @@ export default {
     }
   },
   methods: {
-    submit () {
+    submit() {
       var self = this
       self.isLoading = true
       this.errors = []
@@ -116,17 +118,21 @@ export default {
         email: this.email,
         invitation: this.invitation
       }
-      return axios.post('auth/registration/', payload).then(response => {
-        logger.default.info('Successfully created account')
-        self.$router.push({
-          name: 'profile',
-          params: {
-            username: this.username
-          }})
-      }, error => {
-        self.errors = error.backendErrors
-        self.isLoading = false
-      })
+      return axios.post("auth/registration/", payload).then(
+        response => {
+          logger.default.info("Successfully created account")
+          self.$router.push({
+            name: "profile",
+            params: {
+              username: this.username
+            }
+          })
+        },
+        error => {
+          self.errors = error.backendErrors
+          self.isLoading = false
+        }
+      )
     }
   }
 }
diff --git a/front/src/components/favorites/List.vue b/front/src/components/favorites/List.vue
index f178e41b38523558e17d25317a6d2d9eeb0fc9b6..d3f8fce442c8dc4d1ba196caa9f1f61b63e23c0f 100644
--- a/front/src/components/favorites/List.vue
+++ b/front/src/components/favorites/List.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="main pusher" v-title="labels.title">
-    <div class="ui vertical center aligned stripe segment">
+  <main class="main pusher" v-title="labels.title">
+    <section class="ui vertical center aligned stripe segment">
       <div :class="['ui', {'active': isLoading}, 'inverted', 'dimmer']">
         <div class="ui text loader">
           <translate>Loading your favorites...</translate>
@@ -16,8 +16,8 @@
         </translate>
       </h2>
       <radio-button type="favorites"></radio-button>
-    </div>
-    <div class="ui vertical stripe segment">
+    </section>
+    <section class="ui vertical stripe segment">
       <div :class="['ui', {'loading': isLoading}, 'form']">
         <div class="fields">
           <div class="field">
@@ -56,21 +56,21 @@
           :total="results.count"
           ></pagination>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import axios from 'axios'
-import $ from 'jquery'
-import logger from '@/logging'
-import TrackTable from '@/components/audio/track/Table'
-import RadioButton from '@/components/radios/Button'
-import Pagination from '@/components/Pagination'
-import OrderingMixin from '@/components/mixins/Ordering'
-import PaginationMixin from '@/components/mixins/Pagination'
-import TranslationsMixin from '@/components/mixins/Translations'
-const FAVORITES_URL = 'tracks/'
+import axios from "axios"
+import $ from "jquery"
+import logger from "@/logging"
+import TrackTable from "@/components/audio/track/Table"
+import RadioButton from "@/components/radios/Button"
+import Pagination from "@/components/Pagination"
+import OrderingMixin from "@/components/mixins/Ordering"
+import PaginationMixin from "@/components/mixins/Pagination"
+import TranslationsMixin from "@/components/mixins/Translations"
+const FAVORITES_URL = "tracks/"
 
 export default {
   mixins: [OrderingMixin, PaginationMixin, TranslationsMixin],
@@ -79,8 +79,10 @@ export default {
     RadioButton,
     Pagination
   },
-  data () {
-    let defaultOrdering = this.getOrderingFromString(this.defaultOrdering || '-creation_date')
+  data() {
+    let defaultOrdering = this.getOrderingFromString(
+      this.defaultOrdering || "-creation_date"
+    )
     return {
       results: null,
       isLoading: false,
@@ -88,31 +90,31 @@ export default {
       previousLink: null,
       page: parseInt(this.defaultPage),
       paginateBy: parseInt(this.defaultPaginateBy || 25),
-      orderingDirection: defaultOrdering.direction || '+',
+      orderingDirection: defaultOrdering.direction || "+",
       ordering: defaultOrdering.field,
       orderingOptions: [
-        ['creation_date', 'creation_date'],
-        ['title', 'track_title'],
-        ['album__title', 'album_title'],
-        ['artist__name', 'artist_name']
+        ["creation_date", "creation_date"],
+        ["title", "track_title"],
+        ["album__title", "album_title"],
+        ["artist__name", "artist_name"]
       ]
     }
   },
-  created () {
+  created() {
     this.fetchFavorites(FAVORITES_URL)
   },
-  mounted () {
-    $('.ui.dropdown').dropdown()
+  mounted() {
+    $(".ui.dropdown").dropdown()
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        title: this.$gettext('Your Favorites')
+        title: this.$gettext("Your Favorites")
       }
     }
   },
   methods: {
-    updateQueryString: function () {
+    updateQueryString: function() {
       this.$router.replace({
         query: {
           page: this.page,
@@ -121,42 +123,42 @@ export default {
         }
       })
     },
-    fetchFavorites (url) {
+    fetchFavorites(url) {
       var self = this
       this.isLoading = true
       let params = {
-        favorites: 'true',
+        favorites: "true",
         page: this.page,
         page_size: this.paginateBy,
         ordering: this.getOrderingAsString()
       }
-      logger.default.time('Loading user favorites')
-      axios.get(url, {params: params}).then((response) => {
+      logger.default.time("Loading user favorites")
+      axios.get(url, { params: params }).then(response => {
         self.results = response.data
         self.nextLink = response.data.next
         self.previousLink = response.data.previous
-        self.results.results.forEach((track) => {
-          self.$store.commit('favorites/track', {id: track.id, value: true})
+        self.results.results.forEach(track => {
+          self.$store.commit("favorites/track", { id: track.id, value: true })
         })
-        logger.default.timeEnd('Loading user favorites')
+        logger.default.timeEnd("Loading user favorites")
         self.isLoading = false
       })
     },
-    selectPage: function (page) {
+    selectPage: function(page) {
       this.page = page
     }
   },
   watch: {
-    page: function () {
+    page: function() {
       this.updateQueryString()
     },
-    paginateBy: function () {
+    paginateBy: function() {
       this.updateQueryString()
     },
-    orderingDirection: function () {
+    orderingDirection: function() {
       this.updateQueryString()
     },
-    ordering: function () {
+    ordering: function() {
       this.updateQueryString()
     }
   }
diff --git a/front/src/components/library/Album.vue b/front/src/components/library/Album.vue
index 8ea983369df8e87766153229485cf6a62180514e..943aa9fc92103f950da8b33771b9e4b903cece9c 100644
--- a/front/src/components/library/Album.vue
+++ b/front/src/components/library/Album.vue
@@ -1,10 +1,10 @@
 <template>
-  <div>
+  <main>
     <div v-if="isLoading" class="ui vertical segment" v-title="">
       <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
     </div>
     <template v-if="album">
-      <div :class="['ui', 'head', {'with-background': album.cover.original}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle" v-title="album.title">
+      <section :class="['ui', 'head', {'with-background': album.cover.original}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle" v-title="album.title">
         <div class="segment-content">
           <h2 class="ui center aligned icon header">
             <i class="circular inverted sound yellow icon"></i>
@@ -38,86 +38,93 @@
             <translate>View on MusicBrainz</translate>
           </a>
         </div>
-      </div>
-      <div class="ui vertical stripe segment">
+      </section>
+      <section class="ui vertical stripe segment">
         <h2>
           <translate>Tracks</translate>
         </h2>
         <track-table v-if="album" :artist="album.artist" :display-position="true" :tracks="album.tracks"></track-table>
-      </div>
-      <div class="ui vertical stripe segment">
+      </section>
+      <section class="ui vertical stripe segment">
         <h2>
           <translate>User libraries</translate>
         </h2>
         <library-widget :url="'albums/' + id + '/libraries/'">
           <translate slot="subtitle">This album is present in the following libraries:</translate>
         </library-widget>
-      </div>
+      </section>
     </template>
-  </div>
+  </main>
 </template>
 
 <script>
-import axios from 'axios'
-import logger from '@/logging'
-import backend from '@/audio/backend'
-import PlayButton from '@/components/audio/PlayButton'
-import TrackTable from '@/components/audio/track/Table'
-import LibraryWidget from '@/components/federation/LibraryWidget'
+import axios from "axios"
+import logger from "@/logging"
+import backend from "@/audio/backend"
+import PlayButton from "@/components/audio/PlayButton"
+import TrackTable from "@/components/audio/track/Table"
+import LibraryWidget from "@/components/federation/LibraryWidget"
 
-const FETCH_URL = 'albums/'
+const FETCH_URL = "albums/"
 
 export default {
-  props: ['id'],
+  props: ["id"],
   components: {
     PlayButton,
     TrackTable,
     LibraryWidget
   },
-  data () {
+  data() {
     return {
       isLoading: true,
       album: null
     }
   },
-  created () {
+  created() {
     this.fetchData()
   },
   methods: {
-    fetchData () {
+    fetchData() {
       var self = this
       this.isLoading = true
-      let url = FETCH_URL + this.id + '/'
+      let url = FETCH_URL + this.id + "/"
       logger.default.debug('Fetching album "' + this.id + '"')
-      axios.get(url).then((response) => {
+      axios.get(url).then(response => {
         self.album = backend.Album.clean(response.data)
         self.isLoading = false
       })
     }
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        title: this.$gettext('Album')
+        title: this.$gettext("Album")
       }
     },
-    wikipediaUrl () {
-      return 'https://en.wikipedia.org/w/index.php?search=' + encodeURI(this.album.title + ' ' + this.album.artist.name)
+    wikipediaUrl() {
+      return (
+        "https://en.wikipedia.org/w/index.php?search=" +
+        encodeURI(this.album.title + " " + this.album.artist.name)
+      )
     },
-    musicbrainzUrl () {
+    musicbrainzUrl() {
       if (this.album.mbid) {
-        return 'https://musicbrainz.org/release/' + this.album.mbid
+        return "https://musicbrainz.org/release/" + this.album.mbid
       }
     },
-    headerStyle () {
+    headerStyle() {
       if (!this.album.cover.original) {
-        return ''
+        return ""
       }
-      return 'background-image: url(' + this.$store.getters['instance/absoluteUrl'](this.album.cover.original) + ')'
+      return (
+        "background-image: url(" +
+        this.$store.getters["instance/absoluteUrl"](this.album.cover.original) +
+        ")"
+      )
     }
   },
   watch: {
-    id () {
+    id() {
       this.fetchData()
     }
   }
@@ -126,5 +133,4 @@ export default {
 
 <!-- Add "scoped" attribute to limit CSS to this component only -->
 <style scoped lang="scss">
-
 </style>
diff --git a/front/src/components/library/Artist.vue b/front/src/components/library/Artist.vue
index 953cea28964d4a1098e3e104470962c1d1af351f..e16e6728d50f2f4f597f87bb6f8e2211c4f3bc0e 100644
--- a/front/src/components/library/Artist.vue
+++ b/front/src/components/library/Artist.vue
@@ -1,10 +1,10 @@
 <template>
-  <div v-title="labels.title">
+  <main v-title="labels.title">
     <div v-if="isLoading" class="ui vertical segment">
       <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
     </div>
     <template v-if="artist">
-      <div :class="['ui', 'head', {'with-background': cover}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle" v-title="artist.name">
+      <section :class="['ui', 'head', {'with-background': cover}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle" v-title="artist.name">
         <div class="segment-content">
           <h2 class="ui center aligned icon header">
             <i class="circular inverted users violet icon"></i>
@@ -36,11 +36,11 @@
             <translate>View on MusicBrainz</translate>
           </a>
         </div>
-      </div>
-      <div v-if="isLoadingAlbums" class="ui vertical stripe segment">
+      </section>
+      <section v-if="isLoadingAlbums" class="ui vertical stripe segment">
         <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
-      </div>
-      <div v-else-if="albums && albums.length > 0" class="ui vertical stripe segment">
+      </section>
+      <section v-else-if="albums && albums.length > 0" class="ui vertical stripe segment">
         <h2>
           <translate>Albums by this artist</translate>
         </h2>
@@ -49,38 +49,38 @@
             <album-card :mode="'rich'" class="fluid" :album="album"></album-card>
           </div>
         </div>
-      </div>
-      <div v-if="tracks.length > 0" class="ui vertical stripe segment">
+      </section>
+      <section v-if="tracks.length > 0" class="ui vertical stripe segment">
         <h2>
           <translate>Tracks by this artist</translate>
         </h2>
         <track-table :display-position="true" :tracks="tracks"></track-table>
-      </div>
-      <div class="ui vertical stripe segment">
+      </section>
+      <section class="ui vertical stripe segment">
         <h2>
           <translate>User libraries</translate>
         </h2>
         <library-widget :url="'artists/' + id + '/libraries/'">
           <translate slot="subtitle">This artist is present in the following libraries:</translate>
         </library-widget>
-      </div>
+      </section>
     </template>
-  </div>
+  </main>
 </template>
 
 <script>
-import _ from 'lodash'
-import axios from 'axios'
-import logger from '@/logging'
-import backend from '@/audio/backend'
-import AlbumCard from '@/components/audio/album/Card'
-import RadioButton from '@/components/radios/Button'
-import PlayButton from '@/components/audio/PlayButton'
-import TrackTable from '@/components/audio/track/Table'
-import LibraryWidget from '@/components/federation/LibraryWidget'
+import _ from "lodash"
+import axios from "axios"
+import logger from "@/logging"
+import backend from "@/audio/backend"
+import AlbumCard from "@/components/audio/album/Card"
+import RadioButton from "@/components/radios/Button"
+import PlayButton from "@/components/audio/PlayButton"
+import TrackTable from "@/components/audio/track/Table"
+import LibraryWidget from "@/components/federation/LibraryWidget"
 
 export default {
-  props: ['id'],
+  props: ["id"],
   components: {
     AlbumCard,
     RadioButton,
@@ -88,7 +88,7 @@ export default {
     TrackTable,
     LibraryWidget
   },
-  data () {
+  data() {
     return {
       isLoading: true,
       isLoadingAlbums: true,
@@ -99,54 +99,63 @@ export default {
       tracks: []
     }
   },
-  created () {
+  created() {
     this.fetchData()
   },
   methods: {
-    fetchData () {
+    fetchData() {
       var self = this
       this.isLoading = true
       logger.default.debug('Fetching artist "' + this.id + '"')
-      axios.get('tracks/', {params: {artist: this.id}}).then((response) => {
+      axios.get("tracks/", { params: { artist: this.id } }).then(response => {
         self.tracks = response.data.results
         self.totalTracks = response.data.count
       })
-      axios.get('artists/' + this.id + '/').then((response) => {
+      axios.get("artists/" + this.id + "/").then(response => {
         self.artist = response.data
         self.isLoading = false
         self.isLoadingAlbums = true
-        axios.get('albums/', {params: {artist: self.id, ordering: '-release_date'}}).then((response) => {
-          self.totalAlbums = response.data.count
-          let parsed = JSON.parse(JSON.stringify(response.data.results))
-          self.albums = parsed.map((album) => {
-            return backend.Album.clean(album)
+        axios
+          .get("albums/", {
+            params: { artist: self.id, ordering: "-release_date" }
           })
+          .then(response => {
+            self.totalAlbums = response.data.count
+            let parsed = JSON.parse(JSON.stringify(response.data.results))
+            self.albums = parsed.map(album => {
+              return backend.Album.clean(album)
+            })
 
-          self.isLoadingAlbums = false
-        })
+            self.isLoadingAlbums = false
+          })
       })
     }
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        title: this.$gettext('Artist')
+        title: this.$gettext("Artist")
       }
     },
-    isPlayable () {
-      return this.artist.albums.filter((a) => {
-        return a.is_playable
-      }).length > 0
+    isPlayable() {
+      return (
+        this.artist.albums.filter(a => {
+          return a.is_playable
+        }).length > 0
+      )
     },
-    wikipediaUrl () {
-      return 'https://en.wikipedia.org/w/index.php?search=' + encodeURI(this.artist.name)
+    wikipediaUrl() {
+      return (
+        "https://en.wikipedia.org/w/index.php?search=" +
+        encodeURI(this.artist.name)
+      )
     },
-    musicbrainzUrl () {
+    musicbrainzUrl() {
       if (this.artist.mbid) {
-        return 'https://musicbrainz.org/artist/' + this.artist.mbid
+        return "https://musicbrainz.org/artist/" + this.artist.mbid
       }
     },
-    allTracks () {
+    allTracks() {
       let tracks = []
       this.albums.forEach(album => {
         album.tracks.forEach(track => {
@@ -155,22 +164,28 @@ export default {
       })
       return tracks
     },
-    cover () {
-      return this.artist.albums.filter(album => {
-        return album.cover
-      }).map(album => {
-        return album.cover
-      })[0]
+    cover() {
+      return this.artist.albums
+        .filter(album => {
+          return album.cover
+        })
+        .map(album => {
+          return album.cover
+        })[0]
     },
-    headerStyle () {
+    headerStyle() {
       if (!this.cover || !this.cover.original) {
-        return ''
+        return ""
       }
-      return 'background-image: url(' + this.$store.getters['instance/absoluteUrl'](this.cover.original) + ')'
+      return (
+        "background-image: url(" +
+        this.$store.getters["instance/absoluteUrl"](this.cover.original) +
+        ")"
+      )
     }
   },
   watch: {
-    id () {
+    id() {
       this.fetchData()
     }
   }
diff --git a/front/src/components/library/Artists.vue b/front/src/components/library/Artists.vue
index 379d07e4b8bccd72838effeb99d8d42f0d37c8ac..83dd0e8e263797af0eb3c8caa2f808427eced4d8 100644
--- a/front/src/components/library/Artists.vue
+++ b/front/src/components/library/Artists.vue
@@ -1,6 +1,6 @@
 <template>
-  <div v-title="labels.title">
-    <div class="ui vertical stripe segment">
+  <main v-title="labels.title">
+    <section class="ui vertical stripe segment">
       <h2 class="ui header">
         <translate>Browsing artists</translate>
       </h2>
@@ -64,60 +64,59 @@
           :total="result.count"
           ></pagination>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import axios from 'axios'
-import _ from 'lodash'
-import $ from 'jquery'
+import axios from "axios"
+import _ from "lodash"
+import $ from "jquery"
 
-import logger from '@/logging'
+import logger from "@/logging"
 
-import OrderingMixin from '@/components/mixins/Ordering'
-import PaginationMixin from '@/components/mixins/Pagination'
-import TranslationsMixin from '@/components/mixins/Translations'
-import ArtistCard from '@/components/audio/artist/Card'
-import Pagination from '@/components/Pagination'
+import OrderingMixin from "@/components/mixins/Ordering"
+import PaginationMixin from "@/components/mixins/Pagination"
+import TranslationsMixin from "@/components/mixins/Translations"
+import ArtistCard from "@/components/audio/artist/Card"
+import Pagination from "@/components/Pagination"
 
-const FETCH_URL = 'artists/'
+const FETCH_URL = "artists/"
 
 export default {
   mixins: [OrderingMixin, PaginationMixin, TranslationsMixin],
   props: {
-    defaultQuery: {type: String, required: false, default: ''}
+    defaultQuery: { type: String, required: false, default: "" }
   },
   components: {
     ArtistCard,
     Pagination
   },
-  data () {
-    let defaultOrdering = this.getOrderingFromString(this.defaultOrdering || '-creation_date')
+  data() {
+    let defaultOrdering = this.getOrderingFromString(
+      this.defaultOrdering || "-creation_date"
+    )
     return {
       isLoading: true,
       result: null,
       page: parseInt(this.defaultPage),
       query: this.defaultQuery,
       paginateBy: parseInt(this.defaultPaginateBy || 12),
-      orderingDirection: defaultOrdering.direction || '+',
+      orderingDirection: defaultOrdering.direction || "+",
       ordering: defaultOrdering.field,
-      orderingOptions: [
-        ['creation_date', 'creation_date'],
-        ['name', 'name']
-      ]
+      orderingOptions: [["creation_date", "creation_date"], ["name", "name"]]
     }
   },
-  created () {
+  created() {
     this.fetchData()
   },
-  mounted () {
-    $('.ui.dropdown').dropdown()
+  mounted() {
+    $(".ui.dropdown").dropdown()
   },
   computed: {
-    labels () {
-      let searchPlaceholder = this.$gettext('Enter an artist name...')
-      let title = this.$gettext('Artists')
+    labels() {
+      let searchPlaceholder = this.$gettext("Enter an artist name...")
+      let title = this.$gettext("Artists")
       return {
         searchPlaceholder,
         title
@@ -125,7 +124,7 @@ export default {
     }
   },
   methods: {
-    updateQueryString: _.debounce(function () {
+    updateQueryString: _.debounce(function() {
       this.$router.replace({
         query: {
           query: this.query,
@@ -135,7 +134,7 @@ export default {
         }
       })
     }, 500),
-    fetchData: _.debounce(function () {
+    fetchData: _.debounce(function() {
       var self = this
       this.isLoading = true
       let url = FETCH_URL
@@ -144,36 +143,36 @@ export default {
         page_size: this.paginateBy,
         name__icontains: this.query,
         ordering: this.getOrderingAsString(),
-        playable: 'true'
+        playable: "true"
       }
-      logger.default.debug('Fetching artists')
-      axios.get(url, {params: params}).then((response) => {
+      logger.default.debug("Fetching artists")
+      axios.get(url, { params: params }).then(response => {
         self.result = response.data
         self.isLoading = false
       })
     }, 500),
-    selectPage: function (page) {
+    selectPage: function(page) {
       this.page = page
     }
   },
   watch: {
-    page () {
+    page() {
       this.updateQueryString()
       this.fetchData()
     },
-    paginateBy () {
+    paginateBy() {
       this.updateQueryString()
       this.fetchData()
     },
-    ordering () {
+    ordering() {
       this.updateQueryString()
       this.fetchData()
     },
-    orderingDirection () {
+    orderingDirection() {
       this.updateQueryString()
       this.fetchData()
     },
-    query () {
+    query() {
       this.updateQueryString()
       this.fetchData()
     }
diff --git a/front/src/components/library/Home.vue b/front/src/components/library/Home.vue
index e11127608818e9024ba38f3c85ecef5860a7f7f2..5e81dbdc86acd8f495e1c667d35f8e119b768918 100644
--- a/front/src/components/library/Home.vue
+++ b/front/src/components/library/Home.vue
@@ -1,6 +1,6 @@
 <template>
-  <div v-title="labels.title">
-    <div class="ui vertical stripe segment">
+  <main v-title="labels.title">
+    <section class="ui vertical stripe segment">
       <div class="ui stackable three column grid">
         <div class="column">
           <track-widget :url="'history/listenings/'" :filters="{scope: 'user', ordering: '-creation_date'}">
@@ -26,23 +26,23 @@
           </album-widget>
         </div>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import axios from 'axios'
-import Search from '@/components/audio/Search'
-import logger from '@/logging'
-import ArtistCard from '@/components/audio/artist/Card'
-import TrackWidget from '@/components/audio/track/Widget'
-import AlbumWidget from '@/components/audio/album/Widget'
-import PlaylistWidget from '@/components/playlists/Widget'
+import axios from "axios"
+import Search from "@/components/audio/Search"
+import logger from "@/logging"
+import ArtistCard from "@/components/audio/artist/Card"
+import TrackWidget from "@/components/audio/track/Widget"
+import AlbumWidget from "@/components/audio/album/Widget"
+import PlaylistWidget from "@/components/playlists/Widget"
 
-const ARTISTS_URL = 'artists/'
+const ARTISTS_URL = "artists/"
 
 export default {
-  name: 'library',
+  name: "library",
   components: {
     Search,
     ArtistCard,
@@ -50,35 +50,35 @@ export default {
     AlbumWidget,
     PlaylistWidget
   },
-  data () {
+  data() {
     return {
       artists: [],
       isLoadingArtists: false
     }
   },
-  created () {
+  created() {
     this.fetchArtists()
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        title: this.$gettext('Home')
+        title: this.$gettext("Home")
       }
     }
   },
   methods: {
-    fetchArtists () {
+    fetchArtists() {
       var self = this
       this.isLoadingArtists = true
       let params = {
-        ordering: '-creation_date',
+        ordering: "-creation_date",
         playable: true
       }
       let url = ARTISTS_URL
-      logger.default.time('Loading latest artists')
-      axios.get(url, {params: params}).then((response) => {
+      logger.default.time("Loading latest artists")
+      axios.get(url, { params: params }).then(response => {
         self.artists = response.data.results
-        logger.default.timeEnd('Loading latest artists')
+        logger.default.timeEnd("Loading latest artists")
         self.isLoadingArtists = false
       })
     }
diff --git a/front/src/components/library/Library.vue b/front/src/components/library/Library.vue
index 01a357724990ed107cffd4d3d7f50e06d9fb4e4d..c0371fe58800145856eaf18c30192565184a183f 100644
--- a/front/src/components/library/Library.vue
+++ b/front/src/components/library/Library.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="main library pusher">
-    <div class="ui secondary pointing menu">
+    <nav class="ui secondary pointing menu" role="navigation" :aria-label="labels.secondaryMenu">
       <router-link class="ui item" to="/library" exact>
         <translate>Browse</translate>
       </router-link>
@@ -13,7 +13,7 @@
       <router-link class="ui item" to="/library/playlists" exact>
         <translate>Playlists</translate>
       </router-link>
-    </div>
+    </nav>
     <router-view :key="$route.fullPath"></router-view>
   </div>
 </template>
@@ -21,8 +21,16 @@
 <script>
 export default {
   computed: {
-    showImports () {
-      return this.$store.state.auth.availablePermissions['upload'] || this.$store.state.auth.availablePermissions['library']
+    showImports() {
+      return (
+        this.$store.state.auth.availablePermissions["upload"] ||
+        this.$store.state.auth.availablePermissions["library"]
+      )
+    },
+    labels() {
+      return {
+        secondaryMenu: this.$gettext("Secondary menu")
+      }
     }
   }
 }
@@ -30,7 +38,7 @@ export default {
 
 <!-- Add "scoped" attribute to limit CSS to this component only -->
 <style lang="scss">
-@import '../../style/vendor/media';
+@import "../../style/vendor/media";
 
 .library {
   .ui.segment.head {
@@ -46,18 +54,16 @@ export default {
     }
     &.with-background {
       .header {
-        &, .sub {
+        &,
+        .sub {
           text-shadow: 0 1px 0 rgba(0, 0, 0, 0.8);
           color: white !important;
         }
       }
       .segment-content {
-        background-color: rgba(0, 0, 0, 0.5)
+        background-color: rgba(0, 0, 0, 0.5);
       }
-
     }
   }
 }
-
-
 </style>
diff --git a/front/src/components/library/Radios.vue b/front/src/components/library/Radios.vue
index 4cc1d58df1286509747129b2fe31fde5eb22a291..48e9b1e2f324e8027f97013d04fc353f8eaca042 100644
--- a/front/src/components/library/Radios.vue
+++ b/front/src/components/library/Radios.vue
@@ -1,6 +1,6 @@
 <template>
-  <div v-title="labels.title">
-    <div class="ui vertical stripe segment">
+  <main v-title="labels.title">
+    <section class="ui vertical stripe segment">
       <h2 class="ui header">
         <translate>Browsing radios</translate>
       </h2>
@@ -86,60 +86,59 @@
           :total="result.count"
           ></pagination>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import axios from 'axios'
-import _ from 'lodash'
-import $ from 'jquery'
+import axios from "axios"
+import _ from "lodash"
+import $ from "jquery"
 
-import logger from '@/logging'
+import logger from "@/logging"
 
-import OrderingMixin from '@/components/mixins/Ordering'
-import PaginationMixin from '@/components/mixins/Pagination'
-import TranslationsMixin from '@/components/mixins/Translations'
-import RadioCard from '@/components/radios/Card'
-import Pagination from '@/components/Pagination'
+import OrderingMixin from "@/components/mixins/Ordering"
+import PaginationMixin from "@/components/mixins/Pagination"
+import TranslationsMixin from "@/components/mixins/Translations"
+import RadioCard from "@/components/radios/Card"
+import Pagination from "@/components/Pagination"
 
-const FETCH_URL = 'radios/radios/'
+const FETCH_URL = "radios/radios/"
 
 export default {
   mixins: [OrderingMixin, PaginationMixin, TranslationsMixin],
   props: {
-    defaultQuery: {type: String, required: false, default: ''}
+    defaultQuery: { type: String, required: false, default: "" }
   },
   components: {
     RadioCard,
     Pagination
   },
-  data () {
-    let defaultOrdering = this.getOrderingFromString(this.defaultOrdering || '-creation_date')
+  data() {
+    let defaultOrdering = this.getOrderingFromString(
+      this.defaultOrdering || "-creation_date"
+    )
     return {
       isLoading: true,
       result: null,
       page: parseInt(this.defaultPage),
       query: this.defaultQuery,
       paginateBy: parseInt(this.defaultPaginateBy || 12),
-      orderingDirection: defaultOrdering.direction || '+',
+      orderingDirection: defaultOrdering.direction || "+",
       ordering: defaultOrdering.field,
-      orderingOptions: [
-        ['creation_date', 'creation_date'],
-        ['name', 'name']
-      ]
+      orderingOptions: [["creation_date", "creation_date"], ["name", "name"]]
     }
   },
-  created () {
+  created() {
     this.fetchData()
   },
-  mounted () {
-    $('.ui.dropdown').dropdown()
+  mounted() {
+    $(".ui.dropdown").dropdown()
   },
   computed: {
-    labels () {
-      let searchPlaceholder = this.$gettext('Enter a radio name...')
-      let title = this.$gettext('Radios')
+    labels() {
+      let searchPlaceholder = this.$gettext("Enter a radio name...")
+      let title = this.$gettext("Radios")
       return {
         searchPlaceholder,
         title
@@ -147,7 +146,7 @@ export default {
     }
   },
   methods: {
-    updateQueryString: _.debounce(function () {
+    updateQueryString: _.debounce(function() {
       this.$router.replace({
         query: {
           query: this.query,
@@ -157,7 +156,7 @@ export default {
         }
       })
     }, 500),
-    fetchData: _.debounce(function () {
+    fetchData: _.debounce(function() {
       var self = this
       this.isLoading = true
       let url = FETCH_URL
@@ -167,34 +166,34 @@ export default {
         name__icontains: this.query,
         ordering: this.getOrderingAsString()
       }
-      logger.default.debug('Fetching radios')
-      axios.get(url, {params: params}).then((response) => {
+      logger.default.debug("Fetching radios")
+      axios.get(url, { params: params }).then(response => {
         self.result = response.data
         self.isLoading = false
       })
     }, 500),
-    selectPage: function (page) {
+    selectPage: function(page) {
       this.page = page
     }
   },
   watch: {
-    page () {
+    page() {
       this.updateQueryString()
       this.fetchData()
     },
-    paginateBy () {
+    paginateBy() {
       this.updateQueryString()
       this.fetchData()
     },
-    ordering () {
+    ordering() {
       this.updateQueryString()
       this.fetchData()
     },
-    orderingDirection () {
+    orderingDirection() {
       this.updateQueryString()
       this.fetchData()
     },
-    query () {
+    query() {
       this.updateQueryString()
       this.fetchData()
     }
diff --git a/front/src/components/library/Track.vue b/front/src/components/library/Track.vue
index ddccda397352bc578999608489f153519f332fe8..a66b240f542d796aef4c8290bfe55681cbf96d1f 100644
--- a/front/src/components/library/Track.vue
+++ b/front/src/components/library/Track.vue
@@ -1,10 +1,10 @@
 <template>
-  <div>
+  <main>
     <div v-if="isLoadingTrack" class="ui vertical segment" v-title="labels.title">
       <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
     </div>
     <template v-if="track">
-      <div :class="['ui', 'head', {'with-background': cover}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle" v-title="track.title">
+      <section :class="['ui', 'head', {'with-background': cover}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle" v-title="track.title">
         <div class="segment-content">
           <h2 class="ui center aligned icon header">
             <i class="circular inverted music orange icon"></i>
@@ -49,8 +49,8 @@
             <translate>Download</translate>
           </a>
         </div>
-      </div>
-      <div class="ui vertical stripe center aligned segment" v-if="upload">
+      </section>
+      <section class="ui vertical stripe center aligned segment" v-if="upload">
         <h2 class="ui header"><translate>Track information</translate></h2>
         <table class="ui very basic collapsing celled center aligned table">
           <tbody>
@@ -100,8 +100,8 @@
             </tr>
           </tbody>
         </table>
-      </div>
-      <div class="ui vertical stripe center aligned segment">
+      </section>
+      <section class="ui vertical stripe center aligned segment">
         <h2>
           <translate>Lyrics</translate>
         </h2>
@@ -117,41 +117,40 @@
             <translate>Search on lyrics.wikia.com</translate>
           </a>
         </template>
-      </div>
-      <div class="ui vertical stripe segment">
+      </section>
+      <section class="ui vertical stripe segment">
         <h2>
           <translate>User libraries</translate>
         </h2>
         <library-widget :url="'tracks/' + id + '/libraries/'">
           <translate slot="subtitle">This track is present in the following libraries:</translate>
         </library-widget>
-      </div>
+      </section>
     </template>
-  </div>
+  </main>
 </template>
 
 <script>
+import time from "@/utils/time"
+import axios from "axios"
+import url from "@/utils/url"
+import logger from "@/logging"
+import PlayButton from "@/components/audio/PlayButton"
+import TrackFavoriteIcon from "@/components/favorites/TrackFavoriteIcon"
+import TrackPlaylistIcon from "@/components/playlists/TrackPlaylistIcon"
+import LibraryWidget from "@/components/federation/LibraryWidget"
 
-import time from '@/utils/time'
-import axios from 'axios'
-import url from '@/utils/url'
-import logger from '@/logging'
-import PlayButton from '@/components/audio/PlayButton'
-import TrackFavoriteIcon from '@/components/favorites/TrackFavoriteIcon'
-import TrackPlaylistIcon from '@/components/playlists/TrackPlaylistIcon'
-import LibraryWidget from '@/components/federation/LibraryWidget'
-
-const FETCH_URL = 'tracks/'
+const FETCH_URL = "tracks/"
 
 export default {
-  props: ['id'],
+  props: ["id"],
   components: {
     PlayButton,
     TrackPlaylistIcon,
     TrackFavoriteIcon,
     LibraryWidget
   },
-  data () {
+  data() {
     return {
       time,
       isLoadingTrack: true,
@@ -160,78 +159,94 @@ export default {
       lyrics: null
     }
   },
-  created () {
+  created() {
     this.fetchData()
     this.fetchLyrics()
   },
   methods: {
-    fetchData () {
+    fetchData() {
       var self = this
       this.isLoadingTrack = true
-      let url = FETCH_URL + this.id + '/'
+      let url = FETCH_URL + this.id + "/"
       logger.default.debug('Fetching track "' + this.id + '"')
-      axios.get(url).then((response) => {
+      axios.get(url).then(response => {
         self.track = response.data
         self.isLoadingTrack = false
       })
     },
-    fetchLyrics () {
+    fetchLyrics() {
       var self = this
       this.isLoadingLyrics = true
-      let url = FETCH_URL + this.id + '/lyrics/'
+      let url = FETCH_URL + this.id + "/lyrics/"
       logger.default.debug('Fetching lyrics for track "' + this.id + '"')
-      axios.get(url).then((response) => {
-        self.lyrics = response.data
-        self.isLoadingLyrics = false
-      }, (response) => {
-        console.error('No lyrics available')
-        self.isLoadingLyrics = false
-      })
+      axios.get(url).then(
+        response => {
+          self.lyrics = response.data
+          self.isLoadingLyrics = false
+        },
+        response => {
+          console.error("No lyrics available")
+          self.isLoadingLyrics = false
+        }
+      )
     }
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        title: this.$gettext('Track')
+        title: this.$gettext("Track")
       }
     },
-    upload () {
+    upload() {
       if (this.track.uploads) {
         return this.track.uploads[0]
       }
     },
-    wikipediaUrl () {
-      return 'https://en.wikipedia.org/w/index.php?search=' + encodeURI(this.track.title + ' ' + this.track.artist.name)
+    wikipediaUrl() {
+      return (
+        "https://en.wikipedia.org/w/index.php?search=" +
+        encodeURI(this.track.title + " " + this.track.artist.name)
+      )
     },
-    musicbrainzUrl () {
+    musicbrainzUrl() {
       if (this.track.mbid) {
-        return 'https://musicbrainz.org/recording/' + this.track.mbid
+        return "https://musicbrainz.org/recording/" + this.track.mbid
       }
     },
-    downloadUrl () {
-      let u = this.$store.getters['instance/absoluteUrl'](this.upload.listen_url)
+    downloadUrl() {
+      let u = this.$store.getters["instance/absoluteUrl"](
+        this.upload.listen_url
+      )
       if (this.$store.state.auth.authenticated) {
-        u = url.updateQueryString(u, 'jwt', encodeURI(this.$store.state.auth.token))
+        u = url.updateQueryString(
+          u,
+          "jwt",
+          encodeURI(this.$store.state.auth.token)
+        )
       }
       return u
     },
-    lyricsSearchUrl () {
-      let base = 'http://lyrics.wikia.com/wiki/Special:Search?query='
-      let query = this.track.artist.name + ' ' + this.track.title
+    lyricsSearchUrl() {
+      let base = "http://lyrics.wikia.com/wiki/Special:Search?query="
+      let query = this.track.artist.name + " " + this.track.title
       return base + encodeURI(query)
     },
-    cover () {
+    cover() {
       return null
     },
-    headerStyle () {
+    headerStyle() {
       if (!this.cover) {
-        return ''
+        return ""
       }
-      return 'background-image: url(' + this.$store.getters['instance/absoluteUrl'](this.cover) + ')'
+      return (
+        "background-image: url(" +
+        this.$store.getters["instance/absoluteUrl"](this.cover) +
+        ")"
+      )
     }
   },
   watch: {
-    id () {
+    id() {
       this.fetchData()
     }
   }
diff --git a/front/src/components/library/radios/Builder.vue b/front/src/components/library/radios/Builder.vue
index 91ea702464d1768b4ef1d3879cd5612c8f917743..b0157497987c8b6545cfab6eb819e8488194980e 100644
--- a/front/src/components/library/radios/Builder.vue
+++ b/front/src/components/library/radios/Builder.vue
@@ -1,7 +1,7 @@
 <template>
   <div class="ui vertical stripe segment" v-title="labels.title">
     <div>
-      <div>
+      <section>
         <h2 class="ui header">
           <translate>Builder</translate>
         </h2>
@@ -87,28 +87,28 @@
           </h3>
           <track-table v-if="checkResult.candidates.sample" :tracks="checkResult.candidates.sample"></track-table>
         </template>
-      </div>
+      </section>
     </div>
   </div>
 </template>
 <script>
-import axios from 'axios'
-import $ from 'jquery'
-import _ from 'lodash'
-import BuilderFilter from './Filter'
-import TrackTable from '@/components/audio/track/Table'
-import RadioButton from '@/components/radios/Button'
+import axios from "axios"
+import $ from "jquery"
+import _ from "lodash"
+import BuilderFilter from "./Filter"
+import TrackTable from "@/components/audio/track/Table"
+import RadioButton from "@/components/radios/Button"
 
 export default {
   props: {
-    id: {required: false}
+    id: { required: false }
   },
   components: {
     BuilderFilter,
     TrackTable,
     RadioButton
   },
-  data: function () {
+  data: function() {
     return {
       isLoading: false,
       success: false,
@@ -116,12 +116,12 @@ export default {
       currentFilterType: null,
       filters: [],
       checkResult: null,
-      radioName: '',
-      radioDesc: '',
+      radioName: "",
+      radioDesc: "",
       isPublic: true
     }
   },
-  created: function () {
+  created: function() {
     let self = this
     this.fetchFilters().then(() => {
       if (self.id) {
@@ -129,18 +129,18 @@ export default {
       }
     })
   },
-  mounted () {
-    $('.ui.dropdown').dropdown()
+  mounted() {
+    $(".ui.dropdown").dropdown()
   },
   methods: {
-    fetchFilters: function () {
+    fetchFilters: function() {
       let self = this
-      let url = 'radios/radios/filters/'
-      return axios.get(url).then((response) => {
+      let url = "radios/radios/filters/"
+      return axios.get(url).then(response => {
         self.availableFilters = response.data
       })
     },
-    add () {
+    add() {
       this.filters.push({
         config: {},
         filter: this.currentFilter,
@@ -148,23 +148,25 @@ export default {
       })
       this.fetchCandidates()
     },
-    updateConfig (index, field, value) {
+    updateConfig(index, field, value) {
       this.filters[index].config[field] = value
       this.fetchCandidates()
     },
-    deleteFilter (index) {
+    deleteFilter(index) {
       this.filters.splice(index, 1)
       this.fetchCandidates()
     },
-    fetch: function () {
+    fetch: function() {
       let self = this
       self.isLoading = true
-      let url = 'radios/radios/' + this.id + '/'
-      axios.get(url).then((response) => {
+      let url = "radios/radios/" + this.id + "/"
+      axios.get(url).then(response => {
         self.filters = response.data.config.map(f => {
           return {
             config: f,
-            filter: this.availableFilters.filter(e => { return e.type === f.type })[0],
+            filter: this.availableFilters.filter(e => {
+              return e.type === f.type
+            })[0],
             hash: +new Date()
           }
         })
@@ -174,24 +176,22 @@ export default {
         self.isLoading = false
       })
     },
-    fetchCandidates: function () {
+    fetchCandidates: function() {
       let self = this
-      let url = 'radios/radios/validate/'
+      let url = "radios/radios/validate/"
       let final = this.filters.map(f => {
         let c = _.clone(f.config)
         c.type = f.filter.type
         return c
       })
       final = {
-        'filters': [
-          {'type': 'group', filters: final}
-        ]
+        filters: [{ type: "group", filters: final }]
       }
-      axios.post(url, final).then((response) => {
+      axios.post(url, final).then(response => {
         self.checkResult = response.data.filters[0]
       })
     },
-    save: function () {
+    save: function() {
       let self = this
       self.success = false
       self.isLoading = true
@@ -202,24 +202,24 @@ export default {
         return c
       })
       final = {
-        'name': this.radioName,
-        'description': this.radioDesc,
-        'is_public': this.isPublic,
-        'config': final
+        name: this.radioName,
+        description: this.radioDesc,
+        is_public: this.isPublic,
+        config: final
       }
       if (this.id) {
-        let url = 'radios/radios/' + this.id + '/'
-        axios.put(url, final).then((response) => {
+        let url = "radios/radios/" + this.id + "/"
+        axios.put(url, final).then(response => {
           self.isLoading = false
           self.success = true
         })
       } else {
-        let url = 'radios/radios/'
-        axios.post(url, final).then((response) => {
+        let url = "radios/radios/"
+        axios.post(url, final).then(response => {
           self.success = true
           self.isLoading = false
           self.$router.push({
-            name: 'library.radios.detail',
+            name: "library.radios.detail",
             params: {
               id: response.data.id
             }
@@ -229,30 +229,28 @@ export default {
     }
   },
   computed: {
-    labels () {
-      let title = this.$gettext('Radio Builder')
+    labels() {
+      let title = this.$gettext("Radio Builder")
       let placeholder = {
-        'name': this.$gettext('My awesome radio'),
-        'description': this.$gettext('My awesome description')
+        name: this.$gettext("My awesome radio"),
+        description: this.$gettext("My awesome description")
       }
       return {
         title,
         placeholder
       }
     },
-    canSave: function () {
-      return (
-        this.radioName.length > 0 && this.checkErrors.length === 0
-      )
+    canSave: function() {
+      return this.radioName.length > 0 && this.checkErrors.length === 0
     },
-    checkErrors: function () {
+    checkErrors: function() {
       if (!this.checkResult) {
         return []
       }
       let errors = this.checkResult.errors
       return errors
     },
-    currentFilter: function () {
+    currentFilter: function() {
       let self = this
       return this.availableFilters.filter(e => {
         return e.type === self.currentFilterType
@@ -261,7 +259,7 @@ export default {
   },
   watch: {
     filters: {
-      handler: function () {
+      handler: function() {
         this.fetchCandidates()
       },
       deep: true
diff --git a/front/src/components/metadata/ArtistCard.vue b/front/src/components/metadata/ArtistCard.vue
index ef2fc616af41523a33fa564d97cdc0905a93024d..98c35337ece4e0c093fc4d77fe0519b835f3b04a 100644
--- a/front/src/components/metadata/ArtistCard.vue
+++ b/front/src/components/metadata/ArtistCard.vue
@@ -5,9 +5,9 @@
         <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
       </div>
       <template v-if="data.id">
-        <div class="header">
+        <header class="header">
           <a :href="getMusicbrainzUrl('artist', data.id)" target="_blank" :title="labels.musicbrainz">{{ data.name }}</a>
-        </div>
+        </header>
         <div class="description">
           <table class="ui very basic fixed single line compact table">
             <tbody>
@@ -32,29 +32,29 @@
 </template>
 
 <script>
-import Vue from 'vue'
-import CardMixin from './CardMixin'
-import time from '@/utils/time'
+import Vue from "vue"
+import CardMixin from "./CardMixin"
+import time from "@/utils/time"
 
 export default Vue.extend({
   mixins: [CardMixin],
-  data () {
+  data() {
     return {
       time
     }
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        musicbrainz: this.$gettext('View on MusicBrainz')
+        musicbrainz: this.$gettext("View on MusicBrainz")
       }
     },
-    type () {
-      return 'artist'
+    type() {
+      return "artist"
     },
-    releasesGroups () {
-      return this.data['release-group-list'].filter(r => {
-        return r.type === 'Album'
+    releasesGroups() {
+      return this.data["release-group-list"].filter(r => {
+        return r.type === "Album"
       })
     }
   }
@@ -64,6 +64,6 @@ export default Vue.extend({
 <!-- Add "scoped" attribute to limit CSS to this component only -->
 <style scoped lang="scss">
 .ui.card {
-    width: 100% !important;
+  width: 100% !important;
 }
 </style>
diff --git a/front/src/views/Notifications.vue b/front/src/views/Notifications.vue
index 758283c779452540478c7e1c640e4d73e3000a27..d8be139965e216a8ea435140037b05db49ef2502 100644
--- a/front/src/views/Notifications.vue
+++ b/front/src/views/Notifications.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="main pusher" v-title="labels.title">
-    <div class="ui vertical aligned stripe segment">
+  <main class="main pusher" v-title="labels.title">
+    <section class="ui vertical aligned stripe segment">
       <div v-if="isLoading" :class="['ui', {'active': isLoading}, 'inverted', 'dimmer']">
         <div class="ui text loader"><translate>Loading notifications...</translate></div>
       </div>
@@ -27,19 +27,19 @@
           <translate>We don't have any notification to display!</translate>
         </p>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import {mapState} from 'vuex'
-import axios from 'axios'
-import logger from '@/logging'
+import { mapState } from "vuex"
+import axios from "axios"
+import logger from "@/logging"
 
-import NotificationRow from '@/components/notifications/NotificationRow'
+import NotificationRow from "@/components/notifications/NotificationRow"
 
 export default {
-  data () {
+  data() {
     return {
       isLoading: false,
       notifications: null,
@@ -51,64 +51,63 @@ export default {
   components: {
     NotificationRow
   },
-  created () {
+  created() {
     this.fetch(this.filters)
-    this.$store.commit('ui/addWebsocketEventHandler', {
-      eventName: 'inbox.item_added',
-      id: 'notificationPage',
+    this.$store.commit("ui/addWebsocketEventHandler", {
+      eventName: "inbox.item_added",
+      id: "notificationPage",
       handler: this.handleNewNotification
     })
   },
-  destroyed () {
-    this.$store.commit('ui/removeWebsocketEventHandler', {
-      eventName: 'inbox.item_added',
-      id: 'notificationPage',
+  destroyed() {
+    this.$store.commit("ui/removeWebsocketEventHandler", {
+      eventName: "inbox.item_added",
+      id: "notificationPage"
     })
   },
   computed: {
     ...mapState({
       events: state => state.instance.events
     }),
-    labels () {
+    labels() {
       return {
-        title: this.$gettext('Notifications'),
+        title: this.$gettext("Notifications")
       }
     }
   },
   methods: {
-    handleNewNotification (event) {
+    handleNewNotification(event) {
       this.notifications.results.unshift(event.item)
     },
-    fetch (params) {
+    fetch(params) {
       this.isLoading = true
       let self = this
-      axios.get('federation/inbox/', {params: params}).then((response) => {
+      axios.get("federation/inbox/", { params: params }).then(response => {
         self.isLoading = false
         self.notifications = response.data
       })
     },
-    markAllAsRead () {
+    markAllAsRead() {
       let self = this
       let before = this.notifications.results[0].id
       let payload = {
-        action: 'read',
-        objects: 'all',
+        action: "read",
+        objects: "all",
         filters: {
           is_read: false,
           before
         }
       }
-      axios.post('federation/inbox/action/', payload).then((response) => {
-        self.$store.commit('ui/notifications', {type: 'inbox', count: 0})
+      axios.post("federation/inbox/action/", payload).then(response => {
+        self.$store.commit("ui/notifications", { type: "inbox", count: 0 })
         self.notifications.results.forEach(n => {
           n.is_read = true
         })
-
       })
-    },
+    }
   },
   watch: {
-    'filters.is_read' () {
+    "filters.is_read"() {
       this.fetch(this.filters)
     }
   }
diff --git a/front/src/views/admin/Settings.vue b/front/src/views/admin/Settings.vue
index 0aa47a5c216498296fa7279a1a192c822a4c9cb4..890eba9fdfc5b8d8f30362ecb1b2d089c37925a0 100644
--- a/front/src/views/admin/Settings.vue
+++ b/front/src/views/admin/Settings.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="main pusher"  v-title="labels.settings">
+  <main class="main pusher"  v-title="labels.settings">
     <div class="ui vertical stripe segment">
       <div class="ui text container">
         <div :class="['ui', {'loading': isLoading}, 'form']"></div>
@@ -24,146 +24,140 @@
 
       </div>
     </div>
-  </div>
+  </main>
 </template>
 
 <script>
-import axios from 'axios'
-import $ from 'jquery'
+import axios from "axios"
+import $ from "jquery"
 
-import SettingsGroup from '@/components/admin/SettingsGroup'
+import SettingsGroup from "@/components/admin/SettingsGroup"
 
 export default {
   components: {
     SettingsGroup
   },
-  data () {
+  data() {
     return {
       isLoading: false,
       settingsData: null,
       current: null
     }
   },
-  created () {
+  created() {
     let self = this
     this.fetchSettings().then(r => {
       self.$nextTick(() => {
         if (self.$store.state.route.hash) {
           self.scrollTo(self.$store.state.route.hash.substr(1))
         }
-        $('select.dropdown').dropdown()
+        $("select.dropdown").dropdown()
       })
     })
   },
   methods: {
-    scrollTo (id) {
+    scrollTo(id) {
       this.current = id
       document.getElementById(id).scrollIntoView()
     },
-    fetchSettings () {
+    fetchSettings() {
       let self = this
       self.isLoading = true
-      return axios.get('instance/admin/settings/').then((response) => {
+      return axios.get("instance/admin/settings/").then(response => {
         self.settingsData = response.data
         self.isLoading = false
       })
     }
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        settings: this.$gettext('Instance settings')
+        settings: this.$gettext("Instance settings")
       }
     },
-    groups () {
+    groups() {
       // somehow, extraction fails if in the return block directly
-      let instanceLabel = this.$gettext('Instance information')
-      let usersLabel = this.$gettext('Users')
-      let musicLabel = this.$gettext('Music')
-      let playlistsLabel = this.$gettext('Playlists')
-      let federationLabel = this.$gettext('Federation')
-      let subsonicLabel = this.$gettext('Subsonic')
-      let statisticsLabel = this.$gettext('Statistics')
-      let errorLabel = this.$gettext('Error reporting')
+      let instanceLabel = this.$gettext("Instance information")
+      let usersLabel = this.$gettext("Users")
+      let musicLabel = this.$gettext("Music")
+      let playlistsLabel = this.$gettext("Playlists")
+      let federationLabel = this.$gettext("Federation")
+      let subsonicLabel = this.$gettext("Subsonic")
+      let statisticsLabel = this.$gettext("Statistics")
+      let errorLabel = this.$gettext("Error reporting")
       return [
         {
           label: instanceLabel,
-          id: 'instance',
+          id: "instance",
           settings: [
-            'instance__name',
-            'instance__short_description',
-            'instance__long_description'
+            "instance__name",
+            "instance__short_description",
+            "instance__long_description"
           ]
         },
         {
           label: usersLabel,
-          id: 'users',
+          id: "users",
           settings: [
-            'users__registration_enabled',
-            'common__api_authentication_required',
-            'users__default_permissions',
-            'users__upload_quota'
+            "users__registration_enabled",
+            "common__api_authentication_required",
+            "users__default_permissions",
+            "users__upload_quota"
           ]
         },
         {
           label: musicLabel,
-          id: 'music',
+          id: "music",
           settings: [
-            'music__transcoding_enabled',
-            'music__transcoding_cache_duration',
+            "music__transcoding_enabled",
+            "music__transcoding_cache_duration"
           ]
         },
         {
           label: playlistsLabel,
-          id: 'playlists',
-          settings: [
-            'playlists__max_tracks'
-          ]
+          id: "playlists",
+          settings: ["playlists__max_tracks"]
         },
         {
           label: federationLabel,
-          id: 'federation',
+          id: "federation",
           settings: [
-            'federation__enabled',
-            'federation__music_needs_approval',
-            'federation__collection_page_size',
-            'federation__music_cache_duration',
-            'federation__actor_fetch_delay'
+            "federation__enabled",
+            "federation__music_needs_approval",
+            "federation__collection_page_size",
+            "federation__music_cache_duration",
+            "federation__actor_fetch_delay"
           ]
         },
         {
           label: subsonicLabel,
-          id: 'subsonic',
-          settings: [
-            'subsonic__enabled'
-          ]
+          id: "subsonic",
+          settings: ["subsonic__enabled"]
         },
         {
           label: statisticsLabel,
-          id: 'statistics',
+          id: "statistics",
           settings: [
-            'instance__nodeinfo_enabled',
-            'instance__nodeinfo_stats_enabled',
-            'instance__nodeinfo_private'
+            "instance__nodeinfo_enabled",
+            "instance__nodeinfo_stats_enabled",
+            "instance__nodeinfo_private"
           ]
         },
         {
           label: errorLabel,
-          id: 'reporting',
-          settings: [
-            'raven__front_enabled',
-            'raven__front_dsn'
-
-          ]
+          id: "reporting",
+          settings: ["raven__front_enabled", "raven__front_dsn"]
         }
       ]
     }
   },
   watch: {
-    settingsData () {
+    settingsData() {
       let self = this
       this.$nextTick(() => {
-        $(self.$el).find('.sticky').sticky({context: '#settings-grid'})
+        $(self.$el)
+          .find(".sticky")
+          .sticky({ context: "#settings-grid" })
       })
     }
   }
diff --git a/front/src/views/admin/library/Base.vue b/front/src/views/admin/library/Base.vue
index 22f20452f76ed5a2c244a47774a7709d0c65bcc3..45d257606b403769d865560749cf31a0700e0d09 100644
--- a/front/src/views/admin/library/Base.vue
+++ b/front/src/views/admin/library/Base.vue
@@ -1,10 +1,10 @@
 <template>
   <div class="main pusher"  v-title="labels.title">
-    <div class="ui secondary pointing menu">
+    <nav class="ui secondary pointing menu" role="navigation" :aria-label="labels.secondaryMenu">
       <router-link
         class="ui item"
         :to="{name: 'manage.library.files'}"><translate>Files</translate></router-link>
-    </div>
+    </nav>
     <router-view :key="$route.fullPath"></router-view>
   </div>
 </template>
@@ -12,10 +12,12 @@
 <script>
 export default {
   computed: {
-    labels () {
-      let title = this.$gettext('Manage library')
+    labels() {
+      let title = this.$gettext("Manage library")
+      let secondaryMenu = this.$gettext("Secondary menu")
       return {
-        title
+        title,
+        secondaryMenu
       }
     }
   }
@@ -23,10 +25,8 @@ export default {
 </script>
 
 <style scoped>
-
 .ui.menu .item > .label {
   position: absolute;
   right: -2em;
 }
-
 </style>
diff --git a/front/src/views/admin/library/FilesList.vue b/front/src/views/admin/library/FilesList.vue
index 1c5216c8facf0c1c902f7b6bab0b8eb0ad19a16c..3557879bb29acb14a704a6c5113cf243ca0a4edc 100644
--- a/front/src/views/admin/library/FilesList.vue
+++ b/front/src/views/admin/library/FilesList.vue
@@ -1,24 +1,24 @@
 <template>
-  <div v-title="labels.title">
-    <div class="ui vertical stripe segment">
+  <main v-title="labels.title">
+    <section class="ui vertical stripe segment">
       <h2 class="ui header"><translate>Library files</translate></h2>
       <div class="ui hidden divider"></div>
       <library-files-table :show-library="true"></library-files-table>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import LibraryFilesTable from '@/components/manage/library/FilesTable'
+import LibraryFilesTable from "@/components/manage/library/FilesTable"
 
 export default {
   components: {
     LibraryFilesTable
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        title: this.$gettext('Files')
+        title: this.$gettext("Files")
       }
     }
   }
diff --git a/front/src/views/admin/users/Base.vue b/front/src/views/admin/users/Base.vue
index 7486d02d742254cc7d2fcb214435ffdaafd1c44f..41110fb3c39c4ab5459182377f185fb9b6506f30 100644
--- a/front/src/views/admin/users/Base.vue
+++ b/front/src/views/admin/users/Base.vue
@@ -1,13 +1,13 @@
 <template>
   <div class="main pusher"  v-title="labels.manageUsers">
-    <div class="ui secondary pointing menu">
+    <nav class="ui secondary pointing menu" role="navigation" :aria-label="labels.secondaryMenu">
       <router-link
         class="ui item"
         :to="{name: 'manage.users.users.list'}"><translate>Users</translate></router-link>
       <router-link
         class="ui item"
         :to="{name: 'manage.users.invitations.list'}"><translate>Invitations</translate></router-link>
-    </div>
+    </nav>
     <router-view :key="$route.fullPath"></router-view>
   </div>
 </template>
@@ -15,9 +15,10 @@
 <script>
 export default {
   computed: {
-    labels () {
+    labels() {
       return {
-        manageUsers: this.$gettext('Manage users')
+        manageUsers: this.$gettext("Manage users"),
+        secondaryMenu: this.$gettext("Secondary menu")
       }
     }
   }
diff --git a/front/src/views/admin/users/InvitationsList.vue b/front/src/views/admin/users/InvitationsList.vue
index b2dd8f0374aa21bf3eea2eba1c3cf52e86e457ad..04da76f8588af789a63fc7f8deacf2e0ff069bbc 100644
--- a/front/src/views/admin/users/InvitationsList.vue
+++ b/front/src/views/admin/users/InvitationsList.vue
@@ -1,17 +1,17 @@
 <template>
-  <div v-title="labels.invitations">
-    <div class="ui vertical stripe segment">
+  <main v-title="labels.invitations">
+    <section class="ui vertical stripe segment">
       <h2 class="ui header"><translate>Invitations</translate></h2>
       <invitation-form></invitation-form>
       <div class="ui hidden divider"></div>
       <invitations-table></invitations-table>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import InvitationForm from '@/components/manage/users/InvitationForm'
-import InvitationsTable from '@/components/manage/users/InvitationsTable'
+import InvitationForm from "@/components/manage/users/InvitationForm"
+import InvitationsTable from "@/components/manage/users/InvitationsTable"
 
 export default {
   components: {
@@ -19,9 +19,9 @@ export default {
     InvitationsTable
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        invitations: this.$gettext('Invitations')
+        invitations: this.$gettext("Invitations")
       }
     }
   }
diff --git a/front/src/views/admin/users/UsersDetail.vue b/front/src/views/admin/users/UsersDetail.vue
index 21e7b9811fdae29b1be9e05d6060343c1485e979..b347f814a20a419eb2f6224be5f62a1ba45e3709 100644
--- a/front/src/views/admin/users/UsersDetail.vue
+++ b/front/src/views/admin/users/UsersDetail.vue
@@ -1,10 +1,10 @@
 <template>
-  <div>
+  <main>
     <div v-if="isLoading" class="ui vertical segment">
       <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
     </div>
     <template v-if="object">
-      <div :class="['ui', 'head', 'vertical', 'center', 'aligned', 'stripe', 'segment']" v-title="object.username">
+      <section :class="['ui', 'head', 'vertical', 'center', 'aligned', 'stripe', 'segment']" v-title="object.username">
         <div class="segment-content">
           <h2 class="ui center aligned icon header">
             <i class="circular inverted user red icon"></i>
@@ -102,35 +102,34 @@
         </div>
         <div class="ui hidden divider"></div>
         <button @click="fetchData" class="ui basic button"><translate>Refresh</translate></button>
-      </div>
+      </section>
     </template>
-  </div>
+  </main>
 </template>
 
 <script>
-
-import $ from 'jquery'
-import axios from 'axios'
-import logger from '@/logging'
+import $ from "jquery"
+import axios from "axios"
+import logger from "@/logging"
 
 export default {
-  props: ['id'],
-  data () {
+  props: ["id"],
+  data() {
     return {
       isLoading: true,
       object: null,
       permissions: []
     }
   },
-  created () {
+  created() {
     this.fetchData()
   },
   methods: {
-    fetchData () {
+    fetchData() {
       var self = this
       this.isLoading = true
-      let url = 'manage/users/users/' + this.id + '/'
-      axios.get(url).then((response) => {
+      let url = "manage/users/users/" + this.id + "/"
+      axios.get(url).then(response => {
         self.object = response.data
         self.permissions = []
         self.allPermissions.forEach(p => {
@@ -141,60 +140,72 @@ export default {
         self.isLoading = false
       })
     },
-    update (attr, toNull) {
+    update(attr, toNull) {
       let newValue = this.object[attr]
       if (toNull && !newValue) {
         newValue = null
       }
-      console.log(newValue, typeof(newValue))
+      console.log(newValue, typeof newValue)
       let params = {}
-      if (attr === 'permissions') {
-        params['permissions'] = {}
+      if (attr === "permissions") {
+        params["permissions"] = {}
         this.allPermissions.forEach(p => {
-          params['permissions'][p.code] = this.permissions.indexOf(p.code) > -1
+          params["permissions"][p.code] = this.permissions.indexOf(p.code) > -1
         })
       } else {
         params[attr] = newValue
       }
-      axios.patch('manage/users/users/' + this.id + '/', params).then((response) => {
-        logger.default.info(`${attr} was updated succcessfully to ${newValue}`)
-      }, (error) => {
-        logger.default.error(`Error while setting ${attr} to ${newValue}`, error)
-      })
+      axios.patch("manage/users/users/" + this.id + "/", params).then(
+        response => {
+          logger.default.info(
+            `${attr} was updated succcessfully to ${newValue}`
+          )
+        },
+        error => {
+          logger.default.error(
+            `Error while setting ${attr} to ${newValue}`,
+            error
+          )
+        }
+      )
     }
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        inactive: this.$gettext('Determine if the user account is active or not. Inactive users cannot login or use the service.'),
-        uploadQuota: this.$gettext('Determine how much content the user can upload. Leave empty to use the default value of the instance.')
+        inactive: this.$gettext(
+          "Determine if the user account is active or not. Inactive users cannot login or use the service."
+        ),
+        uploadQuota: this.$gettext(
+          "Determine how much content the user can upload. Leave empty to use the default value of the instance."
+        )
       }
     },
-    allPermissions () {
+    allPermissions() {
       return [
         {
-          'code': 'upload',
-          'label': this.$gettext('Upload')
+          code: "upload",
+          label: this.$gettext("Upload")
         },
         {
-          'code': 'library',
-          'label': this.$gettext('Library')
+          code: "library",
+          label: this.$gettext("Library")
         },
         {
-          'code': 'federation',
-          'label': this.$gettext('Federation')
+          code: "federation",
+          label: this.$gettext("Federation")
         },
         {
-          'code': 'settings',
-          'label': this.$gettext('Settings')
+          code: "settings",
+          label: this.$gettext("Settings")
         }
       ]
     }
   },
   watch: {
-    object () {
+    object() {
       this.$nextTick(() => {
-        $('select.dropdown').dropdown()
+        $("select.dropdown").dropdown()
       })
     }
   }
diff --git a/front/src/views/admin/users/UsersList.vue b/front/src/views/admin/users/UsersList.vue
index ef4d60961e89ba2566d484bdd42ae800796beded..c0ee4166cd1fd976738a1315b4bce9aa35e512c1 100644
--- a/front/src/views/admin/users/UsersList.vue
+++ b/front/src/views/admin/users/UsersList.vue
@@ -1,24 +1,24 @@
 <template>
-  <div v-title="labels.users">
-    <div class="ui vertical stripe segment">
+  <main v-title="labels.users">
+    <section class="ui vertical stripe segment">
       <h2 class="ui header"><translate>Users</translate></h2>
       <div class="ui hidden divider"></div>
       <users-table></users-table>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import UsersTable from '@/components/manage/users/UsersTable'
+import UsersTable from "@/components/manage/users/UsersTable"
 
 export default {
   components: {
     UsersTable
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        users: this.$gettext('Users')
+        users: this.$gettext("Users")
       }
     }
   }
diff --git a/front/src/views/auth/EmailConfirm.vue b/front/src/views/auth/EmailConfirm.vue
index 7b982504506781119fc27efbffd53ff9c61b3854..c2d83326e1b1e1086208d77a22b125df9db890d3 100644
--- a/front/src/views/auth/EmailConfirm.vue
+++ b/front/src/views/auth/EmailConfirm.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="main pusher" v-title="labels.confirm">
-    <div class="ui vertical stripe segment">
+  <main class="main pusher" v-title="labels.confirm">
+    <section class="ui vertical stripe segment">
       <div class="ui small text container">
         <h2><translate>Confirm your email</translate></h2>
         <form v-if="!success" class="ui form" @submit.prevent="submit()">
@@ -28,16 +28,16 @@
           </router-link>
         </div>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import axios from 'axios'
+import axios from "axios"
 
 export default {
-  props: ['defaultKey'],
-  data () {
+  props: ["defaultKey"],
+  data() {
     return {
       isLoading: false,
       errors: [],
@@ -46,30 +46,32 @@ export default {
     }
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        confirm: this.$gettext('Confirm your email')
+        confirm: this.$gettext("Confirm your email")
       }
     }
   },
   methods: {
-    submit () {
+    submit() {
       let self = this
       self.isLoading = true
       self.errors = []
       let payload = {
         key: this.key
       }
-      return axios.post('auth/registration/verify-email/', payload).then(response => {
-        self.isLoading = false
-        self.success = true
-      }, error => {
-        self.errors = error.backendErrors
-        self.isLoading = false
-      })
+      return axios.post("auth/registration/verify-email/", payload).then(
+        response => {
+          self.isLoading = false
+          self.success = true
+        },
+        error => {
+          self.errors = error.backendErrors
+          self.isLoading = false
+        }
+      )
     }
   }
-
 }
 </script>
 
diff --git a/front/src/views/auth/PasswordReset.vue b/front/src/views/auth/PasswordReset.vue
index 52787a516ff52e22813f83579973bf6de8170387..e7759113c33b80676983ce64d51acfbaf519855e 100644
--- a/front/src/views/auth/PasswordReset.vue
+++ b/front/src/views/auth/PasswordReset.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="main pusher" v-title="labels.reset">
-    <div class="ui vertical stripe segment">
+  <main class="main pusher" v-title="labels.reset">
+    <section class="ui vertical stripe segment">
       <div class="ui small text container">
         <h2><translate>Reset your password</translate></h2>
         <form class="ui form" @submit.prevent="submit()">
@@ -28,29 +28,31 @@
             <translate>Ask for a password reset</translate></button>
         </form>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import axios from 'axios'
+import axios from "axios"
 
 export default {
-  props: ['defaultEmail'],
-  data () {
+  props: ["defaultEmail"],
+  data() {
     return {
       email: this.defaultEmail,
       isLoading: false,
       errors: []
     }
   },
-  mounted () {
+  mounted() {
     this.$refs.email.focus()
   },
   computed: {
-    labels () {
-      let reset = this.$gettext('Reset your password')
-      let placeholder = this.$gettext('Input the email address binded to your account')
+    labels() {
+      let reset = this.$gettext("Reset your password")
+      let placeholder = this.$gettext(
+        "Input the email address binded to your account"
+      )
       return {
         reset,
         placeholder
@@ -58,25 +60,27 @@ export default {
     }
   },
   methods: {
-    submit () {
+    submit() {
       let self = this
       self.isLoading = true
       self.errors = []
       let payload = {
         email: this.email
       }
-      return axios.post('auth/password/reset/', payload).then(response => {
-        self.isLoading = false
-        self.$router.push({
-          name: 'auth.password-reset-confirm'
-        })
-      }, error => {
-        self.errors = error.backendErrors
-        self.isLoading = false
-      })
+      return axios.post("auth/password/reset/", payload).then(
+        response => {
+          self.isLoading = false
+          self.$router.push({
+            name: "auth.password-reset-confirm"
+          })
+        },
+        error => {
+          self.errors = error.backendErrors
+          self.isLoading = false
+        }
+      )
     }
   }
-
 }
 </script>
 
diff --git a/front/src/views/auth/PasswordResetConfirm.vue b/front/src/views/auth/PasswordResetConfirm.vue
index b6e4f23224242d160b0c2121b176ed5fb07a8287..df0589beb8d13d909bcefaefe4268fef91bacbaf 100644
--- a/front/src/views/auth/PasswordResetConfirm.vue
+++ b/front/src/views/auth/PasswordResetConfirm.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="main pusher" v-title="labels.changePassword">
-    <div class="ui vertical stripe segment">
+  <main class="main pusher" v-title="labels.changePassword">
+    <section class="ui vertical stripe segment">
       <div class="ui small text container">
         <h2><translate>Change your password</translate></h2>
         <form v-if="!success" class="ui form" @submit.prevent="submit()">
@@ -33,22 +33,22 @@
           </router-link>
         </div>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import axios from 'axios'
-import PasswordInput from '@/components/forms/PasswordInput'
+import axios from "axios"
+import PasswordInput from "@/components/forms/PasswordInput"
 
 export default {
-  props: ['defaultToken', 'defaultUid'],
+  props: ["defaultToken", "defaultUid"],
   components: {
     PasswordInput
   },
-  data () {
+  data() {
     return {
-      newPassword: '',
+      newPassword: "",
       isLoading: false,
       errors: [],
       token: this.defaultToken,
@@ -57,14 +57,14 @@ export default {
     }
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        changePassword: this.$gettext('Change your password')
+        changePassword: this.$gettext("Change your password")
       }
     }
   },
   methods: {
-    submit () {
+    submit() {
       let self = this
       self.isLoading = true
       self.errors = []
@@ -74,16 +74,18 @@ export default {
         new_password1: this.newPassword,
         new_password2: this.newPassword
       }
-      return axios.post('auth/password/reset/confirm/', payload).then(response => {
-        self.isLoading = false
-        self.success = true
-      }, error => {
-        self.errors = error.backendErrors
-        self.isLoading = false
-      })
+      return axios.post("auth/password/reset/confirm/", payload).then(
+        response => {
+          self.isLoading = false
+          self.success = true
+        },
+        error => {
+          self.errors = error.backendErrors
+          self.isLoading = false
+        }
+      )
     }
   }
-
 }
 </script>
 
diff --git a/front/src/views/content/Base.vue b/front/src/views/content/Base.vue
index cfdd204281bb2086700e1abf744fda1121a8ad51..039e21adcd311edd99badb5443ac5f8a16f0f478 100644
--- a/front/src/views/content/Base.vue
+++ b/front/src/views/content/Base.vue
@@ -1,24 +1,25 @@
 <template>
-  <div class="main pusher"  v-title="labels.title">
-    <div class="ui secondary pointing menu">
+  <main class="main pusher"  v-title="labels.title">
+    <nav class="ui secondary pointing menu" role="navigation" :aria-label="labels.secondaryMenu">
       <router-link
         class="ui item"
         :to="{name: 'content.libraries.index'}"><translate>Libraries</translate></router-link>
       <router-link
         class="ui item"
         :to="{name: 'content.libraries.files'}"><translate>Tracks</translate></router-link>
-    </div>
+    </nav>
     <router-view :key="$route.fullPath"></router-view>
-  </div>
+  </main>
 </template>
 <script>
-
 export default {
   computed: {
-    labels () {
-      let title = this.$gettext('Add content')
+    labels() {
+      let title = this.$gettext("Add content")
+      let secondaryMenu = this.$gettext("Secondary menu")
       return {
-        title
+        title,
+        secondaryMenu
       }
     }
   }
diff --git a/front/src/views/content/Home.vue b/front/src/views/content/Home.vue
index 91fe11824d694ff613c99065213e8b4916e975a0..d75a4a89f139605cd3bbc4e2cb8adaac47c9b0bd 100644
--- a/front/src/views/content/Home.vue
+++ b/front/src/views/content/Home.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="ui vertical aligned stripe segment" v-title="labels.title">
+  <section class="ui vertical aligned stripe segment" v-title="labels.title">
     <div class="ui text container">
       <h1>{{ labels.title }}</h1>
       <p><translate>We offer various way to grab new content and make it available here.</translate></p>
@@ -22,21 +22,24 @@
       </div>
 
     </div>
-  </div>
+  </section>
 </template>
 
 <script>
-import {humanSize} from '@/filters'
+import { humanSize } from "@/filters"
 
 export default {
   computed: {
-    labels () {
+    labels() {
       return {
-        title: this.$gettext('Add and manage content')
+        title: this.$gettext("Add and manage content")
       }
     },
-    defaultQuota () {
-      let quota = this.$store.state.instance.settings.users.upload_quota.value * 1000 * 1000
+    defaultQuota() {
+      let quota =
+        this.$store.state.instance.settings.users.upload_quota.value *
+        1000 *
+        1000
       return humanSize(quota)
     }
   }
diff --git a/front/src/views/content/libraries/Detail.vue b/front/src/views/content/libraries/Detail.vue
index eed851e5e551b48ec9b4cc09485e7da407b6f47b..324a16c7f642789ae10fbe0393262b4ecae1c58b 100644
--- a/front/src/views/content/libraries/Detail.vue
+++ b/front/src/views/content/libraries/Detail.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="ui vertical aligned stripe segment">
+  <section class="ui vertical aligned stripe segment">
     <div v-if="isLoadingLibrary" :class="['ui', {'active': isLoadingLibrary}, 'inverted', 'dimmer']">
       <div class="ui text loader"><translate>Loading library data...</translate></div>
     </div>
@@ -64,15 +64,15 @@
         <library-form :library="library" @updated="libraryUpdated" @deleted="libraryDeleted" />
       </div>
     </detail-area>
-  </div>
+  </section>
 </template>
 
 <script>
-import axios from 'axios'
-import DetailMixin from './DetailMixin'
-import DetailArea from './DetailArea'
-import LibraryForm from './Form'
-import LibraryFilesTable from './FilesTable'
+import axios from "axios"
+import DetailMixin from "./DetailMixin"
+import DetailArea from "./DetailArea"
+import LibraryForm from "./Form"
+import LibraryFilesTable from "./FilesTable"
 
 export default {
   mixins: [DetailMixin],
@@ -81,46 +81,48 @@ export default {
     LibraryForm,
     LibraryFilesTable
   },
-  data () {
+  data() {
     return {
-      currentTab: 'follows',
+      currentTab: "follows",
       isLoadingFollows: false,
       follows: null
     }
   },
-  created () {
+  created() {
     this.fetchFollows()
   },
   methods: {
-    libraryUpdated () {
+    libraryUpdated() {
       this.hiddenForm = true
       this.fetch()
     },
-    libraryDeleted () {
+    libraryDeleted() {
       this.$router.push({
-        name: 'content.libraries.index'
+        name: "content.libraries.index"
       })
     },
-    fetchFollows () {
+    fetchFollows() {
       let self = this
       self.isLoadingLibrary = true
-      axios.get(`libraries/${this.id}/follows/`).then((response) => {
+      axios.get(`libraries/${this.id}/follows/`).then(response => {
         self.follows = response.data
         self.isLoadingFollows = false
       })
     },
-    updateApproved (follow, value) {
+    updateApproved(follow, value) {
       let self = this
       let action
       if (value) {
-        action = 'accept'
+        action = "accept"
       } else {
-        action = 'reject'
+        action = "reject"
       }
-      axios.post(`federation/follows/library/${follow.uuid}/${action}/`).then((response) => {
-        follow.isLoading = false
-        follow.approved = value
-      })
+      axios
+        .post(`federation/follows/library/${follow.uuid}/${action}/`)
+        .then(response => {
+          follow.isLoading = false
+          follow.approved = value
+        })
     }
   }
 }
diff --git a/front/src/views/content/libraries/Files.vue b/front/src/views/content/libraries/Files.vue
index 752dcd77699056c6eac175d2faf571f37cb1a3e1..0184df3864d0080900e1fa8aeb7f7243acf2c70a 100644
--- a/front/src/views/content/libraries/Files.vue
+++ b/front/src/views/content/libraries/Files.vue
@@ -1,14 +1,14 @@
 <template>
-  <div class="ui vertical aligned stripe segment">
+  <section class="ui vertical aligned stripe segment">
     <library-files-table :default-query="query"></library-files-table>
-  </div>
+  </section>
 </template>
 
 <script>
-import LibraryFilesTable from './FilesTable'
+import LibraryFilesTable from "./FilesTable"
 
 export default {
-  props: ['query'],
+  props: ["query"],
   components: {
     LibraryFilesTable
   }
diff --git a/front/src/views/content/libraries/Home.vue b/front/src/views/content/libraries/Home.vue
index 98dc2dde3e6fd6fc820427491d86533d1155c372..4162cd72c4d07922b06dc75d11fdbe0b1bd49032 100644
--- a/front/src/views/content/libraries/Home.vue
+++ b/front/src/views/content/libraries/Home.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="ui vertical aligned stripe segment">
+  <section class="ui vertical aligned stripe segment">
     <div v-if="isLoading" :class="['ui', {'active': isLoading}, 'inverted', 'dimmer']">
       <div class="ui text loader"><translate>Loading Libraries...</translate></div>
     </div>
@@ -24,24 +24,24 @@
         </div>
       </div>
     </div>
-  </div>
+  </section>
 </template>
 
 <script>
-import axios from 'axios'
-import LibraryForm from './Form'
-import LibraryCard from './Card'
-import Quota from './Quota'
+import axios from "axios"
+import LibraryForm from "./Form"
+import LibraryCard from "./Card"
+import Quota from "./Quota"
 
 export default {
-  data () {
+  data() {
     return {
       isLoading: false,
       hiddenForm: true,
       libraries: []
     }
   },
-  created () {
+  created() {
     this.fetch()
   },
   components: {
@@ -50,10 +50,10 @@ export default {
     Quota
   },
   methods: {
-    fetch () {
+    fetch() {
       this.isLoading = true
       let self = this
-      axios.get('libraries/').then((response) => {
+      axios.get("libraries/").then(response => {
         self.isLoading = false
         self.libraries = response.data.results
         if (self.libraries.length === 0) {
@@ -61,7 +61,7 @@ export default {
         }
       })
     },
-    libraryCreated (library) {
+    libraryCreated(library) {
       this.hiddenForm = true
       this.libraries.unshift(library)
     }
diff --git a/front/src/views/playlists/Detail.vue b/front/src/views/playlists/Detail.vue
index c1a08a7d1c51e5995c495e08cd48d495fbab8a50..9a548106d2a5035fcc8bfb4a5d869a5a324dbe2d 100644
--- a/front/src/views/playlists/Detail.vue
+++ b/front/src/views/playlists/Detail.vue
@@ -1,9 +1,9 @@
 <template>
-  <div>
+  <main>
     <div v-if="isLoading" class="ui vertical segment" v-title="labels.playlist">
       <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
     </div>
-    <div v-if="!isLoading && playlist" class="ui head vertical center aligned stripe segment" v-title="playlist.name">
+    <section v-if="!isLoading && playlist" class="ui head vertical center aligned stripe segment" v-title="playlist.name">
       <div class="segment-content">
         <h2 class="ui center aligned icon header">
           <i class="circular inverted list yellow icon"></i>
@@ -39,8 +39,8 @@
           <p slot="modal-confirm"><translate>Delete playlist</translate></p>
         </dangerous-button>
       </div>
-    </div>
-    <div class="ui vertical stripe segment">
+    </section>
+    <section class="ui vertical stripe segment">
       <template v-if="edit">
         <playlist-editor
           @playlist-updated="playlist = $event"
@@ -51,20 +51,20 @@
         <h2><translate>Tracks</translate></h2>
         <track-table :display-position="true" :tracks="tracks"></track-table>
       </template>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 <script>
-import axios from 'axios'
-import TrackTable from '@/components/audio/track/Table'
-import RadioButton from '@/components/radios/Button'
-import PlayButton from '@/components/audio/PlayButton'
-import PlaylistEditor from '@/components/playlists/Editor'
+import axios from "axios"
+import TrackTable from "@/components/audio/track/Table"
+import RadioButton from "@/components/radios/Button"
+import PlayButton from "@/components/audio/PlayButton"
+import PlaylistEditor from "@/components/playlists/Editor"
 
 export default {
   props: {
-    id: {required: true},
-    defaultEdit: {type: Boolean, default: false}
+    id: { required: true },
+    defaultEdit: { type: Boolean, default: false }
   },
   components: {
     PlaylistEditor,
@@ -72,7 +72,7 @@ export default {
     PlayButton,
     RadioButton
   },
-  data: function () {
+  data: function() {
     return {
       edit: this.defaultEdit,
       isLoading: false,
@@ -81,18 +81,18 @@ export default {
       playlistTracks: []
     }
   },
-  created: function () {
+  created: function() {
     this.fetch()
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        playlist: this.$gettext('Playlist')
+        playlist: this.$gettext("Playlist")
       }
     }
   },
   methods: {
-    updatePlts (v) {
+    updatePlts(v) {
       this.playlistTracks = v
       this.tracks = v.map((e, i) => {
         let track = e.track
@@ -100,26 +100,29 @@ export default {
         return track
       })
     },
-    fetch: function () {
+    fetch: function() {
       let self = this
       self.isLoading = true
-      let url = 'playlists/' + this.id + '/'
-      axios.get(url).then((response) => {
+      let url = "playlists/" + this.id + "/"
+      axios.get(url).then(response => {
         self.playlist = response.data
-        axios.get(url + 'tracks/').then((response) => {
-          self.updatePlts(response.data.results)
-        }).then(() => {
-          self.isLoading = false
-        })
+        axios
+          .get(url + "tracks/")
+          .then(response => {
+            self.updatePlts(response.data.results)
+          })
+          .then(() => {
+            self.isLoading = false
+          })
       })
     },
-    deletePlaylist () {
+    deletePlaylist() {
       let self = this
-      let url = 'playlists/' + this.id + '/'
-      axios.delete(url).then((response) => {
-        self.$store.dispatch('playlists/fetchOwn')
+      let url = "playlists/" + this.id + "/"
+      axios.delete(url).then(response => {
+        self.$store.dispatch("playlists/fetchOwn")
         self.$router.push({
-          path: '/library'
+          path: "/library"
         })
       })
     }
diff --git a/front/src/views/playlists/List.vue b/front/src/views/playlists/List.vue
index 47035da7fc6df7ce15fe993559071d9dbf8b2069..de9ca78ac74d19ec05d4b3b0446eae06bcd36973 100644
--- a/front/src/views/playlists/List.vue
+++ b/front/src/views/playlists/List.vue
@@ -1,6 +1,6 @@
 <template>
-  <div v-title="labels.playlists">
-    <div class="ui vertical stripe segment">
+  <main v-title="labels.playlists">
+    <section class="ui vertical stripe segment">
       <h2 class="ui header"><translate>Browsing playlists</translate></h2>
       <div :class="['ui', {'loading': isLoading}, 'form']">
         <template v-if="$store.state.auth.authenticated">
@@ -50,59 +50,61 @@
           :total="result.count"
           ></pagination>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import axios from 'axios'
-import _ from 'lodash'
-import $ from 'jquery'
+import axios from "axios"
+import _ from "lodash"
+import $ from "jquery"
 
-import OrderingMixin from '@/components/mixins/Ordering'
-import PaginationMixin from '@/components/mixins/Pagination'
-import TranslationsMixin from '@/components/mixins/Translations'
-import PlaylistCardList from '@/components/playlists/CardList'
-import Pagination from '@/components/Pagination'
+import OrderingMixin from "@/components/mixins/Ordering"
+import PaginationMixin from "@/components/mixins/Pagination"
+import TranslationsMixin from "@/components/mixins/Translations"
+import PlaylistCardList from "@/components/playlists/CardList"
+import Pagination from "@/components/Pagination"
 
-const FETCH_URL = 'playlists/'
+const FETCH_URL = "playlists/"
 
 export default {
   mixins: [OrderingMixin, PaginationMixin, TranslationsMixin],
   props: {
-    defaultQuery: {type: String, required: false, default: ''}
+    defaultQuery: { type: String, required: false, default: "" }
   },
   components: {
     PlaylistCardList,
     Pagination
   },
-  data () {
-    let defaultOrdering = this.getOrderingFromString(this.defaultOrdering || '-creation_date')
+  data() {
+    let defaultOrdering = this.getOrderingFromString(
+      this.defaultOrdering || "-creation_date"
+    )
     return {
       isLoading: true,
       result: null,
       page: parseInt(this.defaultPage),
       query: this.defaultQuery,
       paginateBy: parseInt(this.defaultPaginateBy || 12),
-      orderingDirection: defaultOrdering.direction || '+',
+      orderingDirection: defaultOrdering.direction || "+",
       ordering: defaultOrdering.field,
       orderingOptions: [
-        ['creation_date', 'creation_date'],
-        ['modification_date', 'modification_date'],
-        ['name', 'name']
+        ["creation_date", "creation_date"],
+        ["modification_date", "modification_date"],
+        ["name", "name"]
       ]
     }
   },
-  created () {
+  created() {
     this.fetchData()
   },
-  mounted () {
-    $('.ui.dropdown').dropdown()
+  mounted() {
+    $(".ui.dropdown").dropdown()
   },
   computed: {
-    labels () {
-      let playlists = this.$gettext('Playlists')
-      let searchPlaceholder = this.$gettext('Enter an playlist name...')
+    labels() {
+      let playlists = this.$gettext("Playlists")
+      let searchPlaceholder = this.$gettext("Enter an playlist name...")
       return {
         playlists,
         searchPlaceholder
@@ -110,7 +112,7 @@ export default {
     }
   },
   methods: {
-    updateQueryString: _.debounce(function () {
+    updateQueryString: _.debounce(function() {
       this.$router.replace({
         query: {
           query: this.query,
@@ -120,7 +122,7 @@ export default {
         }
       })
     }, 250),
-    fetchData: _.debounce(function () {
+    fetchData: _.debounce(function() {
       var self = this
       this.isLoading = true
       let url = FETCH_URL
@@ -130,33 +132,33 @@ export default {
         q: this.query,
         ordering: this.getOrderingAsString()
       }
-      axios.get(url, {params: params}).then((response) => {
+      axios.get(url, { params: params }).then(response => {
         self.result = response.data
         self.isLoading = false
       })
     }, 500),
-    selectPage: function (page) {
+    selectPage: function(page) {
       this.page = page
     }
   },
   watch: {
-    page () {
+    page() {
       this.updateQueryString()
       this.fetchData()
     },
-    paginateBy () {
+    paginateBy() {
       this.updateQueryString()
       this.fetchData()
     },
-    ordering () {
+    ordering() {
       this.updateQueryString()
       this.fetchData()
     },
-    orderingDirection () {
+    orderingDirection() {
       this.updateQueryString()
       this.fetchData()
     },
-    query () {
+    query() {
       this.updateQueryString()
       this.fetchData()
     }
diff --git a/front/src/views/radios/Detail.vue b/front/src/views/radios/Detail.vue
index e269bf284e9c08fbd52e00061070d4f3fac02bb3..0c46385a6bab53f50f96d65acd491ac6c287e4c3 100644
--- a/front/src/views/radios/Detail.vue
+++ b/front/src/views/radios/Detail.vue
@@ -1,9 +1,9 @@
 <template>
-  <div>
+  <main>
     <div v-if="isLoading" class="ui vertical segment" v-title="labels.title">
       <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
     </div>
-    <div v-if="!isLoading && radio" class="ui head vertical center aligned stripe segment" v-title="radio.name">
+    <section v-if="!isLoading && radio" class="ui head vertical center aligned stripe segment" v-title="radio.name">
       <div class="segment-content">
         <h2 class="ui center aligned icon header">
           <i class="circular inverted feed blue icon"></i>
@@ -30,8 +30,8 @@
           </dangerous-button>
         </template>
       </div>
-    </div>
-    <div class="ui vertical stripe segment">
+    </section>
+    <section class="ui vertical stripe segment">
       <h2><translate>Tracks</translate></h2>
       <track-table :tracks="tracks"></track-table>
       <div class="ui center aligned basic segment">
@@ -43,26 +43,26 @@
           :total="totalTracks"
           ></pagination>
       </div>
-    </div>
-  </div>
+    </section>
+  </main>
 </template>
 
 <script>
-import axios from 'axios'
-import TrackTable from '@/components/audio/track/Table'
-import RadioButton from '@/components/radios/Button'
-import Pagination from '@/components/Pagination'
+import axios from "axios"
+import TrackTable from "@/components/audio/track/Table"
+import RadioButton from "@/components/radios/Button"
+import Pagination from "@/components/Pagination"
 
 export default {
   props: {
-    id: {required: true}
+    id: { required: true }
   },
   components: {
     TrackTable,
     RadioButton,
     Pagination
   },
-  data: function () {
+  data: function() {
     return {
       isLoading: false,
       radio: null,
@@ -71,46 +71,49 @@ export default {
       page: 1
     }
   },
-  created: function () {
+  created: function() {
     this.fetch()
   },
   computed: {
-    labels () {
+    labels() {
       return {
-        title: this.$gettext('Radio')
+        title: this.$gettext("Radio")
       }
     }
   },
   methods: {
-    selectPage: function (page) {
+    selectPage: function(page) {
       this.page = page
     },
-    fetch: function () {
+    fetch: function() {
       let self = this
       self.isLoading = true
-      let url = 'radios/radios/' + this.id + '/'
-      axios.get(url).then((response) => {
+      let url = "radios/radios/" + this.id + "/"
+      axios.get(url).then(response => {
         self.radio = response.data
-        axios.get(url + 'tracks/', {params: {page: this.page}}).then((response) => {
-          this.totalTracks = response.data.count
-          this.tracks = response.data.results
-        }).then(() => {
-          self.isLoading = false
-        })
+        axios
+          .get(url + "tracks/", { params: { page: this.page } })
+          .then(response => {
+            this.totalTracks = response.data.count
+            this.tracks = response.data.results
+          })
+          .then(() => {
+            self.isLoading = false
+          })
       })
     },
-    deleteRadio () {
+    deleteRadio() {
       let self = this
-      let url = 'radios/radios/' + this.id + '/'
-      axios.delete(url).then((response) => {
+      let url = "radios/radios/" + this.id + "/"
+      axios.delete(url).then(response => {
         self.$router.push({
-          path: '/library'
+          path: "/library"
         })
       })
     }
   },
   watch: {
-    page: function () {
+    page: function() {
       this.fetch()
     }
   }