diff --git a/changes/changelog.d/756.feature b/changes/changelog.d/756.feature
new file mode 100644
index 0000000000000000000000000000000000000000..402b0e589ffb2c255d6ab392f822afab77b84b21
--- /dev/null
+++ b/changes/changelog.d/756.feature
@@ -0,0 +1 @@
+Dark theme (#756)
diff --git a/front/public/index.html b/front/public/index.html
index 7b09feaf028a141997212222c7967639038a1bd1..142419ca63f3295dadbf8773931f2cbe7e37e1cc 100644
--- a/front/public/index.html
+++ b/front/public/index.html
@@ -9,7 +9,7 @@
   <title>Funkwhale</title>
 </head>
 
-<body>
+<body class="theme-light" id="body">
   <noscript>
     <strong>We're sorry but Funkwhale doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
   </noscript>
diff --git a/front/src/App.vue b/front/src/App.vue
index 5711466c5bb9cd29eaec058174a9fe7a4c5376b9..e401d475e05955a42eb24c0625325306b1d9e956 100644
--- a/front/src/App.vue
+++ b/front/src/App.vue
@@ -251,6 +251,14 @@ export default {
       this.$store.dispatch('instance/fetchSettings')
       this.fetchNodeInfo()
     },
+    '$store.state.ui.theme': {
+      immediate: true,
+      handler (newValue, oldValue) {
+        let oldTheme = oldValue || 'light'
+        document.body.classList.remove(`theme-${oldTheme}`)
+        document.body.classList.add(`theme-${newValue}`)
+      },
+    },
     '$store.state.auth.authenticated' (newValue) {
       if (!newValue) {
         this.disconnect()
diff --git a/front/src/components/Footer.vue b/front/src/components/Footer.vue
index 2d06b50c768e368c538b15bbcf6b33a00c6b42ab..d758b456bf9edaea8b6af87efe6e349a7e640778 100644
--- a/front/src/components/Footer.vue
+++ b/front/src/components/Footer.vue
@@ -33,6 +33,14 @@
             <a href="https://funkwhale.audio/apps" class="item" target="_blank"><translate translate-context="Footer/*/List item.Link">Mobile and desktop apps</translate></a>
             <div role="button" class="item" @click="$emit('show:shortcuts-modal')"><translate translate-context="*/*/*/Noun">Keyboard shortcuts</translate></div>
           </div>
+          <div class="ui form">
+            <div class="ui field">
+              <label><translate translate-context="Footer/Settings/Dropdown.Label/Short, Verb">Change theme</translate></label>
+              <select class="ui dropdown" :value="$store.state.ui.theme" @change="$store.commit('ui/theme', $event.target.value)">
+                <option v-for="theme in themes" :key="theme.key" :value="theme.key">{{ theme.name }}</option>
+              </select>
+            </div>
+          </div>
         </section>
         <section class="four wide column">
           <h4 v-translate translate-context="Footer/*/Link" class="ui header">Getting help</h4>
@@ -76,15 +84,18 @@ export default {
       parser.href = url
       return parser.hostname
     },
+    themes () {
+      return [
+        {
+          name: this.$pgettext('Footer/Settings/Dropdown.Label/Theme name', 'Light'),
+          key: 'light'
+        },
+        {
+          name: this.$pgettext('Footer/Settings/Dropdown.Label/Theme name', 'Dark'),
+          key: 'dark'
+        }
+      ]
+    }
   }
 }
 </script>
-<style scoped>
-footer p {
-  color: grey;
-}
-
-footer#footer div.item:hover {
-  color: rgba(0, 0, 0, 0.87);
-}
-</style>
diff --git a/front/src/components/notifications/NotificationRow.vue b/front/src/components/notifications/NotificationRow.vue
index b8e145647d03340fb62b8edc981f25179f341a9a..95f8e36440f486d1e7916b813a2301639e3efbd8 100644
--- a/front/src/components/notifications/NotificationRow.vue
+++ b/front/src/components/notifications/NotificationRow.vue
@@ -126,7 +126,4 @@ export default {
 .read > span {
   cursor: pointer;
 }
-.disabled-row {
-  color: rgba(40, 40, 40, 0.3);
-}
 </style>
diff --git a/front/src/components/playlists/Card.vue b/front/src/components/playlists/Card.vue
index 60a322ab4c86486842d79f176b6b536b6714041e..6efc2350e00b990d0410c0e8c644340a5b7c3f4f 100644
--- a/front/src/components/playlists/Card.vue
+++ b/front/src/components/playlists/Card.vue
@@ -59,12 +59,10 @@ export default {
 
 <!-- Add "scoped" attribute to limit CSS to this component only -->
 <style>
-
 .playlist.card .header .ellipsis.vertical.large.grey {
   font-size: 1.2em;
   margin-right: 0;
 }
-
 </style>
 <style scoped>
 .card .header {
@@ -72,8 +70,7 @@ export default {
 }
 
 .attached.button {
-  background-color: rgb(243, 244, 245);
-  background-size: 25% ;
+  background-size: 25%;
   background-repeat: no-repeat;
   background-origin: border-box;
   background-position: 0 0, 33.33% 0, 66.67% 0, 100% 0;
@@ -82,5 +79,4 @@ export default {
   font-size: 4em;
   box-shadow: 0px 0px 0px 1px rgba(34, 36, 38, 0.15) inset !important;
 }
-
 </style>
diff --git a/front/src/store/index.js b/front/src/store/index.js
index 791dbb1e92fd1d3add3509ea67575dc734c64dd8..126368e23eaa6759b05bcfde1b9c71bbc5207e5e 100644
--- a/front/src/store/index.js
+++ b/front/src/store/index.js
@@ -40,7 +40,7 @@ export default new Vuex.Store({
     }),
     createPersistedState({
       key: 'ui',
-      paths: ['ui.currentLanguage', 'ui.momentLocale']
+      paths: ['ui.currentLanguage', 'ui.momentLocale', 'ui.theme']
     }),
     createPersistedState({
       key: 'radios',
diff --git a/front/src/store/ui.js b/front/src/store/ui.js
index 8a8bc1da01854a6b630c5a3877bca91217feb524..d13a92d8820776bbee8c052e90f36c7c52cb1a56 100644
--- a/front/src/store/ui.js
+++ b/front/src/store/ui.js
@@ -10,6 +10,7 @@ export default {
     maxMessages: 100,
     messageDisplayDuration: 10000,
     messages: [],
+    theme: 'light',
     notifications: {
       inbox: 0,
       pendingReviewEdits: 0,
@@ -39,6 +40,9 @@ export default {
     computeLastDate: (state) => {
       state.lastDate = new Date()
     },
+    theme: (state, value) => {
+      state.theme = value
+    },
     addMessage (state, message) {
       state.messages.push(message)
       if (state.messages.length > state.maxMessages) {
diff --git a/front/src/style/_main.scss b/front/src/style/_main.scss
index 8f8ee8b16f514dac63480f0375774166e17db18f..bc527415c7658b82a418f0f678398f01f8627d3e 100644
--- a/front/src/style/_main.scss
+++ b/front/src/style/_main.scss
@@ -10,6 +10,10 @@
   Import this file into your LESS project to use Semantic UI without build tools
 */
 
+// Those semantic-ui-css/*.scss don't exist in the package, but we create them
+// via scripts/link-scss-files.sh on postinstall, so we can include theme
+// under a class namespace
+
 /* Global */
 @import "~semantic-ui-css/components/reset.css";
 // we use our custom site css here to avoid loading google font
@@ -96,6 +100,7 @@ body {
 #app > main, #app > .main {
   flex: 1;
 }
+
 .instance-chooser {
   margin-top: 2em;
 }
@@ -126,14 +131,10 @@ body {
   margin-left: 0;
   margin-right: 0;
   border: none;
-  box-shadow: inset 0px -2px 0px 0px rgba(34, 36, 38, 0.15);
   .ui.item {
     border: none;
     border-bottom-style: none;
     margin-bottom: 0px;
-    &.active {
-      box-shadow: inset 0px -2px 0px 0px #000;
-    }
   }
   @include media(">tablet") {
     padding: 0 2.5rem;
@@ -148,7 +149,6 @@ body {
   @include media(">widedesktop") {
     left: $widedesktop-sidebar-width;
   }
-  background-color: white;
   .item {
     padding-top: 1.5em;
     padding-bottom: 1.5em;
@@ -208,9 +208,6 @@ body {
   }
 }
 
-.discrete {
-  color: rgba(0, 0, 0, 0.87);
-}
 .link {
   cursor: pointer;
 }
@@ -355,3 +352,8 @@ input + .help {
 .table td .ui.dropdown {
   min-width: 150px;
 }
+
+
+
+@import "./themes/_light.scss";
+@import "./themes/_dark.scss";
diff --git a/front/src/style/themes/_dark.scss b/front/src/style/themes/_dark.scss
new file mode 100644
index 0000000000000000000000000000000000000000..f612914ca87ad3e14876d8026ec95757ffb2e380
--- /dev/null
+++ b/front/src/style/themes/_dark.scss
@@ -0,0 +1,222 @@
+$background-color: rgb(43, 58, 66);
+$button-hover-color: rgb(33, 48, 56);
+$light-background-color: rgb(51, 71, 82);
+$input-background-color: rgb(189, 211, 222);
+$loading-background-color: rgba(43, 58, 66, 0.9);
+$text-color: rgb(223, 235, 240);
+$discrete-text-color: rgba(223, 235, 240, 0.904);
+$border-color: rgb(63, 88, 102);
+$light-shadow-color: rgba(223, 235, 240, 0.15);
+$shadow-color: rgba(63, 102, 97, 0.95);
+$box-shadow: 0px 1px 3px 0px rgba(63, 88, 102, 0.95), 0px 0px 0px 1px rgba(63, 88, 102, 0.98);
+$link-color: rgb(255, 144, 0);
+
+.theme-dark {
+  background-color: $background-color;
+  .ui.labeled.input {
+    input, .label {
+      background-color: $input-background-color;
+      &::placeholder {
+        color: $light-background-color;
+
+      }
+
+    }
+  }
+  .ui.statistics .statistic {
+    > .label, > .value {
+      color: $text-color;
+    }
+  }
+  .ui.link.list.list .active.item, .ui.link.list.list .active.item a:not(.ui) {
+    color: inherit;
+  }
+  .ui.form textarea, .ui.form select, .ui.selection.dropdown, .ui.dropdown.selected, .ui.dropdown .menu .selected.item, .ui.form input:not([type]), .ui.form input[type="date"], .ui.form input[type="datetime-local"], .ui.form input[type="email"], .ui.form input[type="number"], .ui.form input[type="password"], .ui.form input[type="search"], .ui.form input[type="tel"], .ui.form input[type="time"], .ui.form input[type="text"], .ui.form input[type="file"], .ui.form input[type="url"] {
+    background-color: $input-background-color;
+    &::placeholder {
+      color: $light-background-color;
+
+    }
+  }
+  .ui.dropdown .menu .item:hover {
+    background-color: $light-background-color;
+    color: $text-color;
+
+  }
+  .main.pusher > .ui.secondary.menu {
+    background-color: $background-color;
+    box-shadow: inset 0px -2px 0px 0px $light-background-color;
+    .ui.item {
+      color: $text-color;
+      &.active {
+        box-shadow: inset 0px -2px 0px 0px $shadow-color;
+      }
+    }
+  }
+  .ui.modal {
+    > .header, > .content, > .actions {
+      background-color: $background-color;
+    }
+    > .header {
+      border-bottom: 1px solid $border-color;
+    }
+
+    > .actions {
+      border-top: 1px solid $border-color;
+    }
+  }
+  main, .main, footer, .modal {
+
+    .ui.menu {
+      background-color: $light-background-color;
+      .item {
+
+        color: $text-color;
+      }
+    }
+    .ui.secondary.menu .dropdown.item:hover, .ui.secondary.menu .link.item:hover, .ui.secondary.menu a.item:hover {
+      background: $background-color;
+      color: $text-color;
+    }
+    .header, .ui.form .field > label, .sub.header {
+      color: $text-color;
+    }
+    .ui.attached.header {
+      background-color: transparent;
+    }
+    .ui.toggle.checkbox input:checked ~ .box, .ui.toggle.checkbox input:checked ~ label {
+      color: $text-color !important;
+    }
+    .ui.toggle.checkbox .box::before, .ui.toggle.checkbox label::before {
+      background-color: $light-background-color;
+    }
+    a:not(.ui):not(.discrete) {
+      color: $link-color;
+    }
+    .ui.segment:not(.basic) {
+      background-color: $light-background-color;
+    }
+    .ui.list, .ui.dropdown {
+      .item, div.item, a.item, .button.item {
+        background-color: $background-color;
+        color: $discrete-text-color;
+      }
+      .selected.item:not(:hover) {
+        color: $background-color;
+      }
+    }
+    .ui.divided.items > .item:not(:first-child) {
+      border-top: 1px solid $border-color;
+    }
+    .ui.items {
+      .extra {
+        color: $discrete-text-color;
+      }
+    }
+    label, .toggle label {
+      color: $text-color !important;
+    }
+    &, .main.pusher, .ui.vertical.segment {
+      color: $text-color;
+      background-color: $background-color;
+    }
+
+    .discrete {
+      color: $discrete-text-color;
+    }
+
+    .ui.table thead th, .ui.table {
+      color: $text-color;
+    }
+    .ui.divider:not(.vertical):not(.horizontal) {
+      border-top: 1px solid $border-color;
+      border-bottom: 1px solid $border-color;
+    }
+    .ui.cards > .card, .ui.card {
+      color: $text-color;
+      background-color: $background-color;
+      box-shadow: $box-shadow;
+      .content, .header, .description {
+        color: $text-color;
+      }
+      .extra, .meta {
+        color: $discrete-text-color;
+      }
+    }
+    .playlist.card {
+      .attached.button {
+        background-color: $light-background-color;
+      }
+    }
+
+    // buttons
+    [class='ui button ui button'], [class='ui button'], [class='ui icon button'], [class='ui fluid button'], [class='ui cancel button'] {
+      background-color: $light-background-color;
+      color: $text-color;
+      &:hover {
+        background-color: $button-hover-color;
+
+      }
+    }
+    .ui.buttons > .ui.button:not(.basic):not(.inverted), .ui.buttons:not(.basic):not(.inverted) > .button {
+      box-shadow: 0px 0px 0px 1px $light-shadow-color inset;
+
+    }
+    .ui.basic.buttons:not(:hover):not(.green):not(.orange):not(.yellow):not(.red) .button, .ui.basic.button {
+      box-shadow: 0px 0px 0px 1px $text-color inset;
+      &:not(:hover):not(.green):not(.orange):not(.yellow):not(.red) {
+        color: $text-color !important;
+      }
+    }
+    .ui.basic.buttons .button, .ui.basic.button {
+      &:hover {
+        color: $text-color !important;
+      }
+
+    }
+    .ui.basic.buttons:not(.green):not(.orange):not(.yellow):not(.red) .button:hover, .ui.basic.button:not(.green):not(.orange):not(.yellow):not(.red):hover, .ui.basic.button:not(.green):not(.orange):not(.yellow):not(.red):active, .ui.basic.button:not(.green):not(.orange):not(.yellow):not(.red):focus {
+      color: $background-color !important;
+    }
+    // loading /dimmers
+    .ui.loading.form::before {
+      background-color: $loading-background-color;
+    }
+    .ui.inverted.dimmer {
+      background-color: $loading-background-color;
+    }
+    // table
+    .ui.basic.table tbody tr, .ui.table tr td {
+      border-bottom: 1px solid $border-color;
+    }
+    .ui.table thead th {
+      border-bottom: 1px solid $border-color;
+    }
+    .ui.table {
+      &:not(.basic) {
+        &, thead th {
+          background-color: $light-background-color;
+        }
+      }
+    }
+  }
+  .ui.link.list.list a.item:hover, .ui.link.list.list .item a:not(.ui):not(.button):hover {
+    color: $link-color;
+  }
+  [data-tooltip]::after {
+    background-color: $light-background-color;
+    color: $text-color;
+  }
+  .ui.progress > .label {
+    color: $text-color;
+
+  }
+  i.grey.icon {
+    color: $text-color !important;
+  }
+  input {
+    &::selection, &::-moz-selection {
+      background: $background-color;
+      color: $text-color;
+    }
+  }
+}
diff --git a/front/src/style/themes/_light.scss b/front/src/style/themes/_light.scss
new file mode 100644
index 0000000000000000000000000000000000000000..a6e1a0cdf621edaf3b7ed5dd57d0a6e8c4727b02
--- /dev/null
+++ b/front/src/style/themes/_light.scss
@@ -0,0 +1,35 @@
+
+
+.theme-light {
+
+  .main.pusher > .ui.secondary.menu {
+    box-shadow: inset 0px -2px 0px 0px rgba(34, 36, 38, 0.15);
+    background-color: white;
+    .ui.item {
+      &.active {
+        box-shadow: inset 0px -2px 0px 0px #000;
+      }
+    }
+  }
+
+  .discrete {
+    color: rgba(0, 0, 0, 0.87);
+  }
+  .playlist.card {
+    .attached.button {
+      background-color: rgb(243, 244, 245);
+    }
+  }
+
+  .disabled-row {
+    color: rgba(40, 40, 40, 0.3);
+  }
+  footer p {
+    color: grey;
+  }
+
+  footer#footer div.item:hover {
+    color: rgba(0, 0, 0, 0.87);
+  }
+
+}