From cd422832dd51b1dedbb55ec7bb60973f481cfbab Mon Sep 17 00:00:00 2001
From: Agate <me@agate.blue>
Date: Fri, 15 May 2020 14:12:36 +0200
Subject: [PATCH] New theming system

---
 .gitlab-ci.yml                                |   2 +-
 CONTRIBUTING.rst                              |  15 +
 api/funkwhale_api/static/css/project.css      |  38 -
 api/funkwhale_api/static/fonts/.gitkeep       |   0
 api/funkwhale_api/static/images/favicon.ico   | Bin 8348 -> 0 bytes
 api/funkwhale_api/static/js/project.js        |   1 -
 api/funkwhale_api/static/sass/project.scss    |  51 -
 front/package.json                            |   6 +-
 front/public/index.html                       |   2 +-
 front/scripts/fix-fomantic-css.py             | 897 ++++++++++++++++++
 front/scripts/fix-fomantic-css.sh             |   8 +
 front/src/App.vue                             | 190 ----
 front/src/components/About.vue                |  49 +-
 front/src/components/Footer.vue               |  34 +-
 front/src/components/Home.vue                 |  52 +-
 front/src/components/PageNotFound.vue         |   4 -
 front/src/components/Pagination.vue           |   9 +-
 front/src/components/Queue.vue                | 213 +----
 front/src/components/SetInstanceModal.vue     |   3 -
 front/src/components/ShortcutsModal.vue       |   4 -
 front/src/components/Sidebar.vue              | 242 +----
 front/src/components/admin/SettingsGroup.vue  |  11 +-
 front/src/components/audio/AlbumEntries.vue   |   2 +-
 front/src/components/audio/ChannelCard.vue    |  11 +-
 .../src/components/audio/ChannelEntryCard.vue |  16 +-
 .../src/components/audio/ChannelSerieCard.vue |  19 +-
 front/src/components/audio/EmbedWizard.vue    |   9 -
 .../components/audio/LibraryFollowButton.vue  |   4 -
 front/src/components/audio/PlayButton.vue     |  14 +-
 front/src/components/audio/Player.vue         |  83 +-
 front/src/components/audio/Search.vue         |   4 -
 front/src/components/audio/SearchBar.vue      |   4 -
 front/src/components/audio/VolumeControl.vue  |  36 +-
 front/src/components/audio/album/Card.vue     |  26 +-
 front/src/components/audio/album/Widget.vue   |  15 -
 front/src/components/audio/artist/Card.vue    |  17 +-
 front/src/components/audio/artist/Widget.vue  |  24 -
 front/src/components/audio/track/Row.vue      |  22 +-
 front/src/components/audio/track/Table.vue    |  15 +-
 front/src/components/audio/track/Widget.vue   |  40 +-
 front/src/components/auth/ApplicationForm.vue |  14 +-
 front/src/components/auth/Authorize.vue       |  20 +-
 front/src/components/auth/LoginForm.vue       |   6 +-
 front/src/components/auth/Logout.vue          |   4 -
 front/src/components/auth/Settings.vue        |  16 +-
 front/src/components/auth/SignupForm.vue      |   8 +-
 .../src/components/auth/SubsonicTokenForm.vue |  10 +-
 .../components/channels/SubscribeButton.vue   |   4 -
 front/src/components/channels/UploadForm.vue  |   8 +-
 .../src/components/common/ActionFeedback.vue  |   2 +-
 front/src/components/common/ActionTable.vue   |  12 +-
 front/src/components/common/ActorAvatar.vue   |   7 -
 front/src/components/common/ContentForm.vue   |   2 +-
 front/src/components/common/CopyInput.vue     |  13 +-
 .../src/components/common/DangerousButton.vue |   2 +-
 front/src/components/common/EmptyState.vue    |  12 +-
 front/src/components/common/UserLink.vue      |   8 +-
 front/src/components/favorites/List.vue       |   6 +-
 .../favorites/TrackFavoriteIcon.vue           |   4 -
 .../src/components/federation/FetchButton.vue |   2 +-
 front/src/components/library/AlbumBase.vue    |   4 +-
 front/src/components/library/AlbumDetail.vue  |   4 -
 front/src/components/library/Albums.vue       |   8 +-
 front/src/components/library/ArtistBase.vue   |   2 +-
 front/src/components/library/ArtistDetail.vue |   4 -
 front/src/components/library/Artists.vue      |  23 +-
 front/src/components/library/EditCard.vue     |  14 +-
 front/src/components/library/EditForm.vue     |   7 +-
 front/src/components/library/FileUpload.vue   |  35 +-
 .../components/library/FileUploadWidget.vue   |   4 -
 front/src/components/library/Home.vue         |   4 -
 front/src/components/library/Library.vue      |  34 +-
 front/src/components/library/Radios.vue       |   8 +-
 front/src/components/library/TagDetail.vue    |  10 +-
 front/src/components/library/TagsSelector.vue |   8 -
 front/src/components/library/TrackBase.vue    |   2 +-
 front/src/components/library/TrackDetail.vue  |   8 -
 .../src/components/library/radios/Builder.vue |   2 +-
 .../src/components/library/radios/Filter.vue  |   6 +-
 front/src/components/manage/ChannelsTable.vue |   2 +-
 .../components/manage/library/AlbumsTable.vue |   2 +-
 .../manage/library/ArtistsTable.vue           |   2 +-
 .../manage/library/LibrariesTable.vue         |   2 +-
 .../components/manage/library/TagsTable.vue   |   2 +-
 .../components/manage/library/TracksTable.vue |   2 +-
 .../manage/library/UploadsTable.vue           |   2 +-
 .../manage/moderation/DomainsTable.vue        |   2 +-
 .../manage/moderation/InstancePolicyCard.vue  |   3 -
 .../manage/moderation/InstancePolicyForm.vue  |  16 +-
 .../manage/moderation/NotesThread.vue         |   2 +-
 .../manage/moderation/ReportCard.vue          |  10 +-
 .../manage/moderation/UserRequestCard.vue     |  10 +-
 .../manage/users/InvitationForm.vue           |   3 -
 .../manage/users/InvitationsTable.vue         |   4 +-
 .../components/manage/users/UsersTable.vue    |   4 +-
 .../src/components/moderation/FilterModal.vue |   6 +-
 .../src/components/moderation/ReportModal.vue |   6 +-
 .../notifications/NotificationRow.vue         |  13 +-
 front/src/components/playlists/Card.vue       |   4 +-
 front/src/components/playlists/CardList.vue   |   4 -
 front/src/components/playlists/Editor.vue     |  22 +-
 front/src/components/playlists/Form.vue       |   4 -
 .../components/playlists/PlaylistModal.vue    |  13 +-
 .../playlists/TrackPlaylistIcon.vue           |   4 -
 front/src/components/playlists/Widget.vue     |   7 +-
 front/src/components/radios/Button.vue        |  11 +-
 front/src/components/radios/Card.vue          |   7 +-
 front/src/components/semantic/Modal.vue       |   4 -
 front/src/components/tags/List.vue            |  11 +-
 front/src/style/_css_vars.scss                |   5 +
 front/src/style/_main.scss                    | 832 +---------------
 front/src/style/_site.scss                    | 202 ----
 front/src/style/_vars.scss                    | 115 +++
 front/src/style/components/_action_table.scss |   8 +
 front/src/style/components/_album_card.scss   |   7 +
 front/src/style/components/_avatar.scss       |  12 +
 front/src/style/components/_button.scss       |  83 ++
 front/src/style/components/_card.scss         | 125 +++
 front/src/style/components/_content_form.scss |  17 +
 front/src/style/components/_copy_input.scss   |  11 +
 front/src/style/components/_empty_state.scss  |   6 +
 front/src/style/components/_file_upload.scss  |  34 +
 front/src/style/components/_form.scss         |  30 +
 front/src/style/components/_header.scss       |  17 +
 front/src/style/components/_label.scss        |  10 +
 front/src/style/components/_modal.scss        |   4 +
 front/src/style/components/_pagination.scss   |  12 +
 front/src/style/components/_placeholder.scss  |  31 +
 front/src/style/components/_play_button.scss  |   5 +
 front/src/style/components/_player.scss       | 219 +++++
 .../style/components/_playlist_editor.scss    |   7 +
 front/src/style/components/_queue.scss        | 233 +++++
 .../src/style/components/_settings_group.scss |   5 +
 front/src/style/components/_sidebar.scss      | 244 +++++
 front/src/style/components/_table.scss        |  38 +
 front/src/style/components/_tags_list.scss    |   9 +
 front/src/style/components/_tooltip.scss      |  14 +
 front/src/style/components/_track_table.scss  |  17 +
 front/src/style/components/_track_widget.scss |  31 +
 front/src/style/components/_user_link.scss    |   7 +
 .../src/style/components/_volume_control.scss |  31 +
 front/src/style/globals/_app.scss             |   9 +
 front/src/style/globals/_channels.scss        | 117 +++
 front/src/style/globals/_fomantic.scss        |  73 ++
 front/src/style/globals/_layout.scss          | 167 ++++
 front/src/style/globals/_typography.scss      |  18 +
 front/src/style/globals/_utils.scss           | 106 +++
 front/src/style/pages/_about.scss             |  31 +
 .../style/pages/_admin_account_detail.scss    |   9 +
 .../src/style/pages/_admin_domain_detail.scss |   5 +
 front/src/style/pages/_admin_library.scss     |   6 +
 front/src/style/pages/_home.scss              |  33 +
 front/src/style/pages/_library.scss           |  27 +
 front/src/style/pages/_notifications.scss     |   7 +
 front/src/style/pages/_profile.scss           |   6 +
 front/src/style/themes/_dark.scss             | 300 ------
 front/src/style/themes/_light.scss            |  52 -
 front/src/style/themes/dark/_main.scss        |   7 +
 front/src/style/themes/dark/_vars.scss        |  33 +
 front/src/style/themes/light/_main.scss       |   9 +
 front/src/views/Notifications.vue             |  11 +-
 front/src/views/admin/ChannelDetail.vue       |   2 +-
 front/src/views/admin/library/AlbumDetail.vue |   2 +-
 .../src/views/admin/library/ArtistDetail.vue  |   2 +-
 front/src/views/admin/library/Base.vue        |   9 +-
 front/src/views/admin/library/EditsList.vue   |   4 -
 .../src/views/admin/library/LibraryDetail.vue |   2 +-
 front/src/views/admin/library/TagDetail.vue   |   2 +-
 front/src/views/admin/library/TrackDetail.vue |   2 +-
 .../src/views/admin/library/UploadDetail.vue  |   2 +-
 .../views/admin/moderation/AccountsDetail.vue |  14 +-
 .../views/admin/moderation/AccountsList.vue   |   4 -
 .../views/admin/moderation/DomainsDetail.vue  |  11 +-
 .../views/admin/moderation/DomainsList.vue    |   6 +-
 .../views/admin/moderation/ReportsList.vue    |   4 -
 .../views/admin/moderation/RequestsList.vue   |   4 -
 .../src/views/admin/users/InvitationsList.vue |   4 -
 front/src/views/admin/users/UsersList.vue     |   4 -
 front/src/views/auth/EmailConfirm.vue         |   6 +-
 front/src/views/auth/Login.vue                |   4 -
 front/src/views/auth/PasswordReset.vue        |   6 +-
 front/src/views/auth/PasswordResetConfirm.vue |   6 +-
 front/src/views/auth/ProfileBase.vue          |  13 +-
 front/src/views/auth/Signup.vue               |   4 -
 front/src/views/channels/DetailBase.vue       |   2 +-
 front/src/views/content/libraries/Form.vue    |   5 +-
 front/src/views/content/libraries/Home.vue    |   4 -
 front/src/views/content/libraries/Quota.vue   |  20 +-
 front/src/views/content/remote/Card.vue       |  14 +-
 front/src/views/content/remote/Home.vue       |   4 -
 front/src/views/library/Edit.vue              |  10 +-
 front/src/views/playlists/Detail.vue          |   8 +-
 front/src/views/playlists/List.vue            |   8 +-
 front/src/views/radios/Detail.vue             |   6 +-
 front/vue.config.js                           |   7 +
 front/yarn.lock                               | 470 ++-------
 196 files changed, 3359 insertions(+), 3486 deletions(-)
 delete mode 100644 api/funkwhale_api/static/css/project.css
 delete mode 100644 api/funkwhale_api/static/fonts/.gitkeep
 delete mode 100644 api/funkwhale_api/static/images/favicon.ico
 delete mode 100644 api/funkwhale_api/static/js/project.js
 delete mode 100644 api/funkwhale_api/static/sass/project.scss
 create mode 100755 front/scripts/fix-fomantic-css.py
 create mode 100755 front/scripts/fix-fomantic-css.sh
 create mode 100644 front/src/style/_css_vars.scss
 delete mode 100644 front/src/style/_site.scss
 create mode 100644 front/src/style/_vars.scss
 create mode 100644 front/src/style/components/_action_table.scss
 create mode 100644 front/src/style/components/_album_card.scss
 create mode 100644 front/src/style/components/_avatar.scss
 create mode 100644 front/src/style/components/_button.scss
 create mode 100644 front/src/style/components/_card.scss
 create mode 100644 front/src/style/components/_content_form.scss
 create mode 100644 front/src/style/components/_copy_input.scss
 create mode 100644 front/src/style/components/_empty_state.scss
 create mode 100644 front/src/style/components/_file_upload.scss
 create mode 100644 front/src/style/components/_form.scss
 create mode 100644 front/src/style/components/_header.scss
 create mode 100644 front/src/style/components/_label.scss
 create mode 100644 front/src/style/components/_modal.scss
 create mode 100644 front/src/style/components/_pagination.scss
 create mode 100644 front/src/style/components/_placeholder.scss
 create mode 100644 front/src/style/components/_play_button.scss
 create mode 100644 front/src/style/components/_player.scss
 create mode 100644 front/src/style/components/_playlist_editor.scss
 create mode 100644 front/src/style/components/_queue.scss
 create mode 100644 front/src/style/components/_settings_group.scss
 create mode 100644 front/src/style/components/_sidebar.scss
 create mode 100644 front/src/style/components/_table.scss
 create mode 100644 front/src/style/components/_tags_list.scss
 create mode 100644 front/src/style/components/_tooltip.scss
 create mode 100644 front/src/style/components/_track_table.scss
 create mode 100644 front/src/style/components/_track_widget.scss
 create mode 100644 front/src/style/components/_user_link.scss
 create mode 100644 front/src/style/components/_volume_control.scss
 create mode 100644 front/src/style/globals/_app.scss
 create mode 100644 front/src/style/globals/_channels.scss
 create mode 100644 front/src/style/globals/_fomantic.scss
 create mode 100644 front/src/style/globals/_layout.scss
 create mode 100644 front/src/style/globals/_typography.scss
 create mode 100644 front/src/style/globals/_utils.scss
 create mode 100644 front/src/style/pages/_about.scss
 create mode 100644 front/src/style/pages/_admin_account_detail.scss
 create mode 100644 front/src/style/pages/_admin_domain_detail.scss
 create mode 100644 front/src/style/pages/_admin_library.scss
 create mode 100644 front/src/style/pages/_home.scss
 create mode 100644 front/src/style/pages/_library.scss
 create mode 100644 front/src/style/pages/_notifications.scss
 create mode 100644 front/src/style/pages/_profile.scss
 delete mode 100644 front/src/style/themes/_dark.scss
 delete mode 100644 front/src/style/themes/_light.scss
 create mode 100644 front/src/style/themes/dark/_main.scss
 create mode 100644 front/src/style/themes/dark/_vars.scss
 create mode 100644 front/src/style/themes/light/_main.scss

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 25d6ccb04..7a7e106e8 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -108,7 +108,7 @@ flake8:
   variables:
     GIT_STRATEGY: fetch
   before_script:
-    - pip install flake8
+    - pip install 'flake8<3.7'
   script:
     - flake8 -v api
   cache:
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index fe081b97c..d3cfb6828 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -704,6 +704,21 @@ Views: you can find some readable views tests in file: ``api/tests/users/test_vi
 Contributing to the front-end
 -----------------------------
 
+Styles and themes
+^^^^^^^^^^^^^^^^^
+
+Our UI framework is Fomantic UI (https://fomantic-ui.com/), and Funkwhale's custom styles are written in SCSS. All the styles are configured in ``front/src/styles/_main.scss``,
+including imporing of Fomantic UI styles and components.
+
+We're applying several changes on top of the Fomantic CSS files, before they are imported:
+
+1. Many hardcoded color values are replaced by CSS vars: e.g ``color: orange`` is replaced by ``color: var(--vibrant-color)``. This makes theming way easier.
+2. Unused components variations and icons are stripped from the source files, in order to reduce the final size of our CSS files
+
+This changes are applied automatically when running ``yarn install``, through a ``postinstall`` hook. Internally, ``front/scripts/fix-fomantic-css.py`` is called
+and handle both kind of modifications. Please refer to this script if you need to use new icons to the project, or restore some components variations that
+were stripped in order to use them.
+
 Running tests
 ^^^^^^^^^^^^^
 
diff --git a/api/funkwhale_api/static/css/project.css b/api/funkwhale_api/static/css/project.css
deleted file mode 100644
index 08505aeef..000000000
--- a/api/funkwhale_api/static/css/project.css
+++ /dev/null
@@ -1,38 +0,0 @@
-/* These styles are generated from project.scss. */
-
-.alert-debug {
-  color: black;
-  background-color: white;
-  border-color: #d6e9c6;
-}
-
-.alert-error {
-  color: #b94a48;
-  background-color: #f2dede;
-  border-color: #eed3d7;
-}
-
-/* This is a fix for the bootstrap4 alpha release */
-@media (max-width: 47.9em) {
-  .navbar-nav .nav-item {
-    float: none;
-    width: 100%;
-    display: inline-block;
-  }
-
-  .navbar-nav .nav-item + .nav-item {
-    margin-left: 0;
-  }
-
-  .nav.navbar-nav.pull-right {
-    float: none !important;
-  }
-}
-
-/* Display django-debug-toolbar.
-   See https://github.com/django-debug-toolbar/django-debug-toolbar/issues/742
-   and https://github.com/pydanny/cookiecutter-django/issues/317
-*/
-[hidden][style="display: block;"] {
-  display: block !important;
-}
\ No newline at end of file
diff --git a/api/funkwhale_api/static/fonts/.gitkeep b/api/funkwhale_api/static/fonts/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/api/funkwhale_api/static/images/favicon.ico b/api/funkwhale_api/static/images/favicon.ico
deleted file mode 100644
index e1c1dd1a32a3a077c41a21e52bc7fb5ac90d3afb..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 8348
zcmeHLX-gGh6rQ3V&`<D7CE<noAlEjsXwxujXd#F)ld{xou?49lT4^C#DMQg92<lcw
zMzolfs1@y^S<!}Aws>92%;lcmGu`Jl^WK@OV`^XKh08PZoaH%l?rdiiWq>kJ2@6vM
zhAH8L6=kTRD1!xR`-2o^hS&}loN!U1#gF+=YxEq~uqf5#iBw%p0;w;5ehm+6a!sSu
zh;X+$+}oF$X1Q6DwS~>Y_Hpw^(&QvJO<5ZCPrn5l<LUX!$CQ2UuxTFFU@d=5o~T)_
zM&9lX?aYJe)cC!DuyW2Hv#fzNBja7o{yI$k?DQi4Qa1T~osibBqqBpOvgVuShAz;K
zmYFZ>aNp%s{<NfD^tq;K$)o9J`Bk|DTDfaUPz^Y4pxH6?go{g!rZ4q%h8hZr&i<2r
zT*`Rbm$#it9u?E4ueH?P(XQoh{?VjqRllpGTlcTg>B3x1hf%*?eW>oS{<{4s^u_yG
zpDyG!_iV#~G=q<~slG@0Sx47XXQ%O;HY7ILVf}<nvkjKkUq5Z@&&!CV?=8)SJU*X~
zs@_!4mAeIW{>B-)R$6GV^A78)t0tN9`tu3r9Z+xM?NgUd8geu=c`0?r;-KR&IQjKs
zSB#VCpg8CPW&M}$sth@H=VS%t;23!!j};F)bb;W3EkA!4QmCsY_p81^T<lC2Jw<JL
zl>K0{;$g>e1Hl998^0M+r0-7ZSN+l_cMSRuEALTE@|d6+3{GMP^;_|<<G6v~fm@Db
z$SJ7$Z6)nIxtS6-&!vT#DsfNbg?K9-cIX1Z>yhubb{FF1IPgH|0>SH%pC!c)uFI)H
z?jv4y0uO{P5WE>~I<$r!RFo0lN4r{xm;Jy4p$h~b3S*a#)$Z*nI}&Kc_C+*zZHz1v
zIR8TBVH<RTUHMzt<pQkyWFERe+1}lKD}5^-d7%pgZx^j}ta#*yE)cw3G}D>7Y?Mp~
zofpsr+SP@>EX4e*bQ{nAUYtKrQ&-5d4j;FF{^-^Dt2^5I`HSb!|22PN26n3>hKPOy
zW<i`2eS&}fZx3to{LSA9#ODI-3qh#w1a37VHY(or(QZwVGl$n`v8t<fVnSrlb)f&h
zX0M(~Jg?;!WC#2>2D*A*v+c{bFKCwozbB{dObp~e&1Nxrj<4Ih4~w)M`nl08o}Wq2
z#Jt(k+M>+}JY(=2gm(gdUq)^@e%tX(F)RBtotnB2^y>YKz-5eg_qO&n%lJ1nuQmTY
zxmyE1NWjl1EGvD?Z|n;neT;sa?Q;Ef-#%$BYxXAhD4xF+@M>*qrR!x^sPN98|BX4;
z!$NJcKF`^C7f(<_vlp%b>`pxL^8Y<&?KFx{n_!5C9VqLA*CP@zhXs2t#dmrAKu?d_
y^&_r5_e@uesG}CO*g!3o?-kB+I^cA`>44J#rvpw0oDMi0a5~_0!0Eu>4*Uj$LD0AW

diff --git a/api/funkwhale_api/static/js/project.js b/api/funkwhale_api/static/js/project.js
deleted file mode 100644
index d26d23b9b..000000000
--- a/api/funkwhale_api/static/js/project.js
+++ /dev/null
@@ -1 +0,0 @@
-/* Project specific Javascript goes here. */
diff --git a/api/funkwhale_api/static/sass/project.scss b/api/funkwhale_api/static/sass/project.scss
deleted file mode 100644
index 37c69e454..000000000
--- a/api/funkwhale_api/static/sass/project.scss
+++ /dev/null
@@ -1,51 +0,0 @@
-// project specific CSS goes here
-
-// Alert colors
-
-$white: #fff;
-$mint-green: #d6e9c6;
-$black: #000;
-$pink: #f2dede;
-$dark-pink: #eed3d7;
-$red: #b94a48;
-
-// bootstrap alert CSS, translated to the django-standard levels of
-// debug, info, success, warning, error
-
-.alert-debug {
-  background-color: $white;
-  border-color: $mint-green;
-  color: $black;
-}
-
-.alert-error {
-  background-color: $pink;
-  border-color: $dark-pink;
-  color: $red;
-}
-
-// This is a fix for the bootstrap4 alpha release
-
-@media (max-width: 47.9em) {
-  .navbar-nav .nav-item {
-    display: inline-block;
-    float: none;
-    width: 100%;
-  }
-
-  .navbar-nav .nav-item + .nav-item {
-    margin-left: 0;
-  }
-
-  .nav.navbar-nav.pull-right {
-    float: none !important;
-  }
-}
-
-// Display django-debug-toolbar.
-// See https://github.com/django-debug-toolbar/django-debug-toolbar/issues/742
-// and https://github.com/pydanny/cookiecutter-django/issues/317
-
-[hidden][style="display: block;"] {
-  display: block !important;
-}
diff --git a/front/package.json b/front/package.json
index f64e619a9..004370f65 100644
--- a/front/package.json
+++ b/front/package.json
@@ -10,7 +10,9 @@
     "test:unit": "vue-cli-service test:unit",
     "lint": "vue-cli-service lint",
     "i18n-compile": "scripts/i18n-compile.sh",
-    "i18n-extract": "scripts/i18n-extract.sh"
+    "i18n-extract": "scripts/i18n-extract.sh",
+    "fix-fomantic-css": "scripts/fix-fomantic-css.sh",
+    "postinstall": "yarn run fix-fomantic-css"
   },
   "dependencies": {
     "axios": "^0.18.0",
@@ -25,6 +27,7 @@
     "qs": "^6.7.0",
     "register-service-worker": "^1.6.2",
     "sanitize-html": "^1.20.1",
+    "sass": "^1.26.5",
     "showdown": "^1.8.6",
     "text-clipper": "^1.3.0",
     "vue": "^2.6.10",
@@ -54,7 +57,6 @@
     "glob-all": "^3.1.0",
     "mocha": "^5.2.0",
     "moxios": "^0.4.0",
-    "node-sass": "^4.9.3",
     "preload-webpack-plugin": "^3.0.0-beta.4",
     "purgecss-webpack-plugin": "^1.6.0",
     "sass-loader": "^8.0.2",
diff --git a/front/public/index.html b/front/public/index.html
index 37fbacde4..9825df720 100644
--- a/front/public/index.html
+++ b/front/public/index.html
@@ -30,7 +30,7 @@
     #orange-square {
       width: 56px;
       height: 56px;
-      background-color: #f2711c
+      background-color: #f2711c;
     }
     #fake-content {
       height: 100vh;
diff --git a/front/scripts/fix-fomantic-css.py b/front/scripts/fix-fomantic-css.py
new file mode 100755
index 000000000..0cdb4b5ff
--- /dev/null
+++ b/front/scripts/fix-fomantic-css.py
@@ -0,0 +1,897 @@
+#!/usr/bin/env python3
+"""
+This scripts handles all the heavy-lifting of parsing CSS files from ``fomantic-ui-css`` and:
+
+1. Replace hardcoded values by their CSS vars counterparts, for easier theming
+2. Strip unused styles and icons to reduce the final size of CSS
+
+Updated files are not modified in place, but instead copied to another directory (``fomantic-ui-css/tweaked``), in order
+to allow easy comparison detection of changes.
+
+If you change this file, you'll need to run ``yarn run fix-fomantic-css`` manually for the changes
+to be picked up. If the ``NOSTRIP`` environment variable is set, the second step will be skipped.
+"""
+import argparse
+import os
+
+STRIP_UNUSED = "NOSTRIP" not in os.environ
+
+# Perform a blind replacement of some strings in all fomantic CSS files
+GLOBAL_REPLACES = [
+    # some selectors are repeated in the stylesheet, for some reason
+    (".ui.ui.ui.ui", ".ui"),
+    (".ui.ui.ui", ".ui"),
+    (".ui.ui", ".ui"),
+    (".icon.icon.icon.icon", ".icon"),
+    (".icon.icon.icon", ".icon"),
+    (".icon.icon", ".icon"),
+    # actually useful stuff
+    ("'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif", "var(--font-family)"),
+    (".orange", ".vibrant"),
+    ("#F2711C", "var(--vibrant-color)"),
+    ("#FF851B", "var(--vibrant-color)"),
+    ("#f26202", "var(--vibrant-hover-color)"),
+    ("#e76b00", "var(--vibrant-hover-color)"),
+    ("#cf590c", "var(--vibrant-active-color)"),
+    ("#f56100", "var(--vibrant-active-color)"),
+    ("#e76b00", "var(--vibrant-active-color)"),
+    ("#e55b00", "var(--vibrant-focus-color)"),
+    ("#f17000", "var(--vibrant-focus-color)"),
+    (".green", ".success"),
+    ("#21BA45", "var(--success-color)"),
+    ("#2ECC40", "var(--success-color)"),
+    ("#16ab39", "var(--success-hover-color)"),
+    ("#1ea92e", "var(--success-hover-color)"),
+    ("#198f35", "var(--success-active-color)"),
+    ("#25a233", "var(--success-active-color)"),
+    ("#0ea432", "var(--success-focus-color)"),
+    ("#19b82b", "var(--success-focus-color)"),
+    (".blue", ".primary"),
+    ("#2185D0", "var(--primary-color)"),
+    ("#54C8FF", "var(--primary-color)"),
+    ("#54C8FF", "var(--primary-color)"),
+    ("#1678c2", "var(--primary-hover-color)"),
+    ("#21b8ff", "var(--primary-hover-color)"),
+    ("#1a69a4", "var(--primary-active-color)"),
+    ("#0d71bb", "var(--primary-focus-color)"),
+    ("#2bbbff", "var(--primary-focus-color)"),
+    (".yellow", ".warning"),
+    ("#FBBD08", "var(--warning-color)"),
+    ("#FFE21F", "var(--warning-color)"),
+    ("#eaae00", "var(--warning-hover-color)"),
+    ("#ebcd00", "var(--warning-hover-color)"),
+    ("#cd9903", "var(--warning-active-color)"),
+    ("#ebcd00", "var(--warning-active-color)"),
+    ("#daa300", "var(--warning-focus-color)"),
+    ("#f5d500", "var(--warning-focus-color)"),
+    (".red.", ".danger."),
+    ("#DB2828", "var(--danger-color)"),
+    ("#FF695E", "var(--danger-color)"),
+    ("#d01919", "var(--danger-hover-color)"),
+    ("#ff392b", "var(--danger-hover-color)"),
+    ("#b21e1e", "var(--danger-active-color)"),
+    ("#ca1010", "var(--danger-focus-color)"),
+    ("#ff4335", "var(--danger-focus-color)"),
+]
+
+def discard_unused_icons(rule):
+    """
+    Add an icon to this list if you want to use it in the app.
+    """
+    used_icons = [
+        ".angle",
+        ".arrow",
+        ".at",
+        ".ban",
+        ".bell",
+        ".book",
+        ".bookmark",
+        ".check",
+        ".clock",
+        ".close",
+        ".cloud",
+        ".code",
+        ".comment",
+        ".copy",
+        ".copyright",
+        ".danger",
+        ".database",
+        ".delete",
+        ".disc",
+        ".down angle",
+        ".download",
+        ".dropdown",
+        ".edit",
+        ".ellipsis",
+        ".eraser",
+        ".external",
+        ".eye",
+        ".feed",
+        ".file",
+        ".forward",
+        ".globe",
+        ".hashtag",
+        ".headphones",
+        ".heart",
+        ".home",
+        ".hourglass",
+        ".info",
+        ".layer",
+        ".lines",
+        ".link",
+        ".list",
+        ".loading",
+        ".lock",
+        ".minus",
+        ".mobile",
+        ".music",
+        ".paper",
+        ".pause",
+        ".pencil",
+        ".play",
+        ".plus",
+        ".podcast",
+        ".question",
+        ".question  ",
+        ".random",
+        ".redo",
+        ".refresh",
+        ".repeat",
+        ".rss",
+        ".search",
+        ".server",
+        ".share",
+        ".shield",
+        ".sidebar",
+        ".sign",
+        ".spinner",
+        ".step",
+        ".stream",
+        ".track",
+        ".trash",
+        ".undo",
+        ".upload",
+        ".user",
+        ".users",
+        ".volume",
+        ".wikipedia",
+        ".wrench",
+        ".x",
+    ]
+    if ":before" not in rule["lines"][0]:
+        return False
+
+    return not match(rule, used_icons)
+
+
+"""
+Below is the main configuration object that is used for fine-grained replacement of properties
+in component files. It also handles removal of unused selectors.
+
+Example config for a component:
+
+REPLACEMENTS = {
+    # applies to fomantic-ui-css/components/component-name.css
+    "component-name": {
+        # Discard any CSS rule matching one of the selectors listed below
+        # matching is done using a simple string search, so ``.pink`` will remove
+        # rules applied to ``.pink``, ``.pink.button`` and `.pinkdark`
+        "skip": [
+            ".unused.variation",
+            ".pink",
+        ],
+        # replace some CSS properties values in specific selectors
+        (".inverted", ".dark"): [
+            ("background", "var(--inverted-background)"),
+            ("color", "var(--inverted-color)"),
+        ],
+        (".active"): [
+            ("font-size", "var(--active-font-size)"),
+        ],
+    }
+}
+
+Given the previous config, the following style sheet:
+
+.. code-block:: css
+
+    .unsed.variation {
+        color: yellow;
+    }
+
+    .primary {
+        color: white;
+    }
+    .primary.pink {
+        color: pink;
+    }
+    .inverted.primary {
+        background: black;
+        color: white;
+        border-top: 1px solid red;
+    }
+    .inverted.primary.active {
+        font-size: 12px;
+    }
+
+Would be converted to:
+
+.. code-block:: css
+
+    .primary {
+        color: white;
+    }
+    .inverted.primary {
+        background: var(--inverted-background);
+        color: var(--inverted-color);
+        border-top: 1px solid red;
+    }
+    .inverted.primary.active {
+        font-size: var(--active-font-size);
+    }
+
+"""
+REPLACEMENTS = {
+    "site": {
+        ("a",): [
+            ("color", "var(--link-color)"),
+            ("text-decoration", "var(--link-text-decoration)"),
+        ],
+        ("a:hover",): [
+            ("color", "var(--link-hover-color)"),
+            ("text-decoration", "var(--link-hover-text-decoration)"),
+        ],
+        ("body",): [
+            ("background", "var(--site-background)"),
+            ("color", "var(--text-color)"),
+        ],
+        ("::-webkit-selection", "::-moz-selection", "::selection",): [
+            ("color", "var(--text-selection-color)"),
+            ("background-color", "var(--text-selection-background)"),
+        ],
+        (
+            "textarea::-webkit-selection",
+            "input::-webkit-selection",
+            "textarea::-moz-selection",
+            "input::-moz-selection",
+            "textarea::selection",
+            "input::selection",
+        ): [
+            ("color", "var(--input-selection-color)"),
+            ("background-color", "var(--input-selection-background)"),
+        ],
+    },
+    "button": {
+        "skip": [
+            ".vertical",
+            ".animated",
+            ".active",
+            ".olive",
+            ".brown",
+            ".teal",
+            ".violet",
+            ".purple",
+            ".brown",
+            ".grey",
+            ".black",
+            ".positive",
+            ".negative",
+            ".secondary",
+            ".tertiary",
+            ".facebook",
+            ".twitter",
+            ".google.plus",
+            ".vk",
+            ".linkedin",
+            ".instagram",
+            ".youtube",
+            ".whatsapp",
+            ".telegram",
+        ],
+        (".ui.orange.button", ".ui.orange.button:hover"): [
+            ("background-color", "var(--button-orange-background)")
+        ],
+        (".ui.basic.button",): [
+            ("background", "var(--button-basic-background)"),
+            ("color", "var(--button-basic-color)"),
+            ("box-shadow", "var(--button-basic-box-shadow)"),
+        ],
+        (".ui.basic.button:hover",): [
+            ("background", "var(--button-basic-hover-background)"),
+            ("color", "var(--button-basic-hover-color)"),
+            ("box-shadow", "var(--button-basic-hover-box-shadow)"),
+        ],
+    },
+    "card": {
+        "skip": [
+            ".inverted",
+            ".olive",
+            ".brown",
+            ".teal",
+            ".violet",
+            ".purple",
+            ".brown",
+            ".grey",
+            ".pink",
+            ".black",
+            ".vibrant",
+            ".success",
+            ".warning",
+            ".danger",
+            ".primary",
+            ".secondary",
+            ".horizontal",
+            ".raised",
+        ]
+    },
+    "checkbox": {
+        (
+            ".ui.toggle.checkbox label",
+            ".ui.toggle.checkbox input:checked ~ label",
+            '.ui.checkbox input[type="checkbox"]',
+            ".ui.checkbox input:focus ~ label",
+            ".ui.toggle.checkbox input:focus:checked ~ label",
+            ".ui.checkbox input:active ~ label",
+        ): [("color", "var(--form-label-color)"),],
+        (".ui.toggle.checkbox label:before",): [
+            ("background", "var(--input-background)"),
+        ],
+    },
+    "divider": {
+        (".ui.divider:not(.vertical):not(.horizontal)",): [
+            ("border-top", "var(--divider)"),
+            ("border-bottom", "var(--divider)"),
+        ],
+        (".ui.divider",): [("color", "var(--text-color)"),],
+    },
+    "dimmer": {
+        (".ui.inverted.dimmer",): [
+            ("background-color", "var(--dimmer-background)"),
+            ("color", "var(--dropdown-color)"),
+        ],
+    },
+    "dropdown": {
+        "skip": [".error", ".info", ".success", ".warning",],
+        (
+            ".ui.selection.dropdown",
+            ".ui.selection.visible.dropdown > .text:not(.default)",
+            ".ui.dropdown .menu",
+        ): [
+            ("background", "var(--dropdown-background)"),
+            ("color", "var(--dropdown-color)"),
+        ],
+        (".ui.dropdown .menu > .item",): [("color", "var(--dropdown-item-color)"),],
+        (".ui.dropdown .menu > .item:hover",): [
+            ("color", "var(--dropdown-item-hover-color)"),
+            ("background", "var(--dropdown-item-hover-background)"),
+        ],
+        (".ui.dropdown .menu .selected.item",): [
+            ("color", "var(--dropdown-item-selected-color)"),
+            ("background", "var(--dropdown-item-selected-background)"),
+        ],
+        (".ui.dropdown .menu > .header:not(.ui)",): [
+            ("color", "var(--dropdown-header-color)"),
+        ],
+        (".ui.dropdown .menu > .divider",): [("border-top", "var(--divider)"),],
+    },
+    "form": {
+        "skip": [".inverted", ".success", ".warning", ".error", ".info",],
+        ('.ui.form input[type="text"]', ".ui.form select", ".ui.input textarea"): [
+            ("background", "var(--input-background)"),
+            ("color", "var(--input-color)"),
+        ],
+        (
+            '.ui.form input[type="text"]:focus',
+            ".ui.form select:focus",
+            ".ui.form textarea:focus",
+        ): [
+            ("background", "var(--input-focus-background)"),
+            ("color", "var(--input-focus-color)"),
+        ],
+        (
+            ".ui.form ::-webkit-input-placeholder",
+            ".ui.form :-ms-input-placeholder",
+            ".ui.form ::-moz-placeholder",
+        ): [("color", "var(--input-placeholder-color)"),],
+        (
+            ".ui.form :focus::-webkit-input-placeholder",
+            ".ui.form :focus:-ms-input-placeholder",
+            ".ui.form :focus::-moz-placeholder",
+        ): [("color", "var(--input-focus-placeholder-color)"),],
+        (".ui.form .field > label", ".ui.form .inline.fields .field > label",): [
+            ("color", "var(--form-label-color)"),
+        ],
+    },
+    "grid": {
+        "skip": [
+            "wide tablet",
+            "screen",
+            "mobile only",
+            "tablet only",
+            "computer only",
+            "computer reversed",
+            "tablet reversed",
+            "wide computer",
+            "wide mobile",
+            "wide tablet",
+            "vertically",
+            ".celled",
+            ".doubling",
+            ".olive",
+            ".brown",
+            ".teal",
+            ".violet",
+            ".purple",
+            ".brown",
+            ".grey",
+            ".black",
+            ".positive",
+            ".negative",
+            ".secondary",
+            ".tertiary",
+            ".danger",
+            ".vibrant",
+            ".warning",
+            ".primary",
+            ".success",
+            ".justified",
+            ".centered",
+        ]
+    },
+    "icon": {"skip": discard_unused_icons},
+    "input": {
+        (".ui.input > input",): [
+            ("background", "var(--input-background)"),
+            ("color", "var(--input-color)"),
+        ],
+        (".ui.input > input:focus",): [
+            ("background", "var(--input-focus-background)"),
+            ("color", "var(--input-focus-color)"),
+        ],
+        (
+            ".ui.input > input::-webkit-input-placeholder",
+            ".ui.input > input::-moz-placeholder",
+            ".ui.input > input:-ms-input-placeholder",
+        ): [("color", "var(--input-placeholder-color)"),],
+        (
+            ".ui.input > input:focus::-webkit-input-placeholder",
+            ".ui.input > input:focus::-moz-placeholder",
+            ".ui.input > input:focus:-ms-input-placeholder",
+        ): [("color", "var(--input-focus-placeholder-color)"),],
+    },
+    "item": {
+        (".ui.divided.items > .item",): [("border-top", "var(--divider)"),],
+        (".ui.items > .item > .content",): [("color", "var(--text-color)"),],
+        (".ui.items > .item .extra",): [
+            ("color", "var(--really-discrete-text-color)"),
+        ],
+    },
+    "header": {
+        "skip": [
+            ".inverted",
+            ".block",
+            ".olive",
+            ".brown",
+            ".teal",
+            ".violet",
+            ".purple",
+            ".brown",
+            ".grey",
+            ".black",
+            ".pink",
+        ],
+        (".ui.header",): [("color", "var(--header-color)"),],
+        (".ui.header .sub.header",): [("color", "var(--header-color)"),],
+    },
+    "label": {
+        "skip": [
+            ".olive",
+            ".brown",
+            ".teal",
+            ".violet",
+            ".purple",
+            ".brown",
+            ".grey",
+            ".black",
+            ".positive",
+            ".negative",
+            ".secondary",
+            ".tertiary",
+            ".facebook",
+            ".twitter",
+            ".google.plus",
+            ".vk",
+            ".linkedin",
+            ".instagram",
+            ".youtube",
+            ".whatsapp",
+            ".telegram",
+            ".corner",
+            "ribbon",
+            "pointing",
+            "attached",
+        ],
+    },
+    "list": {
+        "skip": [
+            ".mini",
+            ".tiny",
+            ".small",
+            ".large",
+            ".big",
+            ".huge",
+            ".massive",
+            ".celled",
+            ".horizontal",
+            ".bulleted",
+            ".ordered",
+            ".suffixed",
+            ".inverted",
+            ".fitted",
+            "aligned",
+        ],
+        (".ui.list .list > .item a.header", ".ui.list .list > a.item"): [
+            ("color", "var(--link-color)"),
+            ("text-decoration", "var(--link-text-decoration)"),
+        ],
+        ("a:hover", ".ui.list .list > a.item:hover"): [
+            ("color", "var(--link-hover-color)"),
+            ("text-decoration", "var(--link-hover-text-decoration)"),
+        ],
+    },
+    "loader": {
+        "skip": [
+            ".olive",
+            ".brown",
+            ".teal",
+            ".violet",
+            ".purple",
+            ".brown",
+            ".grey",
+            ".black",
+            ".pink",
+            ".primary",
+            ".vibrant",
+            ".warning",
+            ".success",
+            ".danger",
+            ".elastic",
+        ],
+        (".ui.inverted.dimmer > .ui.loader",): [("color", "var(--dimmer-color)"),],
+    },
+    "message": {
+        "skip": [
+            ".olive",
+            ".brown",
+            ".teal",
+            ".violet",
+            ".purple",
+            ".brown",
+            ".grey",
+            ".black",
+            ".pink",
+            ".vibrant",
+            ".primary",
+            ".secondary",
+            ".floating",
+        ],
+    },
+    "menu": {
+        "skip": [
+            ".inverted.pointing",
+            ".olive",
+            ".brown",
+            ".teal",
+            ".violet",
+            ".purple",
+            ".brown",
+            ".grey",
+            ".black",
+            ".vertical.tabular",
+            ".primary.menu",
+            ".pink.menu",
+            ".vibrant.menu",
+            ".warning.menu",
+            ".success.menu",
+            ".danger.menu",
+            ".fitted",
+            "fixed",
+        ],
+        (".ui.menu .item",): [("color", "var(--menu-item-color)"),],
+        (".ui.vertical.inverted.menu .menu .item", ".ui.inverted.menu .item"): [
+            ("color", "var(--inverted-menu-item-color)"),
+        ],
+        (".inverted-ui.menu .active.item",): [
+            ("color", "var(--menu-inverted-active-item-color)"),
+        ],
+        (".ui.secondary.pointing.menu .active.item",): [
+            ("color", "var(--secondary-menu-active-item-color)"),
+        ],
+        (
+            ".ui.secondary.pointing.menu a.item:hover",
+            ".ui.secondary.pointing.menu .active.item:hover",
+        ): [("color", "var(--secondary-menu-hover-item-color)"),],
+        (".ui.menu .ui.dropdown .menu > .item",): [
+            ("color", "var(--dropdown-item-color) !important"),
+        ],
+        (".ui.menu .ui.dropdown .menu > .item:hover",): [
+            ("color", "var(--dropdown-item-hover-color) !important"),
+            ("background", "var(--dropdown-item-hover-background) !important"),
+        ],
+        (".ui.menu .dropdown.item .menu",): [
+            ("color", "var(--dropdown--color)"),
+            ("background", "var(--dropdown-background)"),
+        ],
+        (".ui.menu .ui.dropdown .menu > .active.item",): [
+            ("color", "var(--dropdown-item-selected-color)"),
+            ("background", "var(--dropdown-item-selected-background) !important"),
+        ],
+    },
+    "modal": {
+        (".ui.modal", ".ui.modal > .actions", ".ui.modal > .content"): [
+            ("background", "var(--modal-background)"),
+            ("border-bottom", "var(--divider)"),
+            ("border-top", "var(--divider)"),
+        ],
+        (".ui.modal > .close.inside",): [("color", "var(--text-color)"),],
+        (".ui.modal > .header",): [
+            ("color", "var(--header-color)"),
+            ("background", "var(--modal-background)"),
+            ("border-bottom", "var(--divider)"),
+            ("border-top", "var(--divider)"),
+        ],
+    },
+    "search": {
+        (
+            ".ui.search > .results",
+            ".ui.search > .results .result",
+            ".ui.category.search > .results .category .results",
+            ".ui.category.search > .results .category",
+            ".ui.category.search > .results .category > .name",
+            ".ui.search > .results > .message .header",
+            ".ui.search > .results > .message .description",
+        ): [
+            ("background", "var(--dropdown-background)"),
+            ("color", "var(--dropdown-item-color)"),
+        ],
+        (
+            ".ui.search > .results .result .title",
+            ".ui.search > .results .result .description",
+        ): [("color", "var(--dropdown-item-color)"),],
+        (".ui.search > .results .result:hover",): [
+            ("color", "var(--dropdown-item-hover-color)"),
+            ("background", "var(--dropdown-item-hover-background)"),
+        ],
+    },
+    "segment": {
+        "skip": [
+            ".stacked",
+            ".horizontal.segment",
+            ".inverted.segment",
+            ".circular",
+            ".piled",
+        ],
+    },
+    "sidebar": {
+        (".ui.left.visible.sidebar",): [("box-shadow", "var(--sidebar-box-shadow)"),]
+    },
+    "statistic": {
+        (".ui.statistic > .value", ".ui.statistic > .label"): [
+            ("color", "var(--text-color)"),
+        ],
+    },
+    "progress": {
+        (".ui.progress.success > .label",): [("color", "var(--text-color)"),],
+    },
+    "table": {
+        "skip": [
+            ".marked",
+            ".active",
+            ".olive",
+            ".brown",
+            ".teal",
+            ".violet",
+            ".purple",
+            ".brown",
+            ".grey",
+            ".black",
+            ".padded",
+            ".column.table",
+            ".inverted",
+            ".definition",
+            ".error",
+            ".negative",
+            ".structured",
+            "tablet stackable",
+        ],
+        (".ui.table", ".ui.table > thead > tr > th",): [
+            ("color", "var(--text-color)"),
+            ("background", "var(--table-background)"),
+        ],
+        (".ui.table > tr > td", ".ui.table > tbody + tbody tr:first-child > td"): [
+            ("border-top", "var(--table-border)"),
+        ],
+    },
+}
+
+
+def match(rule, skip):
+    if hasattr(skip, "__call__"):
+        return skip(rule)
+    for s in skip:
+        for rs in rule["selectors"]:
+            if s in rs:
+                return True
+
+    return False
+
+
+def rules_from_media_query(rule):
+    internal = rule["lines"][1:-1]
+    return parse_rules("\n".join(internal))
+
+
+def wraps(rule, internal_rules):
+    return {
+        "lines": [rule["lines"][0]]
+        + [line for r in internal_rules for line in r["lines"]]
+        + ["}"]
+    }
+
+
+def set_vars(component_name, rules):
+    """
+    Given rules parsed via ``parse_rules``, replace properties values when needed
+    using ``REPLACEMENTS`` and ``GLOBAL_REPLACES``.
+
+    Also remove unused styles if STRIP_UNUSED is set to True.
+    """
+    final_rules = []
+    try:
+        conf = REPLACEMENTS[component_name]
+    except KeyError:
+        return rules
+    selectors = list(conf.keys()) + list()
+    skip = None
+    if STRIP_UNUSED:
+        skip = conf.get("skip", [])
+        try:
+            skip = set(skip)
+        except TypeError:
+            pass
+
+    for rule in rules:
+        if rule["lines"][0].startswith("@media"):
+            # manual handling of media queries, becaues our parser is really
+            # simplistic
+            internal_rules = rules_from_media_query(rule)
+            internal_rules = set_vars(component_name, internal_rules)
+            rule = wraps(rule, internal_rules)
+            if len(rule["lines"]) > 2:
+                final_rules.append(rule)
+            continue
+
+        if skip and match(rule, skip):
+            # discard rule entirely
+            continue
+
+        matching = []
+        for s in selectors:
+            if set(s) & set(rule["selectors"]):
+                matching.append(s)
+        if not matching:
+            # no replacements to apply, keep rule as is
+            final_rules.append(rule)
+            continue
+        new_rule = {"lines": []}
+
+        for m in matching:
+            # the block match one of our replacement rules, so we loop on each line
+            # and replace values if needed.
+            replacements = conf[m]
+            for line in rule["lines"]:
+                for property, new_value in replacements:
+                    if line.strip().startswith("{}:".format(property)):
+                        new_property = "{}: {};".format(property, new_value)
+                        indentation = " " * (len(line) - len(line.lstrip(" ")))
+                        line = indentation + new_property
+                        break
+                new_rule["lines"].append(line)
+        final_rules.append(new_rule)
+    return final_rules
+
+
+def parse_rules(text):
+    """
+    Really basic CSS parsers that stores selectors and corresponding properties. Only works
+    because the source files have coma-separated selectors (one per line), and one
+    property/value per line.
+
+    Returns a list of dictionaries, each dictionarry containing the selectors and
+    lines of of each block.
+    """
+    rules = []
+    current_rule = None
+    opened_brackets = 0
+    current_selector = []
+    for line in text.splitlines():
+        if not current_rule and line.endswith(","):
+            current_selector.append(line.rstrip(",").strip())
+        elif line.endswith(" {"):
+            # for media queries
+            opened_brackets += 1
+            if not current_rule:
+                current_selector.append(line.rstrip("{").strip())
+                current_rule = {
+                    "lines": [",\n".join(current_selector) + " {"],
+                    "selectors": current_selector,
+                }
+            else:
+                current_rule["lines"].append(line)
+        elif current_rule:
+            current_rule["lines"].append(line)
+            if line.strip() == "}":
+                opened_brackets -= 1
+                if not opened_brackets:
+                    # move on to next rule
+                    rules.append(current_rule)
+                    current_rule = None
+                    current_selector = []
+
+    return rules
+
+
+def serialize_rules(rules):
+    """
+    Convert rules back to valid CSS.
+    """
+    lines = []
+    for rule in rules:
+        for line in rule["lines"]:
+            lines.append(line)
+
+    return "\n".join(lines)
+
+
+def iter_components(dir):
+    for dname, dirs, files in os.walk(dir):
+        for fname in files:
+            if fname.endswith(".min.css"):
+                continue
+            if fname.endswith(".js"):
+                continue
+            if "semantic" in fname:
+                continue
+            if fname.endswith(".css"):
+                yield os.path.join(dname, fname)
+
+
+def replace_vars(source, dest):
+    components = list(sorted(iter_components(os.path.join(source, "components"))))
+    for c in components:
+        with open(c, "r") as f:
+            text = f.read()
+
+        for s, r in GLOBAL_REPLACES:
+            text = text.replace(s, r)
+            text = text.replace(s.lower(), r)
+            text = text.replace(s.upper(), r)
+        rules = parse_rules(text)
+        name = c.split("/")[-1].split(".")[0]
+        updated_rules = set_vars(name, rules)
+        text = serialize_rules(updated_rules)
+        with open(os.path.join(dest, "{}.css".format(name)), "w") as f:
+            f.write(text)
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="Replace hardcoded values by CSS vars and strip unused rules")
+    parser.add_argument(
+        "source", help="Source path of the fomantic-ui-less distribution to fix"
+    )
+    parser.add_argument(
+        "dest", help="Destination directory where fixed files should be written"
+    )
+    args = parser.parse_args()
+
+    replace_vars(source=args.source, dest=args.dest)
diff --git a/front/scripts/fix-fomantic-css.sh b/front/scripts/fix-fomantic-css.sh
new file mode 100755
index 000000000..0cb6268ef
--- /dev/null
+++ b/front/scripts/fix-fomantic-css.sh
@@ -0,0 +1,8 @@
+#!/bin/bash -eux
+
+find node_modules/fomantic-ui-css/components -name "*.min.css" -delete
+mkdir -p node_modules/fomantic-ui-css/tweaked
+echo 'Removing google font…'
+sed -i '/@import url(/d' node_modules/fomantic-ui-css/components/site.css
+echo "Replacing hardcoded values by CSS vars…"
+scripts/fix-fomantic-css.py node_modules/fomantic-ui-css node_modules/fomantic-ui-css/tweaked
diff --git a/front/src/App.vue b/front/src/App.vue
index 0dec8e783..de76d1c2a 100644
--- a/front/src/App.vue
+++ b/front/src/App.vue
@@ -444,194 +444,4 @@ export default {
 <style lang="scss">
 @import "style/_main";
 
-.ui.bottom-player {
-  z-index: 999999;
-  width: 100%;
-  width: 100vw;
-  .ui.top.attached.progress {
-    top: 0;
-  }
-}
-.dimmed {
-  .ui.bottom-player {
-    @include media("<desktop") {
-      z-index: 0;
-    }
-  }
-}
-#app.queue-focused {
-  .queue-not-focused {
-    @include media("<desktop") {
-      display: none;
-    }
-  }
-}
-.when-queue-focused {
-  .group {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    font-size: 1.1em;
-    > * {
-      margin-left: 0.5em;
-    }
-  }
-  @include media("<desktop") {
-    width: 100%;
-    justify-content: space-between !important;
-  }
-}
-#app:not(.queue-focused) {
-  .when-queue-focused {
-    @include media("<desktop") {
-      display: none;
-    }
-  }
-}
-.ui.bottom-player > .segment.fixed-controls {
-  width: 100%;
-  width: 100vw;
-  border-radius: 0;
-  padding: 0em;
-  position: fixed;
-  bottom: 0;
-  left: 0;
-  margin: 0;
-  z-index: 1001;
-  height: $bottom-player-height;
-  .controls-row {
-    height: $bottom-player-height;
-    margin: 0 auto;
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    @include media(">desktop") {
-      padding: 0 1em;
-      justify-content: space-around;
-    }
-  }
-  cursor: pointer;
-  .indicating.progress {
-    overflow: hidden;
-  }
-
-  .ui.progress .bar {
-    transition: none;
-  }
-
-  .ui.progress .buffer.bar {
-    position: absolute;
-  }
-
-  @keyframes MOVE-BG {
-    from {
-      transform: translateX(0px);
-    }
-    to {
-      transform: translateX(46px);
-    }
-  }
-  .discrete.link {
-    color: inherit;
-  }
-  .indicating.progress .bar {
-    left: -46px;
-    width: 200% !important;
-    color: grey;
-    background: repeating-linear-gradient(
-      -55deg,
-      grey 1px,
-      grey 10px,
-      transparent 10px,
-      transparent 20px
-    ) !important;
-
-    animation-name: MOVE-BG;
-    animation-duration: 2s;
-    animation-timing-function: linear;
-    animation-iteration-count: infinite;
-  }
-  .ui.progress:not([data-percent]):not(.indeterminate)
-    .bar.position:not(.buffer) {
-    background: #ff851b;
-    min-width: 0;
-  }
-
-  .track-controls {
-    display: flex;
-    align-items: center;
-    justify-content: start;
-    flex-grow: 1;
-    .image {
-      padding: 0.5em;
-      width: auto;
-      margin-right: 0.5em;
-      > img {
-        max-height: 3.7em;
-        max-width: 4.7em;
-      }
-    }
-  }
-  .controls {
-    min-width: 8em;
-    font-size: 1.1em;
-    @include media(">desktop") {
-      &:not(.fluid) {
-        width: 20%;
-      }
-      &.queue-controls {
-        width: 32.5%;
-      }
-      &.progress-controls {
-        width: 10%;
-      }
-      &.player-controls {
-        width: 15%;
-      }
-    }
-    &.small, .small {
-      @include media(">desktop") {
-        font-size: 0.9em;
-      }
-    }
-    .icon {
-      font-size: 1.1em;
-    }
-    .icon.large {
-      font-size: 1.4em;
-    }
-    &:not(.track-controls) {
-      @include media(">desktop") {
-        line-height: 1em;
-      }
-      justify-content: center;
-      align-items: center;
-      &.align-right {
-        justify-content: flex-end;
-      }
-      &.align-left {
-        justify-content: flex-start;
-      }
-      > * {
-        padding: 0.5em;
-      }
-    }
-    &.player-controls {
-      .icon {
-        margin: 0;
-      }
-    }
-
-  }
-}
-.queue-enter-active, .queue-leave-active {
-  transition: all 0.2s ease-in-out;
-  .current-track, .queue-column {
-    opacity: 0;
-  }
-}
-.queue-enter, .queue-leave-to {
-  transform: translateY(100vh);
-  opacity: 0;
-}
 </style>
diff --git a/front/src/components/About.vue b/front/src/components/About.vue
index ae8fd24dc..8dac106de 100644
--- a/front/src/components/About.vue
+++ b/front/src/components/About.vue
@@ -1,5 +1,5 @@
 <template>
-  <main class="main pusher">
+  <main class="main pusher page-about">
     <section :class="['ui', 'head', {'with-background': banner}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle">
       <div class="segment-content">
         <h1 class="ui center aligned large header">
@@ -173,22 +173,22 @@
                   <translate translate-context="Content/Home/Header">Statistics</translate>
                 </h3>
                 <p>
-                  <i class="user grey icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: stats.users.toLocaleString($store.state.ui.momentLocale) }" :translate-n="stats.users" translate-plural="%{ count } active users">%{ count } active user</translate>
+                  <i class="user really discrete icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: stats.users.toLocaleString($store.state.ui.momentLocale) }" :translate-n="stats.users" translate-plural="%{ count } active users">%{ count } active user</translate>
                 </p>
                 <p>
-                  <i class="music grey icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: parseInt(stats.hours).toLocaleString($store.state.ui.momentLocale)}" :translate-n="parseInt(stats.hours)" translate-plural="%{ count } hours of music">%{ count } hour of music</translate>
+                  <i class="music really discrete icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: parseInt(stats.hours).toLocaleString($store.state.ui.momentLocale)}" :translate-n="parseInt(stats.hours)" translate-plural="%{ count } hours of music">%{ count } hour of music</translate>
                 </p>
                 <p v-if="stats.artists">
-                  <i class="users grey icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: stats.artists.toLocaleString($store.state.ui.momentLocale) }" :translate-n="stats.artists" translate-plural="%{ count } artists">%{ count } artists</translate>
+                  <i class="users really discrete icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: stats.artists.toLocaleString($store.state.ui.momentLocale) }" :translate-n="stats.artists" translate-plural="%{ count } artists">%{ count } artists</translate>
                 </p>
                 <p v-if="stats.albums">
-                  <i class="headphones grey icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: stats.albums.toLocaleString($store.state.ui.momentLocale) }" :translate-n="stats.albums" translate-plural="%{ count } albums">%{ count } albums</translate>
+                  <i class="headphones really discrete icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: stats.albums.toLocaleString($store.state.ui.momentLocale) }" :translate-n="stats.albums" translate-plural="%{ count } albums">%{ count } albums</translate>
                 </p>
                 <p v-if="stats.tracks">
-                  <i class="file grey icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: stats.tracks.toLocaleString($store.state.ui.momentLocale) }" :translate-n="stats.tracks" translate-plural="%{ count } tracks">%{ count } tracks</translate>
+                  <i class="file really discrete icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: stats.tracks.toLocaleString($store.state.ui.momentLocale) }" :translate-n="stats.tracks" translate-plural="%{ count } tracks">%{ count } tracks</translate>
                 </p>
                 <p v-if="stats.listenings">
-                  <i class="play grey icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: stats.listenings.toLocaleString($store.state.ui.momentLocale) }" :translate-n="stats.listenings" translate-plural="%{ count } listenings">%{ count } listenings</translate>
+                  <i class="play really discrete icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: stats.listenings.toLocaleString($store.state.ui.momentLocale) }" :translate-n="stats.listenings" translate-plural="%{ count } listenings">%{ count } listenings</translate>
                 </p>
               </template>
             </div>
@@ -285,38 +285,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped lang="scss">
-
-.ui.list .list.icon {
-  padding: 0;
-}
-
-h1.header, h1 .sub.header {
-  text-shadow: 0 2px 0 rgba(0,0,0,.8);
-  color: #fff !important;
-}
-h1.ui.header {
-  font-size: 3em;
-}
-h1.ui.header .sub.header {
-  font-size: 0.8em;
-}
-.main.pusher {
-  margin-top: 0;
-  min-height: 10em;
-}
-section.segment.head {
-  padding: 8em 3em;
-  background: linear-gradient(90deg, rgba(40,88,125,1) 0%, rgba(64,130,180,1) 100%);
-  background-repeat: no-repeat;
-  background-size: cover;
-}
-#pod {
-  font-size: 110%;
-  display: block;
-}
-</style>
diff --git a/front/src/components/Footer.vue b/front/src/components/Footer.vue
index 6e9f76167..7634debdb 100644
--- a/front/src/components/Footer.vue
+++ b/front/src/components/Footer.vue
@@ -9,16 +9,16 @@
           <h4 v-else class="ui header ellipsis">
             <span v-translate="{instanceUrl: instanceHostname}" translate-context="Footer/About/Title">About %{instanceUrl}</span>
           </h4>
-          <div class="ui link list">
-            <router-link class="item" to="/about">
+          <div class="ui list">
+            <router-link class="link item" to="/about">
               <translate translate-context="Footer/About/List item.Link">About page</translate>
             </router-link>
-              <a v-if="version" class="item" href="https://docs.funkwhale.audio/changelog.html" target="_blank">
+              <a v-if="version" class="link item" href="https://docs.funkwhale.audio/changelog.html" target="_blank">
                 <translate translate-context="Footer/*/List item" :translate-params="{version: version}" >Version %{version}</translate>
               </a>
-            <div role="button" class="item" @click="$emit('show:set-instance-modal')" >
+            <a role="button" class="link item" @click.prevent="$emit('show:set-instance-modal')" >
               <translate translate-context="Footer/*/List item.Link">Use another instance</translate>
-            </div>
+            </a>
           </div>
           <div class="ui form">
             <div class="ui field">
@@ -31,10 +31,10 @@
         </section>
         <section class="four wide column">
           <h4 v-translate class="ui header" translate-context="Footer/*/Title">Using Funkwhale</h4>
-          <div class="ui link list">
-            <a href="https://docs.funkwhale.audio" class="item" target="_blank"><translate translate-context="Footer/*/List item.Link/Short, Noun">Documentation</translate></a>
-            <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 class="ui list">
+            <a href="https://docs.funkwhale.audio" class="link item" target="_blank"><translate translate-context="Footer/*/List item.Link/Short, Noun">Documentation</translate></a>
+            <a href="https://funkwhale.audio/apps" class="link item" target="_blank"><translate translate-context="Footer/*/List item.Link">Mobile and desktop apps</translate></a>
+            <a role="button" class="link item" @click.prevent="$emit('show:shortcuts-modal')"><translate translate-context="*/*/*/Noun">Keyboard shortcuts</translate></a>
           </div>
           <div class="ui form">
             <div class="ui field">
@@ -47,18 +47,18 @@
         </section>
         <section class="four wide column">
           <h4 v-translate translate-context="Footer/*/Link" class="ui header">Getting help</h4>
-          <div class="ui link list">
-            <a href="https://governance.funkwhale.audio/g/kQgxNq15/funkwhale" class="item" target="_blank"><translate translate-context="Footer/*/Listitem.Link">Support forum</translate></a>
-            <a href="https://riot.im/app/#/room/#funkwhale-troubleshooting:matrix.org" class="item" target="_blank"><translate translate-context="Footer/*/List item.Link">Chat room</translate></a>
-            <a href="https://dev.funkwhale.audio/funkwhale/funkwhale/issues" class="item" target="_blank"><translate translate-context="Footer/*/List item.Link">Issue tracker</translate></a>
+          <div class="ui list">
+            <a href="https://governance.funkwhale.audio/g/kQgxNq15/funkwhale" class="link item" target="_blank"><translate translate-context="Footer/*/Listitem.Link">Support forum</translate></a>
+            <a href="https://riot.im/app/#/room/#funkwhale-troubleshooting:matrix.org" class="link item" target="_blank"><translate translate-context="Footer/*/List item.Link">Chat room</translate></a>
+            <a href="https://dev.funkwhale.audio/funkwhale/funkwhale/issues" class="link item" target="_blank"><translate translate-context="Footer/*/List item.Link">Issue tracker</translate></a>
           </div>
         </section>
         <section class="four wide column">
           <h4 v-translate class="ui header" translate-context="Footer/*/Title/Short">About Funkwhale</h4>
-          <div class="ui link list">
-            <a href="https://funkwhale.audio" class="item" target="_blank"><translate translate-context="Footer/*/List item.Link">Official website</translate></a>
-            <a href="https://contribute.funkwhale.audio" class="item" target="_blank"><translate translate-context="Footer/*/List item.Link">Contribute</translate></a>
-            <a href="https://dev.funkwhale.audio/funkwhale/funkwhale" class="item" target="_blank"><translate translate-context="Footer/*/List item.Link">Source code</translate></a>
+          <div class="ui list">
+            <a href="https://funkwhale.audio" class="link item" target="_blank"><translate translate-context="Footer/*/List item.Link">Official website</translate></a>
+            <a href="https://contribute.funkwhale.audio" class="link item" target="_blank"><translate translate-context="Footer/*/List item.Link">Contribute</translate></a>
+            <a href="https://dev.funkwhale.audio/funkwhale/funkwhale" class="link item" target="_blank"><translate translate-context="Footer/*/List item.Link">Source code</translate></a>
           </div>
           <div class="ui hidden divider"></div>
           <p>
diff --git a/front/src/components/Home.vue b/front/src/components/Home.vue
index dfa040ea8..2d010bb5e 100644
--- a/front/src/components/Home.vue
+++ b/front/src/components/Home.vue
@@ -1,5 +1,5 @@
 <template>
-  <main class="main pusher" v-title="labels.title">
+  <main class="main pusher page-home" v-title="labels.title">
     <section :class="['ui', 'head', {'with-background': banner}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle">
       <div class="segment-content">
         <h1 class="ui center aligned large header">
@@ -32,7 +32,7 @@
                   <div v-if="truncatedDescription" class="ui hidden divider"></div>
                   <div class="ui relaxed list">
                     <div class="item" v-if="truncatedDescription">
-                      <i class="arrow right grey icon"></i>
+                      <i class="arrow right icon"></i>
                       <div class="content">
                         <router-link class="ui link" :to="{name: 'about'}">
                           <translate translate-context="Content/Home/Link">Learn more</translate>
@@ -40,7 +40,7 @@
                       </div>
                     </div>
                     <div class="item" v-if="rules">
-                      <i class="book open grey icon"></i>
+                      <i class="book open icon"></i>
                       <div class="content">
                         <router-link class="ui link" v-if="rules" :to="{name: 'about', hash: '#rules'}">
                           <translate translate-context="Content/Home/Link">Server rules</translate>
@@ -56,10 +56,10 @@
                     <translate translate-context="Content/Home/Header">Statistics</translate>
                   </h3>
                   <p>
-                    <i class="user grey icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: stats.users.toLocaleString($store.state.ui.momentLocale) }" :translate-n="stats.users" translate-plural="%{ count } active users">%{ count } active user</translate>
+                    <i class="user icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: stats.users.toLocaleString($store.state.ui.momentLocale) }" :translate-n="stats.users" translate-plural="%{ count } active users">%{ count } active user</translate>
                   </p>
                   <p>
-                    <i class="music grey icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: parseInt(stats.hours).toLocaleString($store.state.ui.momentLocale)}" :translate-n="parseInt(stats.hours)" translate-plural="%{ count } hours of music">%{ count } hour of music</translate>
+                    <i class="music icon"></i><translate translate-context="Content/Home/Stat" :translate-params="{count: parseInt(stats.hours).toLocaleString($store.state.ui.momentLocale)}" :translate-n="parseInt(stats.hours)" translate-plural="%{ count } hours of music">%{ count } hour of music</translate>
                   </p>
 
                 </template>
@@ -67,7 +67,7 @@
                   <h3 class="sub header">
                     <translate translate-context="Content/Home/Header/Name">Contact</translate>
                   </h3>
-                  <i class="at grey icon"></i>
+                  <i class="at icon"></i>
                   <a :href="`mailto:${contactEmail}`">{{ contactEmail }}</a>
                 </template>
 
@@ -98,7 +98,7 @@
           <h3 class="header">
             <translate translate-context="Head/Login/Title">Log In</translate>
           </h3>
-          <login-form button-classes="basic green" :show-signup="false"></login-form>
+          <login-form button-classes="basic success" :show-signup="false"></login-form>
           <div class="ui hidden clearing divider"></div>
         </div>
         <div class="four wide column">
@@ -112,7 +112,7 @@
             <p v-if="defaultUploadQuota">
               <translate translate-context="Content/Home/Paragraph" :translate-params="{quota: humanSize(defaultUploadQuota * 1000 * 1000)}">Users on this pod also get %{ quota } of free storage to upload their own content!</translate>
             </p>
-            <signup-form button-classes="basic green" :show-login="false"></signup-form>
+            <signup-form button-classes="basic success" :show-login="false"></signup-form>
           </template>
           <div v-else>
             <p translate-context="Content/Home/Paragraph">Registrations are closed on this pod. You can signup on another pod using the link below.</p>
@@ -288,39 +288,3 @@ export default {
 
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped lang="scss">
-@import "../style/vendor/media";
-
-.ui.list .list.icon {
-  padding: 0;
-}
-
-h1.header, h1 .sub.header {
-  text-shadow: 1px 1px 2px rgba(0,0,0,.8);
-  color: #fff !important;
-}
-h1.ui.header {
-  @include media(">tablet") {
-    font-size: 3em;
-  }
-}
-h1.ui.header .sub.header {
-  font-size: 0.8em;
-}
-.main.pusher {
-  margin-top: 0;
-  min-height: 10em;
-}
-section.segment.head {
-  padding: 8em 3em;
-  background: linear-gradient(90deg, rgba(40,88,125,1) 0%, rgba(64,130,180,1) 100%);
-  background-repeat: no-repeat;
-  background-size: cover;
-}
-#pod {
-  font-size: 110%;
-  display: block;
-}
-</style>
diff --git a/front/src/components/PageNotFound.vue b/front/src/components/PageNotFound.vue
index 4dce665bb..274a5798c 100644
--- a/front/src/components/PageNotFound.vue
+++ b/front/src/components/PageNotFound.vue
@@ -36,7 +36,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/Pagination.vue b/front/src/components/Pagination.vue
index b8733f278..f87c84e93 100644
--- a/front/src/components/Pagination.vue
+++ b/front/src/components/Pagination.vue
@@ -1,5 +1,5 @@
 <template>
-  <div v-if='maxPage > 1' class="ui pagination menu" role="navigation" :aria-label="labels.pagination">
+  <div v-if='maxPage > 1' class="ui pagination menu component-pagination" role="navigation" :aria-label="labels.pagination">
     <a href
       :disabled="current - 1 < 1"
       @click.prevent.stop="selectPage(current - 1)"
@@ -95,10 +95,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-.ui.pagination.menu .item {
-  cursor: pointer;
-}
-</style>
diff --git a/front/src/components/Queue.vue b/front/src/components/Queue.vue
index 141b1d2d7..daf9c1657 100644
--- a/front/src/components/Queue.vue
+++ b/front/src/components/Queue.vue
@@ -1,9 +1,9 @@
 <template>
-  <section class="main with-background" :aria-label="labels.queue">
+  <section class="main with-background component-queue" :aria-label="labels.queue">
     <div :class="['ui vertical stripe queue segment', playerFocused ? 'player-focused' : '']">
       <div class="ui fluid container">
         <div class="ui stackable grid" id="queue-grid">
-                    <div class="ui six wide column current-track">
+          <div class="ui six wide column current-track">
             <div class="ui basic segment" id="player">
               <template v-if="currentTrack">
                 <img class="ui image" v-if="currentTrack.album && currentTrack.album.cover && currentTrack.album.cover.original" :src="$store.getters['instance/absoluteUrl'](currentTrack.album.cover.square_crop)">
@@ -55,7 +55,7 @@
                   <div class="progress-area" v-if="currentTrack && !errored">
                     <div
                       ref="progress"
-                      :class="['ui', 'small', 'orange', {'indicating': isLoadingAudio}, 'progress']"
+                      :class="['ui', 'small', 'vibrant', {'indicating': isLoadingAudio}, 'progress']"
                       @click="touchProgress">
                       <div class="buffer bar" :data-percent="bufferProgress" :style="{ 'width': bufferProgress + '%' }"></div>
                       <div class="position bar" :data-percent="progress" :style="{ 'width': progress + '%' }"></div>
@@ -64,7 +64,7 @@
                   <div class="progress-area" v-else>
                     <div
                       ref="progress"
-                      :class="['ui', 'small', 'orange', 'progress']">
+                      :class="['ui', 'small', 'vibrant', 'progress']">
                       <div class="buffer bar"></div>
                       <div class="position bar"></div>
                     </div>
@@ -124,7 +124,7 @@
               </template>
             </div>
           </div>
-          <div class="ui sixteen wide mobile ten wide computer column queue-column">
+          <div class="ui ten wide column queue-column">
             <div class="ui basic clearing fixed-header segment">
               <h2 class="ui header">
                 <div class="content">
@@ -155,7 +155,7 @@
                   :key="index"
                   :class="['queue-item', {'active': index === queue.currentIndex}]">
                   <td class="handle">
-                    <i class="grip lines grey icon"></i>
+                    <i class="grip lines icon"></i>
                   </td>
                   <td class="image-cell" @click="$store.dispatch('queue/currentIndex', index)">
                     <img class="ui mini image" v-if="track.album && track.album.cover && track.album.cover.original" :src="$store.getters['instance/absoluteUrl'](track.album.cover.square_crop)">
@@ -374,204 +374,3 @@ export default {
   }
 }
 </script>
-<style lang="scss" scoped>
-@import "../style/vendor/media";
-
-.main {
-  position: absolute;
-  min-height: 100vh;
-  width: 100vw;
-  z-index: 1000;
-  padding-bottom: 3em;
-}
-.main > .button {
-  position: fixed;
-  top: 1em;
-  right: 1em;
-  z-index: 9999999;
-  @include media("<desktop") {
-    display: none;
-  }
-}
-.queue.segment:not(.player-focused) {
-  #player {
-    @include media("<desktop") {
-      height: 0;
-      display: none;
-    }
-  }
-}
-.queue.segment #player {
-  padding: 0em;
-  > * {
-    padding: 0.5em;
-  }
-}
-.player-focused .grid > .ui.queue-column {
-  @include media("<desktop") {
-    display: none;
-  }
-}
-.queue-column {
-  overflow-y: auto;
-}
-.queue-column .table {
-  margin-top: 4em !important;
-  margin-bottom: 4rem;
-}
-.ui.table > tbody > tr > td.controls {
-  text-align: right;
-}
-.ui.table > tbody > tr > td {
-  border: none;
-}
-td:first-child {
-  padding-left: 1em !important;
-}
-td:last-child {
-  padding-right: 1em !important;
-}
-.image-cell {
-  width: 4em;
-}
-.queue.segment {
-  @include media("<desktop") {
-    padding: 0;
-  }
-  > .container {
-    margin: 0 !important;
-  }
-}
-.handle {
-  @include media("<desktop") {
-    display: none;
-  }
-}
-.duration-cell {
-  @include media("<tablet") {
-    display: none;
-  }
-}
-.fixed-header {
-  position: fixed;
-  right: 0;
-  left: 0;
-  top: 0;
-  z-index: 9;
-  @include media("<desktop") {
-    padding: 1em;
-  }
-  @include media(">desktop") {
-    right: 1em;
-    left: 38%;
-  }
-  .header .content {
-    display: block;
-  }
-}
-.current-track #player {
-  font-size: 1.8em;
-  padding: 1em;
-  text-align: center;
-  display: flex;
-  position: fixed;
-  height: 100vh;
-  align-items: center;
-  justify-content: center;
-  flex-direction: column;
-  bottom: 0;
-  top: 0;
-  width: 32%;
-  @include media("<desktop") {
-    padding: 0.5em;
-    font-size: 1.5em;
-    width: 100%;
-    width: 100vw;
-    left: 0;
-    right: 0;
-    > .image {
-      max-height: 50vh;
-    }
-  }
-  > *:not(.image) {
-    width: 100%;
-  }
-  h1 {
-    margin: 0;
-    min-height: auto;
-  }
-}
-.progress-area {
-  overflow: hidden;
-}
-.progress-wrapper, .warning.message {
-  max-width: 25em;
-  margin: 0 auto;
-}
-.ui.progress .buffer.bar {
-  position: absolute;
-  background-color: rgba(255, 255, 255, 0.15);
-}
-.ui.progress:not([data-percent]):not(.indeterminate)
-  .bar.position:not(.buffer) {
-  background: #ff851b;
-}
-
-.indicating.progress .bar {
-  left: -46px;
-  width: 200% !important;
-  color: grey;
-  background: repeating-linear-gradient(
-    -55deg,
-    grey 1px,
-    grey 10px,
-    transparent 10px,
-    transparent 20px
-  ) !important;
-
-  animation-name: MOVE-BG;
-  animation-duration: 2s;
-  animation-timing-function: linear;
-  animation-iteration-count: infinite;
-}
-.ui.progress {
-  margin: 0.5rem 0;
-}
-.timer {
-  font-size: 0.7em;
-}
-.progress {
-  cursor: pointer;
-  .bar {
-    min-width: 0 !important;
-  }
-}
-
-.player-controls {
-  .control:not(:first-child) {
-    margin-left: 1em;
-  }
-  .icon {
-    font-size: 1.1em;
-  }
-}
-
-.handle {
-  cursor: grab;
-}
-.sortable-chosen {
-  cursor: grabbing;
-}
-.queue-item.sortable-ghost {
-  td {
-    border-top: 3px dashed rgba(0, 0, 0, 0.15) !important;
-    border-bottom: 3px dashed rgba(0, 0, 0, 0.15) !important;
-    &:first-child {
-      border-left: 3px dashed rgba(0, 0, 0, 0.15) !important;
-    }
-    &:last-child {
-      border-right: 3px dashed rgba(0, 0, 0, 0.15) !important;
-    }
-  }
-}
-</style>
diff --git a/front/src/components/SetInstanceModal.vue b/front/src/components/SetInstanceModal.vue
index a5ff00f8a..5cc0ea41c 100644
--- a/front/src/components/SetInstanceModal.vue
+++ b/front/src/components/SetInstanceModal.vue
@@ -142,6 +142,3 @@ export default {
   },
 }
 </script>
-
-<style scoped>
-</style>
diff --git a/front/src/components/ShortcutsModal.vue b/front/src/components/ShortcutsModal.vue
index 8f3f41bab..017cdc977 100644
--- a/front/src/components/ShortcutsModal.vue
+++ b/front/src/components/ShortcutsModal.vue
@@ -157,7 +157,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/Sidebar.vue b/front/src/components/Sidebar.vue
index 7d3f5a2df..a35614a23 100644
--- a/front/src/components/Sidebar.vue
+++ b/front/src/components/Sidebar.vue
@@ -1,8 +1,8 @@
 <template>
-<aside :class="['ui', 'vertical', 'left', 'visible', 'wide', {'collapsed': isCollapsed}, 'sidebar',]">
+<aside :class="['ui', 'vertical', 'left', 'visible', 'wide', {'collapsed': isCollapsed}, 'sidebar', 'component-sidebar']">
   <header class="ui basic segment header-wrapper">
     <router-link :title="'Funkwhale'" :to="{name: logoUrl}">
-      <i class="logo bordered inverted orange big icon">
+      <i class="logo bordered inverted vibrant big icon">
         <logo class="logo"></logo>
       </i>
     </router-link>
@@ -88,7 +88,7 @@
 
         <span
           @click="isCollapsed = !isCollapsed"
-          :class="['ui', 'basic', 'big', {'orange': !isCollapsed}, 'inverted icon', 'collapse', 'button']">
+          :class="['ui', 'basic', 'big', {'vibrant': !isCollapsed}, 'inverted icon', 'collapse', 'button']">
             <i class="sidebar icon"></i></span>
       </div>
     </nav>
@@ -343,239 +343,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped lang="scss">
-@import "../style/vendor/media";
-
-$sidebar-color: #2D2F33;
-
-.sidebar {
-  background: $sidebar-color;
-  z-index: 1;
-  @include media(">desktop") {
-    display: flex;
-    flex-direction: column;
-    justify-content: space-between;
-    padding-bottom: 4em;
-  }
-  > nav {
-    flex-grow: 1;
-    overflow-y: auto;
-  }
-  @include media(">desktop") {
-    .menu .item.collapse-button-wrapper {
-      padding: 0;
-    }
-    .collapse.button {
-      display: none !important;
-    }
-  }
-  @include media("<=desktop") {
-    position: static !important;
-    width: 100% !important;
-    &.collapsed {
-      .player-wrapper,
-      .search,
-      .signup.segment,
-      nav.secondary {
-        display: none;
-      }
-    }
-  }
-
-  > div {
-    margin: 0;
-    background-color: $sidebar-color;
-  }
-  .menu.vertical {
-    background: $sidebar-color;
-  }
-}
-
-.ui.vertical.menu {
-  .item .item {
-    font-size: 1em;
-    > i.icon {
-      float: none;
-      margin: 0 0.5em 0 0;
-    }
-    &:not(.active) {
-      // color: rgba(255, 255, 255, 0.75);
-    }
-  }
-  .item.active {
-    border-right: 5px solid #F2711C;
-    border-radius: 0 !important;
-    background-color: rgba(255, 255, 255, 0.15) !important;
-  }
-  .item.collapsed {
-    &:not(:focus) > .menu {
-      display: none;
-    }
-    .header {
-      margin-bottom: 0;
-    }
-  }
-  .collaspable.item .header {
-    cursor: pointer;
-  }
-}
-.ui.secondary.menu {
-  margin-left: 0;
-  margin-right: 0;
-}
-.tabs {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  overflow-y: auto;
-  justify-content: space-between;
-  @include media("<=desktop") {
-    max-height: 500px;
-  }
-}
-.ui.tab.active {
-  display: flex;
-}
-.tab[data-tab="queue"] {
-  flex-direction: column;
-  tr {
-    cursor: pointer;
-  }
-  td:nth-child(2) {
-    width: 55px;
-  }
-}
-.item .header .angle.icon {
-  float: right;
-  margin: 0;
-}
-.tab[data-tab="library"] {
-  flex-direction: column;
-  flex: 1 1 auto;
-  > .menu {
-    flex: 1;
-    flex-grow: 1;
-  }
-  > .player-wrapper {
-    width: 100%;
-  }
-}
-.sidebar .segment {
-  margin: 0;
-  border-radius: 0;
-}
-
-.ui.menu .item.inline.admin-dropdown.dropdown > .menu {
-  left: 0;
-  right: auto;
-}
-.ui.segment.header-wrapper {
-  padding: 0;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  height: 4em;
-  nav {
-    > .item, > .menu > .item > .item {
-      &:hover {
-        background-color: transparent;
-      }
-    }
-  }
-}
-
-nav.top.title-menu {
-  flex-grow: 1;
-  .item {
-    font-size: 1.5em;
-  }
-}
-
-.logo {
-  cursor: pointer;
-  display: inline-block;
-  margin: 0px;
-}
-
-.collapsed .search-wrapper {
-  @include media("<desktop") {
-    padding: 0;
-  }
-}
-.ui.search {
-  display: flex;
-}
-.ui.message.black {
-  background: $sidebar-color;
-}
-
-.ui.mini.image {
-  width: 100%;
-}
-nav.top {
-  align-items: self-end;
-  padding: 0.5em 0;
-  > .item, > .right.menu > .item {
-    // color: rgba(255, 255, 255, 0.9) !important;
-    font-size: 1.2em;
-    &:hover, > .dropdown > .icon {
-      // color: rgba(255, 255, 255, 0.9) !important;
-    }
-    > .label, > .dropdown > .label {
-      font-size: 0.5em;
-      right: 1.7em;
-      bottom: -0.5em;
-      z-index: 0 !important;
-    }
-  }
-}
-.ui.user-dropdown > .text > .label {
-  margin-right: 0;
-}
-.logo-wrapper {
-  display: inline-block;
-  margin: 0 auto;
-  @include media("<desktop") {
-    margin: 0;
-  }
-  img {
-    height: 1em;
-    display: inline-block;
-    margin: 0 auto;
-  }
-  @include media(">tablet") {
-    img {
-      height: 1.5em;
-    }
-  }
-}
-</style>
-
-<style lang="scss">
-aside.ui.sidebar {
-  overflow-y: visible !important;
-  .ui.search .input {
-    flex: 1;
-    .prompt {
-      border-radius: 0;
-    }
-  }
-  .ui.search .results {
-    vertical-align: middle;
-  }
-  .ui.search .name {
-    vertical-align: middle;
-  }
-}
-.ui.tiny.avatar.image {
-  position: relative;
-  top: -0.5em;
-  width: 3em;
-}
-
-:not(.active) button.title {
-  outline-color: white;
-}
-</style>
diff --git a/front/src/components/admin/SettingsGroup.vue b/front/src/components/admin/SettingsGroup.vue
index 2362d046e..2511bc920 100644
--- a/front/src/components/admin/SettingsGroup.vue
+++ b/front/src/components/admin/SettingsGroup.vue
@@ -1,5 +1,5 @@
 <template>
-  <form :id="group.id" class="ui form" @submit.prevent="save">
+  <form :id="group.id" class="ui form component-settings-group" @submit.prevent="save">
     <div class="ui divider" />
     <h3 class="ui header">{{ group.label }}</h3>
     <div v-if="errors.length > 0" class="ui negative message">
@@ -78,7 +78,7 @@
     </div>
     <button
       type="submit"
-      :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']">
+      :class="['ui', {'loading': isLoading}, 'right', 'floated', 'success', 'button']">
         <translate translate-context="Content/*/Button.Label/Verb">Save</translate>
     </button>
   </form>
@@ -176,10 +176,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-.ui.checkbox p {
-  margin-top: 1rem;
-}
-</style>
diff --git a/front/src/components/audio/AlbumEntries.vue b/front/src/components/audio/AlbumEntries.vue
index c9218b33a..a7658d5a0 100644
--- a/front/src/components/audio/AlbumEntries.vue
+++ b/front/src/components/audio/AlbumEntries.vue
@@ -2,7 +2,7 @@
   <div class="album-entries">
     <div :class="[{active: currentTrack && isPlaying && track.id === currentTrack.id}, 'album-entry']" v-for="track in tracks" :key="track.id">
       <div class="actions">
-        <play-button class="basic circular icon" :button-classes="['circular inverted orange icon button']" :discrete="true" :icon-only="true" :track="track"></play-button>
+        <play-button class="basic circular icon" :button-classes="['circular inverted vibrant icon button']" :discrete="true" :icon-only="true" :track="track"></play-button>
       </div>
       <div class="position">{{ prettyPosition(track.position) }}</div>
       <div class="content ellipsis">
diff --git a/front/src/components/audio/ChannelCard.vue b/front/src/components/audio/ChannelCard.vue
index 99b7e63ae..6b5d9afcb 100644
--- a/front/src/components/audio/ChannelCard.vue
+++ b/front/src/components/audio/ChannelCard.vue
@@ -3,7 +3,7 @@
     <div
       @click="$router.push({name: 'channels.detail', params: {id: urlId}})"
       :class="['ui', 'head-image', {'circular': object.artist.content_category != 'podcast'}, {'padded': object.artist.content_category === 'podcast'}, 'image', {'default-cover': !object.artist.cover}]" v-lazy:background-image="imageUrl">
-      <play-button :icon-only="true" :is-playable="true" :button-classes="['ui', 'circular', 'large', 'orange', 'icon', 'button']" :artist="object.artist"></play-button>
+      <play-button :icon-only="true" :is-playable="true" :button-classes="['ui', 'circular', 'large', 'vibrant', 'icon', 'button']" :artist="object.artist"></play-button>
     </div>
     <div class="content">
       <strong>
@@ -37,7 +37,7 @@
         class="right floated basic icon"
         :dropdown-only="true"
         :is-playable="true"
-        :dropdown-icon-classes="['ellipsis', 'horizontal', 'large', 'grey']" :artist="object.artist" :channel="object" :account="object.attributed_to"></play-button>
+        :dropdown-icon-classes="['ellipsis', 'horizontal', 'large really discrete']" :artist="object.artist" :channel="object" :account="object.attributed_to"></play-button>
     </div>
   </div>
 </template>
@@ -58,14 +58,9 @@ export default {
   },
   computed: {
     imageUrl () {
-      let url = '../../assets/audio/default-cover.png'
-
       if (this.object.artist.cover) {
-        url = this.$store.getters['instance/absoluteUrl'](this.object.artist.cover.medium_square_crop)
-      } else {
-        return null
+        return this.$store.getters['instance/absoluteUrl'](this.object.artist.cover.medium_square_crop)
       }
-      return url
     },
     urlId () {
       if (this.object.actor && this.object.actor.is_local) {
diff --git a/front/src/components/audio/ChannelEntryCard.vue b/front/src/components/audio/ChannelEntryCard.vue
index ade7f892c..430f205f4 100644
--- a/front/src/components/audio/ChannelEntryCard.vue
+++ b/front/src/components/audio/ChannelEntryCard.vue
@@ -1,7 +1,7 @@
 <template>
   <div :class="[{active: currentTrack && isPlaying && entry.id === currentTrack.id}, 'channel-entry-card']">
     <div class="controls">
-      <play-button class="basic circular icon" :discrete="true" :icon-only="true" :is-playable="true" :button-classes="['ui', 'circular', 'inverted orange', 'icon', 'button']" :track="entry"></play-button>
+      <play-button class="basic circular icon" :discrete="true" :icon-only="true" :is-playable="true" :button-classes="['ui', 'circular', 'inverted vibrant', 'icon', 'button']" :track="entry"></play-button>
     </div>
     <img
       @click="$router.push({name: 'library.tracks.detail', params: {id: entry.id}})"
@@ -62,16 +62,6 @@ export default {
     isPlaying () {
       return this.$store.state.player.playing
     },
-    imageUrl () {
-      let url = '../../assets/audio/default-cover.png'
-      let cover = this.cover
-      if (cover && cover.original) {
-        url = this.$store.getters['instance/absoluteUrl'](cover.medium_square_crop)
-      } else {
-        return null
-      }
-      return url
-    },
     cover () {
       if (this.entry.cover) {
         return this.entry.cover
@@ -88,7 +78,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/audio/ChannelSerieCard.vue b/front/src/components/audio/ChannelSerieCard.vue
index aa27a8b0f..5a43e02dc 100644
--- a/front/src/components/audio/ChannelSerieCard.vue
+++ b/front/src/components/audio/ChannelSerieCard.vue
@@ -22,7 +22,7 @@
       </div>
     </div>
     <div class="controls">
-      <play-button :icon-only="true" :is-playable="true" :button-classes="['ui', 'circular', 'orange', 'icon', 'button']" :album="serie"></play-button>
+      <play-button :icon-only="true" :is-playable="true" :button-classes="['ui', 'circular', 'vibrant', 'icon', 'button']" :album="serie"></play-button>
     </div>
   </div>
 </template>
@@ -36,16 +36,6 @@ export default {
     PlayButton,
   },
   computed: {
-    imageUrl () {
-      let url = '../../assets/audio/default-cover.png'
-      let cover = this.cover
-      if (cover && cover.original) {
-        url = this.$store.getters['instance/absoluteUrl'](cover.medium_square_crop)
-      } else {
-        return null
-      }
-      return url
-    },
     cover () {
       if (this.serie.cover) {
         return this.serie.cover
@@ -60,10 +50,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-.default-cover {
-  background-image: url("../../assets/audio/default-cover.png") !important;
-}
-</style>
diff --git a/front/src/components/audio/EmbedWizard.vue b/front/src/components/audio/EmbedWizard.vue
index eb470b35a..0bce4a6b1 100644
--- a/front/src/components/audio/EmbedWizard.vue
+++ b/front/src/components/audio/EmbedWizard.vue
@@ -115,12 +115,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-p.message {
-  position: absolute;
-  right: 0;
-  bottom: -2em;
-}
-</style>
diff --git a/front/src/components/audio/LibraryFollowButton.vue b/front/src/components/audio/LibraryFollowButton.vue
index 2b418edc4..8aa0a8fe3 100644
--- a/front/src/components/audio/LibraryFollowButton.vue
+++ b/front/src/components/audio/LibraryFollowButton.vue
@@ -37,7 +37,3 @@ export default {
 
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/audio/PlayButton.vue b/front/src/components/audio/PlayButton.vue
index 906dafee3..de299a383 100644
--- a/front/src/components/audio/PlayButton.vue
+++ b/front/src/components/audio/PlayButton.vue
@@ -1,5 +1,5 @@
 <template>
-  <span :title="title" :class="['ui', {'tiny': discrete}, {'icon': !discrete}, {'buttons': !dropdownOnly && !iconOnly}, 'play-button']">
+  <span :title="title" :class="['ui', {'tiny': discrete}, {'icon': !discrete}, {'buttons': !dropdownOnly && !iconOnly}, 'play-button component-play-button']">
     <button
       v-if="!dropdownOnly"
       :title="labels.playNow"
@@ -12,6 +12,7 @@
     <div
       v-if="!discrete && !iconOnly"
       @click.prevent="clicked = true"
+      role="button"
       :class="['ui', {disabled: !playable && !filterableArtist}, 'floating', 'dropdown', {'icon': !dropdownOnly}, {'button': !dropdownOnly}]">
       <i :class="dropdownIconClasses.concat(['icon'])" :title="title" ></i>
       <div class="menu" v-if="clicked">
@@ -288,14 +289,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-i {
-  cursor: pointer;
-}
-button.item {
-  background-color: white;
-  width: 100%;
-}
-</style>
diff --git a/front/src/components/audio/Player.vue b/front/src/components/audio/Player.vue
index b072b7819..a6d638995 100644
--- a/front/src/components/audio/Player.vue
+++ b/front/src/components/audio/Player.vue
@@ -1,8 +1,8 @@
 <template>
-  <section v-if="currentTrack" class="player-wrapper ui bottom-player">
+  <section v-if="currentTrack" class="player-wrapper ui bottom-player component-player">
     <div class="ui inverted segment fixed-controls" @click.prevent.stop="toggleMobilePlayer">
       <div
-        :class="['ui', 'top attached', 'small', 'orange', 'inverted', {'indicating': isLoadingAudio}, 'progress']">
+        :class="['ui', 'top attached', 'small', 'inverted', {'indicating': isLoadingAudio}, 'progress']">
         <div class="buffer bar" :data-percent="bufferProgress" :style="{ 'width': bufferProgress + '%' }"></div>
         <div class="position bar" :data-percent="progress" :style="{ 'width': progress + '%' }"></div>
       </div>
@@ -128,7 +128,7 @@
               :disabled="!currentTrack">
               <i
                 class="repeat icon">
-                <span class="ui circular tiny orange label">1</span>
+                <span class="ui circular tiny vibrant label">1</span>
               </i>
             </span>
             <span
@@ -141,7 +141,7 @@
               @click.prevent.stop="$store.commit('player/looping', 0)">
               <i
                 class="repeat icon">
-                <span class="ui circular tiny orange label">&infin;</span>
+                <span class="ui circular tiny vibrant label">&infin;</span>
               </i>
             </span>
             <span
@@ -436,7 +436,6 @@ export default {
           param = "token"
           value = this.$store.state.auth.scopedTokens.listen
         }
-        console.log('HELLO', param, value, this.$store.state.auth.scopedTokens)
         sources.forEach(e => {
           e.url = url.updateQueryString(e.url, param, value)
         })
@@ -777,77 +776,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped lang="scss">
-@import "../../style/vendor/media";
-.controls {
-  display: flex;
-  justify-content: space-between;
-}
-
-.controls .icon.big {
-  cursor: pointer;
-  font-size: 2em !important;
-}
-
-.controls .icon {
-  cursor: pointer;
-  vertical-align: middle;
-}
-.timer {
-  font-size: 1.2em;
-}
-.looping {
-  i {
-    position: relative;
-  }
-  .ui.circular.label {
-    font-family: sans-serif;
-    position: absolute;
-    font-size: 0.5em !important;
-    bottom: -0.7rem;
-    right: -0.7rem;
-    padding: 2px 0 !important;
-    width: 15px !important;
-    height: 15px !important;
-    min-width: 15px !important;
-    min-height: 15px !important;
-    @include media(">desktop") {
-      font-size: 0.6em !important;
-    }
-  }
-}
-.shuffling.loader.inline {
-  margin: 0;
-}
-.control.circular.button {
-  padding: 0;
-  border: none;
-  background-color: transparent;
-  color: inherit;
-  &:focus {
-    box-shadow: none;
-  }
-
-}
-.fake-dropdown {
-  border: 1px solid gray;
-  border-radius: 3px;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  min-width: 10em;
-  > * {
-    padding: 0.5em;
-
-  }
-  .position.control {
-    padding-right: 1em;
-    flex-grow: 1;
-  }
-  .angle.icon {
-    margin-right: 0;
-  }
-}
-</style>
diff --git a/front/src/components/audio/Search.vue b/front/src/components/audio/Search.vue
index 40dbe403d..85960eb38 100644
--- a/front/src/components/audio/Search.vue
+++ b/front/src/components/audio/Search.vue
@@ -98,7 +98,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/audio/SearchBar.vue b/front/src/components/audio/SearchBar.vue
index 37844d65d..333212f2a 100644
--- a/front/src/components/audio/SearchBar.vue
+++ b/front/src/components/audio/SearchBar.vue
@@ -236,7 +236,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/audio/VolumeControl.vue b/front/src/components/audio/VolumeControl.vue
index 2ed687483..8abb6927d 100644
--- a/front/src/components/audio/VolumeControl.vue
+++ b/front/src/components/audio/VolumeControl.vue
@@ -1,5 +1,5 @@
 <template>
-   <span :class="['volume-control', {'expanded': expanded}]" @click.prevent.stop="" @mouseover="handleOver" @mouseleave="handleLeave">
+   <span :class="['component-volume-control', {'expanded': expanded}]" @click.prevent.stop="" @mouseover="handleOver" @mouseleave="handleLeave">
     <span
       role="button"
       v-if="sliderVolume === 0"
@@ -82,37 +82,3 @@ export default {
   }
 }
 </script>
-<style lang="scss" scoped>
-
-.volume-control {
-  display: flex;
-  line-height: inherit;
-  align-items: center;
-  position: relative;
-  overflow: visible;
-  input {
-    max-width: 5.5em;
-    height: 4px;
-  }
-  &.expandable {
-    .popup {
-      background-color: #1B1C1D;
-      position: absolute;
-      left: -4em;
-      top: -7em;
-      transform: rotate(-90deg);
-      display: flex;
-      align-items: center;
-      height: 2.5em;
-      padding: 0 0.5em;
-      box-shadow: 1px 1px 3px rgba(125, 125, 125, 0.5);
-    }
-    input {
-      max-width: 8.5em;
-    }
-    &:not(:hover):not(.expanded) .popup {
-      display: none;
-    }
-  }
-}
-</style>
diff --git a/front/src/components/audio/album/Card.vue b/front/src/components/audio/album/Card.vue
index 6be2dc37c..c619a9053 100644
--- a/front/src/components/audio/album/Card.vue
+++ b/front/src/components/audio/album/Card.vue
@@ -1,9 +1,9 @@
 <template>
-  <div class="card app-card">
+  <div class="card app-card component-album-card">
     <div
       @click="$router.push({name: 'library.albums.detail', params: {id: album.id}})"
       :class="['ui', 'head-image', 'image', {'default-cover': !album.cover.original}]" v-lazy:background-image="imageUrl">
-      <play-button :icon-only="true" :is-playable="album.is_playable" :button-classes="['ui', 'circular', 'large', 'orange', 'icon', 'button']" :album="album"></play-button>
+      <play-button :icon-only="true" :is-playable="album.is_playable" :button-classes="['ui', 'circular', 'large', 'vibrant', 'icon', 'button']" :album="album"></play-button>
     </div>
     <div class="content">
       <strong>
@@ -21,7 +21,7 @@
     </div>
     <div class="extra content">
       <translate translate-context="*/*/*" :translate-params="{count: album.tracks.length}" :translate-n="album.tracks.length" translate-plural="%{ count } tracks">%{ count } track</translate>
-      <play-button class="right floated basic icon" :dropdown-only="true" :is-playable="album.is_playable" :dropdown-icon-classes="['ellipsis', 'horizontal', 'large', 'grey']" :album="album"></play-button>
+      <play-button class="right floated basic icon" :dropdown-only="true" :is-playable="album.is_playable" :dropdown-icon-classes="['ellipsis', 'horizontal', 'large really discrete']" :album="album"></play-button>
     </div>
   </div>
 </template>
@@ -38,28 +38,10 @@ export default {
   },
   computed: {
     imageUrl () {
-      let url = '../../../assets/audio/default-cover.png'
-
       if (this.album.cover.original) {
-        url = this.$store.getters['instance/absoluteUrl'](this.album.cover.medium_square_crop)
-      } else {
-        return null
+        return this.$store.getters['instance/absoluteUrl'](this.album.cover.medium_square_crop)
       }
-      return url
     }
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped lang="scss">
-
-.default-cover {
-  background-image: url("../../../assets/audio/default-cover.png") !important;
-}
-
-.card.app-card > .head-image > .icon {
-  margin: 0.5em;
-
-}
-</style>
diff --git a/front/src/components/audio/album/Widget.vue b/front/src/components/audio/album/Widget.vue
index 33bc44732..abf5cadc4 100644
--- a/front/src/components/audio/album/Widget.vue
+++ b/front/src/components/audio/album/Widget.vue
@@ -91,18 +91,3 @@ export default {
   }
 }
 </script>
-<style scoped lang="scss">
-
-
-.wrapper {
-  width: 100%;
-}
-.ui.cards {
-  justify-content: flex-start;
-}
-</style>
-<style>
-.ui.cards .ui.button {
-  margin-right: 0px;
-}
-</style>
diff --git a/front/src/components/audio/artist/Card.vue b/front/src/components/audio/artist/Card.vue
index f73b7dbb1..7bc82a251 100644
--- a/front/src/components/audio/artist/Card.vue
+++ b/front/src/components/audio/artist/Card.vue
@@ -3,7 +3,7 @@
     <div
       @click="$router.push({name: 'library.artists.detail', params: {id: artist.id}})"
       :class="['ui', 'head-image', 'circular', 'image', {'default-cover': !cover.original}]" v-lazy:background-image="imageUrl">
-      <play-button :icon-only="true" :is-playable="artist.is_playable" :button-classes="['ui', 'circular', 'large', 'orange', 'icon', 'button']" :artist="artist"></play-button>
+      <play-button :icon-only="true" :is-playable="artist.is_playable" :button-classes="['ui', 'circular', 'large', 'vibrant', 'icon', 'button']" :artist="artist"></play-button>
     </div>
     <div class="content">
       <strong>
@@ -16,7 +16,7 @@
     </div>
     <div class="extra content">
       <translate translate-context="*/*/*" :translate-params="{count: artist.tracks_count}" :translate-n="artist.tracks_count" translate-plural="%{ count } tracks">%{ count } track</translate>
-      <play-button class="right floated basic icon" :dropdown-only="true" :is-playable="artist.is_playable" :dropdown-icon-classes="['ellipsis', 'horizontal', 'large', 'grey']" :artist="artist"></play-button>
+      <play-button class="right floated basic icon" :dropdown-only="true" :is-playable="artist.is_playable" :dropdown-icon-classes="['ellipsis', 'horizontal', 'large really discrete']" :artist="artist"></play-button>
     </div>
   </div>
 </template>
@@ -39,14 +39,10 @@ export default {
   },
   computed: {
     imageUrl () {
-      let url = '../../../assets/audio/default-cover.png'
       let cover = this.cover
       if (cover.original) {
-        url = this.$store.getters['instance/absoluteUrl'](cover.medium_square_crop)
-      } else {
-        return null
+        return this.$store.getters['instance/absoluteUrl'](cover.medium_square_crop)
       }
-      return url
     },
     cover () {
       if (this.artist.cover && this.artist.cover.original) {
@@ -61,10 +57,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-.default-cover {
-  background-image: url("../../../assets/audio/default-cover.png") !important;
-}
-</style>
diff --git a/front/src/components/audio/artist/Widget.vue b/front/src/components/audio/artist/Widget.vue
index 733bb9b2b..5ea2e1607 100644
--- a/front/src/components/audio/artist/Widget.vue
+++ b/front/src/components/audio/artist/Widget.vue
@@ -90,27 +90,3 @@ export default {
   }
 }
 </script>
-<style scoped lang="scss">
-@import "../../../style/vendor/media";
-
-.wrapper {
-  width: 100%;
-}
-.ui.cards {
-  justify-content: flex-start;
-}
-
-.ui.three.cards .card {
-  width: 100%;
-}
-@include media(">tablet") {
-  .ui.three.cards .card {
-    width: 25em;
-  }
-}
-</style>
-<style>
-.ui.cards .ui.button {
-  margin-right: 0px;
-}
-</style>
diff --git a/front/src/components/audio/track/Row.vue b/front/src/components/audio/track/Row.vue
index d655a2126..a0dc8f5a3 100644
--- a/front/src/components/audio/track/Row.vue
+++ b/front/src/components/audio/track/Row.vue
@@ -1,7 +1,7 @@
 <template>
   <tr>
     <td>
-      <play-button :class="['basic', {orange: currentTrack && isPlaying && track.id === currentTrack.id}, 'icon']" :discrete="true" :is-playable="playable" :track="track"></play-button>
+      <play-button :class="['basic', {vibrant: currentTrack && isPlaying && track.id === currentTrack.id}, 'icon']" :discrete="true" :is-playable="playable" :track="track"></play-button>
     </td>
     <td>
       <img class="ui mini image" v-if="track.album && track.album.cover.original" v-lazy="$store.getters['instance/absoluteUrl'](track.album.cover.small_square_crop)">
@@ -12,17 +12,17 @@
         <template v-if="displayPosition && track.position">
           {{ track.position }}.
         </template>
-        {{ track.title }}
+        {{ track.title|truncate(40) }}
       </router-link>
     </td>
     <td colspan="4">
       <router-link class="artist discrete link" :to="{name: 'library.artists.detail', params: {id: track.artist.id }}">
-        {{ track.artist.name }}
+        {{ track.artist.name|truncate(40) }}
       </router-link>
     </td>
     <td colspan="4">
       <router-link v-if="track.album" class="album discrete link" :title="track.album.title" :to="{name: 'library.albums.detail', params: {id: track.album.id }}">
-        {{ track.album.title }}
+        {{ track.album.title|truncate(40) }}
       </router-link>
     </td>
     <td colspan="4" v-if="track.uploads && track.uploads.length > 0">
@@ -32,17 +32,17 @@
       <translate translate-context="*/*/*">N/A</translate>
     </td>
     <td colspan="2" v-if="displayActions" class="align right">
-      <track-favorite-icon class="favorite-icon" :track="track"></track-favorite-icon>
       <play-button
         class="play-button basic icon"
         :dropdown-only="true"
         :is-playable="track.is_playable"
-        :dropdown-icon-classes="['ellipsis', 'vertical', 'large', 'grey']"
+        :dropdown-icon-classes="['ellipsis', 'vertical', 'large really discrete']"
         :track="track"
       ></play-button>
       <track-playlist-icon
         v-if="$store.state.auth.authenticated"
         :track="track"></track-playlist-icon>
+      <track-favorite-icon class="favorite-icon" :track="track"></track-favorite-icon>
     </td>
   </tr>
 </template>
@@ -83,13 +83,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style lang="scss" scoped>
-tr:not(:hover) {
-  .favorite-icon:not(.favorited),
-  .playlist-icon {
-    visibility: hidden;
-  }
-}
-</style>
diff --git a/front/src/components/audio/track/Table.vue b/front/src/components/audio/track/Table.vue
index ed91b79a2..2fcba86cf 100644
--- a/front/src/components/audio/track/Table.vue
+++ b/front/src/components/audio/track/Table.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="table-wrapper">
+  <div class="table-wrapper component-track-table">
     <inline-search-bar v-model="query" v-if="search" @search="additionalTracks = []; loadMore()"></inline-search-bar>
     <slot v-if="!isLoading && allTracks.length === 0" name="empty-state">
       <empty-state @refresh="fetchData" :refresh="true"></empty-state>
@@ -90,16 +90,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-tr:not(:hover) .favorite-icon:not(.favorited) {
-  display: none;
-}
-pre {
-  overflow-x: scroll;
-}
-.table-wrapper {
-  overflow: visible;
-}
-</style>
diff --git a/front/src/components/audio/track/Widget.vue b/front/src/components/audio/track/Widget.vue
index 615586b64..00982f56f 100644
--- a/front/src/components/audio/track/Widget.vue
+++ b/front/src/components/audio/track/Widget.vue
@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="component-track-widget">
     <h3 class="ui header">
       <slot name="title"></slot>
       <span v-if="showCount" class="ui tiny circular label">{{ count }}</span>
@@ -9,7 +9,7 @@
         <div class="ui tiny image">
           <img v-if="object.track.album && object.track.album.cover.original" v-lazy="$store.getters['instance/absoluteUrl'](object.track.album.cover.medium_square_crop)">
           <img v-else src="../../../assets/audio/default-cover.png">
-          <play-button class="play-overlay" :icon-only="true" :button-classes="['ui', 'circular', 'tiny', 'orange', 'icon', 'button']" :track="object.track"></play-button>
+          <play-button class="play-overlay" :icon-only="true" :button-classes="['ui', 'circular', 'tiny', 'vibrant', 'icon', 'button']" :track="object.track"></play-button>
         </div>
         <div class="middle aligned content">
           <div class="ui unstackable grid">
@@ -38,7 +38,7 @@
                 class="basic icon"
                 :account="object.actor"
                 :dropdown-only="true"
-                :dropdown-icon-classes="['ellipsis', 'vertical', 'large', 'grey']"
+                :dropdown-icon-classes="['ellipsis', 'vertical', 'large really discrete']"
                 :track="object.track"></play-button>
             </div>
           </div>
@@ -151,37 +151,3 @@ export default {
   }
 }
 </script>
-
-<style scoped lang="scss">
-@import "../../../style/vendor/media";
-
-.play-overlay {
-  position: absolute;
-  top: 4em;
-  left: 4em;
-  @include media(">tablet") {
-    top: 2.5em;
-    left: 2.5em;
-  }
-}
-.refresh.icon {
-  float: right;
-}
-.ui.divided.items > .item:last-child {
-  padding-bottom: 1em !important;
-}
-
-@include media(">tablet") {
-  .divided.items > .track-item.inline {
-    width: 25em;
-    float: left;
-    border-top: none;
-    &,
-    &:first-child {
-      margin-top: 0.5em !important;
-      margin-right: 0.5em !important;
-      padding: 1em 0 !important;
-    }
-  }
-}
-</style>
diff --git a/front/src/components/auth/ApplicationForm.vue b/front/src/components/auth/ApplicationForm.vue
index b8427357a..12988a509 100644
--- a/front/src/components/auth/ApplicationForm.vue
+++ b/front/src/components/auth/ApplicationForm.vue
@@ -1,6 +1,6 @@
 <template>
 
-  <form class="ui form" @submit.prevent="submit()">
+  <form class="ui form component-form" @submit.prevent="submit()">
     <div v-if="errors.length > 0" class="ui negative message">
       <div class="header"><translate translate-context="Content/*/Error message.Title">We cannot save your changes</translate></div>
       <ul class="list">
@@ -62,7 +62,7 @@
       </div>
 
       </div>
-    <button :class="['ui', {'loading': isLoading}, 'green', 'button']" type="submit">
+    <button :class="['ui', {'loading': isLoading}, 'success', 'button']" type="submit">
       <translate v-if="updating" key="2" translate-context="Content/Applications/Button.Label/Verb">Update application</translate>
       <translate v-else key="2" translate-context="Content/Applications/Button.Label/Verb">Create application</translate>
     </button>
@@ -173,13 +173,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-.parent.checkbox {
-  margin: 1em 0;
-}
-.child.checkbox {
-  margin-left: 1em;
-}
-</style>
diff --git a/front/src/components/auth/Authorize.vue b/front/src/components/auth/Authorize.vue
index dd9bf6eeb..91cd0e71f 100644
--- a/front/src/components/auth/Authorize.vue
+++ b/front/src/components/auth/Authorize.vue
@@ -16,15 +16,15 @@
         <form v-else-if="application && !code" :class="['ui', {loading: isLoading}, 'form']" @submit.prevent="submit">
           <h3><translate translate-context="Content/Auth/Title" :translate-params="{app: application.name}">%{ app } wants to access your Funkwhale account</translate></h3>
 
-          <h4 v-for="topic in topicScopes" class="ui header">
-            <span v-if="topic.write && !topic.read" :class="['ui', 'basic', 'right floated', 'tiny', 'label']">
+          <h4 v-for="topic in topicScopes" class="ui header vertical-align">
+            <span v-if="topic.write && !topic.read" :class="['ui', 'basic', 'right floated', 'tiny', 'vertically-spaced component-label label']">
               <i class="pencil icon"></i>
               <translate translate-context="Content/Auth/Label/Noun">Write-only</translate>
             </span>
-            <span v-else-if="!topic.write && topic.read" :class="['ui', 'basic', 'right floated', 'tiny', 'label']">
+            <span v-else-if="!topic.write && topic.read" :class="['ui', 'basic', 'right floated', 'tiny', 'vertically-spaced component-label label']">
               <translate translate-context="Content/Auth/Label/Noun">Read-only</translate>
             </span>
-            <span v-else-if="topic.write && topic.read" :class="['ui', 'basic', 'right floated', 'tiny', 'label']">
+            <span v-else-if="topic.write && topic.read" :class="['ui', 'basic', 'right floated', 'tiny', 'vertically-spaced component-label label']">
               <i class="pencil icon"></i>
               <translate translate-context="Content/Auth/Label/Noun">Full access</translate>
             </span>
@@ -43,7 +43,7 @@
             </ul>
 
           </div>
-          <button class="ui green labeled icon button" type="submit">
+          <button class="ui success labeled icon button" type="submit">
             <i class="lock open icon"></i>
             <translate translate-context="Content/Signup/Button.Label/Verb" :translate-params="{app: application.name}">Authorize %{ app }</translate>
           </button>
@@ -189,13 +189,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-.ui.header .content {
-  text-align: left;
-}
-.ui.header > .ui.label {
-  margin-top: 0.3em;
-}
-</style>
diff --git a/front/src/components/auth/LoginForm.vue b/front/src/components/auth/LoginForm.vue
index d14d893dc..5e9ec22a1 100644
--- a/front/src/components/auth/LoginForm.vue
+++ b/front/src/components/auth/LoginForm.vue
@@ -55,7 +55,7 @@ import PasswordInput from "@/components/forms/PasswordInput"
 export default {
   props: {
     next: { type: String, default: "/library" },
-    buttonClasses: { type: String, default: "green" },
+    buttonClasses: { type: String, default: "success" },
     showSignup: { type: Boolean, default: true},
   },
   components: {
@@ -117,7 +117,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/auth/Logout.vue b/front/src/components/auth/Logout.vue
index 99fbc9da6..510f3562e 100644
--- a/front/src/components/auth/Logout.vue
+++ b/front/src/components/auth/Logout.vue
@@ -23,7 +23,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/auth/Settings.vue b/front/src/components/auth/Settings.vue
index 588c1c4e1..6a8aa5b72 100644
--- a/front/src/components/auth/Settings.vue
+++ b/front/src/components/auth/Settings.vue
@@ -81,7 +81,7 @@
             <password-input required v-model="new_password" />
           </div>
           <dangerous-button
-            :class="['ui', {'loading': isLoading}, 'yellow', 'button']"
+            :class="['ui', {'loading': isLoading}, 'warning', 'button']"
             :action="submitPassword">
             <translate translate-context="Content/Settings/Button.Label">Change password</translate>
             <p slot="modal-header"><translate translate-context="Popup/Settings/Title">Change your password?</translate></p>
@@ -178,7 +178,7 @@
               </td>
               <td>
                 <dangerous-button
-                  class="ui tiny basic red button"
+                  class="ui tiny basic danger button"
                   @confirm="revokeApp(app.client_id)">
                   <translate translate-context="*/*/*/Verb">Revoke</translate>
                   <p slot="modal-header" v-translate="{application: app.name}" translate-context="Popup/Settings/Title">Revoke access for application "%{ application }"?</p>
@@ -207,7 +207,7 @@
           </div>
         </h2>
         <p><translate translate-context="Content/Settings/Paragraph">This is the list of applications that you have created.</translate></p>
-        <router-link class="ui basic green button" :to="{name: 'settings.applications.new'}">
+        <router-link class="ui basic success button" :to="{name: 'settings.applications.new'}">
           <translate translate-context="Content/Settings/Button.Label">Create a new application</translate>
         </router-link>
         <table v-if="ownedApps.length > 0" class="ui compact very basic unstackable table">
@@ -233,11 +233,11 @@
                 <human-date :date="app.created" />
               </td>
               <td>
-                <router-link class="ui basic tiny green button" :to="{name: 'settings.applications.edit', params: {id: app.client_id}}">
+                <router-link class="ui basic tiny success button" :to="{name: 'settings.applications.edit', params: {id: app.client_id}}">
                   <translate translate-context="Content/*/Button.Label/Verb">Edit</translate>
                 </router-link>
                 <dangerous-button
-                  class="ui tiny basic red button"
+                  class="ui tiny basic danger button"
                   @confirm="deleteApp(app.client_id)">
                   <translate translate-context="*/*/*/Verb">Delete</translate>
                   <p slot="modal-header" v-translate="{application: app.name}" translate-context="Popup/Settings/Title">Delete application "%{ application }"?</p>
@@ -283,7 +283,7 @@
             <password-input required v-model="password" />
           </div>
           <dangerous-button
-            :class="['ui', {'loading': isDeletingAccount}, {disabled: !password}, 'red', 'button']"
+            :class="['ui', {'loading': isDeletingAccount}, {disabled: !password}, 'danger', 'button']"
             :action="deleteAccount">
             <translate translate-context="*/*/Button.Label">Delete my account…</translate>
             <p slot="modal-header"><translate translate-context="Popup/Settings/Title">Do you want to delete your account?</translate></p>
@@ -534,7 +534,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/auth/SignupForm.vue b/front/src/components/auth/SignupForm.vue
index d78ac4351..744666f69 100644
--- a/front/src/components/auth/SignupForm.vue
+++ b/front/src/components/auth/SignupForm.vue
@@ -9,7 +9,7 @@
       </p>
     </div>
     <h2><translate translate-context="Content/Login/Title/Verb">Log in to your Funkwhale account</translate></h2>
-    <login-form button-classes="basic green" :show-signup="false"></login-form>
+    <login-form button-classes="basic success" :show-signup="false"></login-form>
   </div>
   <form
     v-else
@@ -93,7 +93,7 @@ export default {
   props: {
     defaultInvitation: { type: String, required: false, default: null },
     next: { type: String, default: "/" },
-    buttonClasses: { type: String, default: "green" },
+    buttonClasses: { type: String, default: "success" },
     customization: { type: Object, default: null},
     fetchDescriptionHtml: { type: Boolean, default: false},
     fetchDescriptionHtml: { type: Boolean, default: false},
@@ -176,7 +176,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/auth/SubsonicTokenForm.vue b/front/src/components/auth/SubsonicTokenForm.vue
index 67621bf8d..b1e1e713b 100644
--- a/front/src/components/auth/SubsonicTokenForm.vue
+++ b/front/src/components/auth/SubsonicTokenForm.vue
@@ -33,7 +33,7 @@
       </div>
       <dangerous-button
         v-if="token"
-        :class="['ui', {'loading': isLoading}, 'grey', 'button']"
+        :class="['ui', {'loading': isLoading}, 'button']"
         :action="requestNewToken">
         <translate translate-context="*/Settings/Button.Label/Verb">Request a new password</translate>
         <p slot="modal-header"><translate translate-context="Popup/Settings/Title">Request a new Subsonic API password?</translate></p>
@@ -42,12 +42,12 @@
       </dangerous-button>
       <button
         v-else
-        color="grey"
+        color=""
         :class="['ui', {'loading': isLoading}, 'button']"
         @click="requestNewToken"><translate translate-context="Content/Settings/Button.Label/Verb">Request a password</translate></button>
         <dangerous-button
           v-if="token"
-          :class="['ui', {'loading': isLoading}, 'yellow', 'button']"
+          :class="['ui', {'loading': isLoading}, 'warning', 'button']"
           :action="disable">
           <translate translate-context="Content/Settings/Button.Label/Verb">Disable Subsonic access</translate>
           <p slot="modal-header"><translate translate-context="Popup/Settings/Title">Disable Subsonic API access?</translate></p>
@@ -135,7 +135,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/channels/SubscribeButton.vue b/front/src/components/channels/SubscribeButton.vue
index d4d095c2f..6a087554b 100644
--- a/front/src/components/channels/SubscribeButton.vue
+++ b/front/src/components/channels/SubscribeButton.vue
@@ -37,7 +37,3 @@ export default {
 
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/channels/UploadForm.vue b/front/src/components/channels/UploadForm.vue
index 951054378..821c34c97 100644
--- a/front/src/components/channels/UploadForm.vue
+++ b/front/src/components/channels/UploadForm.vue
@@ -1,5 +1,5 @@
 <template>
-  <form @submit.stop.prevent :class="['ui', {loading: isLoadingStep1}, 'form']">
+  <form @submit.stop.prevent :class="['ui', {loading: isLoadingStep1}, 'form component-file-upload']">
     <div v-if="errors.length > 0" class="ui negative message">
       <div class="header"><translate translate-context="Content/*/Error message.Title">Error while publishing</translate></div>
       <ul class="list">
@@ -60,7 +60,7 @@
               <div
                 v-if="file.error"
                 @click.stop.prevent="selectedUploadId = file.response.uuid"
-                class="ui basic red icon label"
+                class="ui basic danger icon label"
                 :title="file.error">
                 <i class="warning sign icon"></i>
               </div>
@@ -402,9 +402,9 @@ export default {
           } else {
             d.icon = "user"
             if (c.artist.content_category === 'podcast') {
-              d.iconClass = "bordered grey icon"
+              d.iconClass = "bordered icon"
             } else {
-              d.iconClass = "circular grey icon"
+              d.iconClass = "circular icon"
 
             }
           }
diff --git a/front/src/components/common/ActionFeedback.vue b/front/src/components/common/ActionFeedback.vue
index 5f53e989d..160db953d 100644
--- a/front/src/components/common/ActionFeedback.vue
+++ b/front/src/components/common/ActionFeedback.vue
@@ -1,7 +1,7 @@
 <template>
   <span class="feedback" v-if="isLoading || isDone">
     <span v-if="isLoading" :class="['ui', 'active', size, 'inline', 'loader']"></span>
-    <i v-if="isDone" :class="['green', size, 'check', 'icon']"></i>
+    <i v-if="isDone" :class="['success', size, 'check', 'icon']"></i>
   </span>
 </template>
 
diff --git a/front/src/components/common/ActionTable.vue b/front/src/components/common/ActionTable.vue
index 830331382..256db8c0b 100644
--- a/front/src/components/common/ActionTable.vue
+++ b/front/src/components/common/ActionTable.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="table-wrapper">
+  <div class="table-wrapper component-action-table">
     <table class="ui compact very basic unstackable table">
       <thead>
         <tr>
@@ -30,7 +30,7 @@
                 <div class="field">
                   <dangerous-button
                     v-if="selectAll || currentAction.isDangerous" :class="['ui', {disabled: checked.length === 0}, {'loading': actionLoading}, 'button']"
-                    :confirm-color="currentAction.confirmColor || 'green'"
+                    :confirm-color="currentAction.confirmColor || 'success'"
                     @confirm="launchAction">
                     <translate translate-context="Content/*/Button.Label/Short, Verb">Go</translate>
                     <p slot="modal-header">
@@ -300,11 +300,3 @@ export default {
   }
 }
 </script>
-<style scoped>
-.count.field {
-  font-weight: normal;
-}
-.ui.form .inline.fields {
-  margin: 0;
-}
-</style>
diff --git a/front/src/components/common/ActorAvatar.vue b/front/src/components/common/ActorAvatar.vue
index 6137823a3..a0a6f4ca9 100644
--- a/front/src/components/common/ActorAvatar.vue
+++ b/front/src/components/common/ActorAvatar.vue
@@ -20,10 +20,3 @@ export default {
   }
 }
 </script>
-<style lang="scss">
-.ui.circular.avatar {
-  width: 28px;
-  height: 28px;
-  font-size: 1em !important;
-}
-</style>
diff --git a/front/src/components/common/ContentForm.vue b/front/src/components/common/ContentForm.vue
index f47daa6fc..4b1fd73c6 100644
--- a/front/src/components/common/ContentForm.vue
+++ b/front/src/components/common/ContentForm.vue
@@ -39,7 +39,7 @@
       </template>
     </div>
     <div class="ui bottom attached segment">
-      <span :class="['right', 'floated', {'ui red text': remainingChars < 0}]" v-if="charLimit">
+      <span :class="['right', 'floated', {'ui danger text': remainingChars < 0}]" v-if="charLimit">
         {{ remainingChars }}
       </span>
       <p>
diff --git a/front/src/components/common/CopyInput.vue b/front/src/components/common/CopyInput.vue
index f6637297e..3e862ef9e 100644
--- a/front/src/components/common/CopyInput.vue
+++ b/front/src/components/common/CopyInput.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="ui fluid action input">
+  <div class="ui fluid action input component-copy-input">
     <p class="message" v-if="copied">
       <translate translate-context="Content/*/Paragraph">Text copied to clipboard!</translate>
     </p>
@@ -39,14 +39,3 @@ export default {
   }
 }
 </script>
-<style scoped>
-.message {
-  position: absolute;
-  right: 0;
-  bottom: -3em;
-  padding: 0.3em;
-  box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.3);
-  background-color: white;
-  z-index: 999;
-}
-</style>
diff --git a/front/src/components/common/DangerousButton.vue b/front/src/components/common/DangerousButton.vue
index 969b6c667..fd7009ad2 100644
--- a/front/src/components/common/DangerousButton.vue
+++ b/front/src/components/common/DangerousButton.vue
@@ -34,7 +34,7 @@ export default {
   props: {
     action: {type: Function, required: false},
     disabled: {type: Boolean, default: false},
-    confirmColor: {type: String, default: "red", required: false}
+    confirmColor: {type: String, default: "danger", required: false}
   },
   components: {
     Modal
diff --git a/front/src/components/common/EmptyState.vue b/front/src/components/common/EmptyState.vue
index 3a45ad6fa..a05cb407d 100644
--- a/front/src/components/common/EmptyState.vue
+++ b/front/src/components/common/EmptyState.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="ui small placeholder segment">
+  <div class="ui small placeholder segment component-placeholder component-empty-state">
     <div class="ui header">
       <div class="content">
         <slot name="title">
@@ -28,13 +28,3 @@ export default {
   }
 }
 </script>
-
-<style scoped>
-.ui.small.placeholder.segment {
-  min-height: auto;
-}
-.ui.header .content {
-  text-align: center;
-  display: block;
-}
-</style>
diff --git a/front/src/components/common/UserLink.vue b/front/src/components/common/UserLink.vue
index 1631cb93a..20c6ddb98 100644
--- a/front/src/components/common/UserLink.vue
+++ b/front/src/components/common/UserLink.vue
@@ -1,5 +1,5 @@
 <template>
-  <span>
+  <span class="component-user-link">
     <template v-if="avatar">
       <img
         class="ui tiny circular avatar"
@@ -32,9 +32,3 @@ export default {
   }
 }
 </script>
-<style scoped>
-.tiny.circular.avatar {
-  width: 1.7em;
-  height: 1.7em;
-}
-</style>
diff --git a/front/src/components/favorites/List.vue b/front/src/components/favorites/List.vue
index 003f293b5..5766a219b 100644
--- a/front/src/components/favorites/List.vue
+++ b/front/src/components/favorites/List.vue
@@ -64,7 +64,7 @@
           translate-context="Content/Home/Placeholder"
         >No tracks have been added to your favorites yet</translate>
       </div>
-      <router-link :to="'/library'" class="ui green labeled icon button">
+      <router-link :to="'/library'" class="ui success labeled icon button">
       <i class="headphones icon"></i>
         <translate translate-context="Content/*/Verb">Browse the library</translate>
       </router-link>
@@ -178,7 +178,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/favorites/TrackFavoriteIcon.vue b/front/src/components/favorites/TrackFavoriteIcon.vue
index 8670dbcb9..de292ca87 100644
--- a/front/src/components/favorites/TrackFavoriteIcon.vue
+++ b/front/src/components/favorites/TrackFavoriteIcon.vue
@@ -36,7 +36,3 @@ export default {
 
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/federation/FetchButton.vue b/front/src/components/federation/FetchButton.vue
index c31111ea4..cde0959b4 100644
--- a/front/src/components/federation/FetchButton.vue
+++ b/front/src/components/federation/FetchButton.vue
@@ -85,7 +85,7 @@
         <div role="button" class="ui basic cancel button">
           <translate translate-context="*/*/Button.Label/Verb">Close</translate>
         </div>
-        <div role="button" @click="showModal = false; $emit('refresh')" class="ui confirm green button" v-if="fetch && fetch.status === 'finished'">
+        <div role="button" @click="showModal = false; $emit('refresh')" class="ui confirm success button" v-if="fetch && fetch.status === 'finished'">
           <translate translate-context="*/*/Button.Label/Verb">Close and reload page</translate>
         </div>
       </div>
diff --git a/front/src/components/library/AlbumBase.vue b/front/src/components/library/AlbumBase.vue
index c9e24bc32..104280a76 100644
--- a/front/src/components/library/AlbumBase.vue
+++ b/front/src/components/library/AlbumBase.vue
@@ -32,7 +32,7 @@
                     <translate v-else translate-context="*/*/*" :translate-params="{count: totalTracks}" :translate-n="totalTracks" translate-plural="%{ count } tracks">%{ count } track</translate>
                   </template>
                   <div class="ui small hidden divider"></div>
-                  <play-button class="orange" :tracks="object.tracks"></play-button>
+                  <play-button class="vibrant" :tracks="object.tracks"></play-button>
                   <div class="ui hidden horizontal divider"></div>
                   <album-dropdown
                     :object="object"
@@ -75,7 +75,7 @@
               </template>
               <human-duration v-if="totalDuration > 0" :duration="totalDuration"></human-duration>
               <div class="ui small hidden divider"></div>
-              <play-button class="orange" :tracks="object.tracks"></play-button>
+              <play-button class="vibrant" :tracks="object.tracks"></play-button>
               <div class="ui horizontal hidden divider"></div>
               <album-dropdown
                 :object="object"
diff --git a/front/src/components/library/AlbumDetail.vue b/front/src/components/library/AlbumDetail.vue
index f4a8fa6aa..dff0d1948 100644
--- a/front/src/components/library/AlbumDetail.vue
+++ b/front/src/components/library/AlbumDetail.vue
@@ -58,7 +58,3 @@ export default {
   },
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped lang="scss">
-</style>
diff --git a/front/src/components/library/Albums.vue b/front/src/components/library/Albums.vue
index 268754994..60bafe245 100644
--- a/front/src/components/library/Albums.vue
+++ b/front/src/components/library/Albums.vue
@@ -48,7 +48,7 @@
         item-selector=".column"
         percent-position="true"
         stagger="0"
-        class="ui stackable three column doubling grid">
+        class="">
         <div
           v-if="result.results.length > 0"
           class="ui app-cards cards">
@@ -67,7 +67,7 @@
           <router-link
           v-if="$store.state.auth.authenticated"
           :to="{name: 'content.index'}"
-          class="ui green button labeled icon">
+          class="ui success button labeled icon">
           <i class="upload icon"></i>
             <translate translate-context="Content/*/Verb">
               Add some music
@@ -223,7 +223,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/library/ArtistBase.vue b/front/src/components/library/ArtistBase.vue
index d7ff13a66..dbd282e04 100644
--- a/front/src/components/library/ArtistBase.vue
+++ b/front/src/components/library/ArtistBase.vue
@@ -29,7 +29,7 @@
 
             </div>
             <div class="ui buttons">
-              <play-button :is-playable="isPlayable" class="orange" :artist="object">
+              <play-button :is-playable="isPlayable" class="vibrant" :artist="object">
                 <translate translate-context="Content/Artist/Button.Label/Verb">Play all albums</translate>
               </play-button>
             </div>
diff --git a/front/src/components/library/ArtistDetail.vue b/front/src/components/library/ArtistDetail.vue
index 5b2021f98..56ba5e3fd 100644
--- a/front/src/components/library/ArtistDetail.vue
+++ b/front/src/components/library/ArtistDetail.vue
@@ -95,7 +95,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/library/Artists.vue b/front/src/components/library/Artists.vue
index bb5fdfa64..19e22089b 100644
--- a/front/src/components/library/Artists.vue
+++ b/front/src/components/library/Artists.vue
@@ -58,7 +58,7 @@
         <router-link
           v-if="$store.state.auth.authenticated"
           :to="{name: 'content.index'}"
-          class="ui green button labeled icon">
+          class="ui success button labeled icon">
           <i class="upload icon"></i>
           <translate translate-context="Content/*/Verb">
               Add some music
@@ -214,24 +214,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped lang="scss">
-@import "../../style/vendor/media";
-
-.wrapper {
-  width: 100%;
-}
-.ui.cards {
-  justify-content: flex-start;
-}
-
-.ui.three.cards .card {
-  width: 100%;
-}
-@include media(">tablet") {
-  .ui.three.cards .card {
-    width: 25em;
-  }
-}
-</style>
diff --git a/front/src/components/library/EditCard.vue b/front/src/components/library/EditCard.vue
index c255b69c7..c973ef1e0 100644
--- a/front/src/components/library/EditCard.vue
+++ b/front/src/components/library/EditCard.vue
@@ -18,19 +18,19 @@
 
         <span class="right floated">
           <span v-if="obj.is_approved && obj.is_applied">
-            <i class="green check icon"></i>
+            <i class="success check icon"></i>
             <translate translate-context="Content/Library/Card/Short">Approved and applied</translate>
           </span>
           <span v-else-if="obj.is_approved">
-            <i class="green check icon"></i>
+            <i class="success check icon"></i>
             <translate translate-context="Content/*/*/Short">Approved</translate>
           </span>
           <span v-else-if="obj.is_approved === null">
-            <i class="yellow hourglass icon"></i>
+            <i class="warning hourglass icon"></i>
             <translate translate-context="Content/Admin/*/Noun">Pending review</translate>
           </span>
           <span v-else-if="obj.is_approved === false">
-            <i class="red x icon"></i>
+            <i class="dangerx icon"></i>
             <translate translate-context="Content/Library/*/Short">Rejected</translate>
           </span>
         </span>
@@ -95,18 +95,18 @@
       <button
         v-if="canApprove && obj.is_approved !== true"
         @click="approve(true)"
-        :class="['ui', {loading: isLoading}, 'green', 'basic', 'button']">
+        :class="['ui', {loading: isLoading}, 'success', 'basic', 'button']">
         <translate translate-context="Content/*/Button.Label/Verb">Approve</translate>
       </button>
       <button
         v-if="canApprove && obj.is_approved === null"
         @click="approve(false)"
-        :class="['ui', {loading: isLoading}, 'yellow', 'basic', 'button']">
+        :class="['ui', {loading: isLoading}, 'warning', 'basic', 'button']">
         <translate translate-context="Content/Library/Button.Label">Reject</translate>
       </button>
       <dangerous-button
         v-if="canDelete"
-        :class="['ui', {loading: isLoading}, 'basic red button']"
+        :class="['ui', {loading: isLoading}, 'basic danger button']"
         :action="remove">
         <translate translate-context="*/*/*/Verb">Delete</translate>
         <p slot="modal-header"><translate translate-context="Popup/Library/Title">Delete this suggestion?</translate></p>
diff --git a/front/src/components/library/EditForm.vue b/front/src/components/library/EditForm.vue
index d58de5051..4e1bc3053 100644
--- a/front/src/components/library/EditForm.vue
+++ b/front/src/components/library/EditForm.vue
@@ -123,7 +123,7 @@
       >
         <translate translate-context="*/*/Button.Label/Verb">Cancel</translate>
       </router-link>
-      <button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit" :disabled="isLoading || !mutationPayload">
+      <button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'success', 'button']" type="submit" :disabled="isLoading || !mutationPayload">
         <translate v-if="canEdit" key="1" translate-context="Content/Library/Button.Label/Verb">Submit and apply edit</translate>
         <translate v-else key="2" translate-context="Content/Library/Button.Label/Verb">Submit suggestion</translate>
       </button>
@@ -256,8 +256,3 @@ export default {
   }
 }
 </script>
-<style>
-.reset.button {
-  margin-top: 0.5em;
-}
-</style>
diff --git a/front/src/components/library/FileUpload.vue b/front/src/components/library/FileUpload.vue
index 2086f6291..3960863bc 100644
--- a/front/src/components/library/FileUpload.vue
+++ b/front/src/components/library/FileUpload.vue
@@ -1,5 +1,5 @@
   <template>
-  <div>
+  <div class="component-file-upload">
     <div class="ui top attached tabular menu">
       <a :class="['item', {active: currentTab === 'summary'}]" @click="currentTab = 'summary'"><translate translate-context="Content/Library/Tab.Title/Short">Summary</translate></a>
       <a :class="['item', {active: currentTab === 'uploads'}]" @click="currentTab = 'uploads'">
@@ -7,10 +7,10 @@
         <div v-if="files.length === 0" class="ui label">
           0
         </div>
-        <div v-else-if="files.length > uploadedFilesCount + erroredFilesCount" class="ui yellow label">
+        <div v-else-if="files.length > uploadedFilesCount + erroredFilesCount" class="ui warning label">
           {{ uploadedFilesCount + erroredFilesCount }}/{{ files.length }}
         </div>
-        <div v-else :class="['ui', {'green': erroredFilesCount === 0}, {'red': erroredFilesCount > 0}, 'label']">
+        <div v-else :class="['ui', {'success': erroredFilesCount === 0}, {'danger': erroredFilesCount > 0}, 'label']">
           {{ uploadedFilesCount + erroredFilesCount }}/{{ files.length }}
         </div>
       </a>
@@ -19,10 +19,10 @@
         <div v-if="processableFiles === 0" class="ui label">
           0
         </div>
-        <div v-else-if="processableFiles > processedFilesCount" class="ui yellow label">
+        <div v-else-if="processableFiles > processedFilesCount" class="ui warning label">
           {{ processedFilesCount }}/{{ processableFiles }}
         </div>
-        <div v-else :class="['ui', {'green': uploads.errored === 0}, {'red': uploads.errored > 0}, 'label']">
+        <div v-else :class="['ui', {'success': uploads.errored === 0}, {'danger': uploads.errored > 0}, 'label']">
           {{ processedFilesCount }}/{{ processableFiles }}
         </div>
       </a>
@@ -54,12 +54,12 @@
           </div>
         </div>
 
-        <button type="submit" class="ui green button"><translate translate-context="Content/Library/Button.Label">Proceed</translate></button>
+        <button type="submit" class="ui success button"><translate translate-context="Content/Library/Button.Label">Proceed</translate></button>
       </form>
     </div>
     <div :class="['ui', 'bottom', 'attached', 'segment', {hidden: currentTab != 'uploads'}]">
       <div :class="['ui', {loading: isLoadingQuota}, 'container']">
-        <div :class="['ui', {red: remainingSpace === 0}, {yellow: remainingSpace > 0 && remainingSpace <= 50}, 'small', 'statistic']">
+        <div :class="['ui', {red: remainingSpace === 0}, {warning: remainingSpace > 0 && remainingSpace <= 50}, 'small', 'statistic']">
           <div class="label">
             <translate translate-context="Content/Library/Paragraph">Remaining storage space</translate>
           </div>
@@ -113,14 +113,14 @@
               <td>{{ file.size | humanSize }}</td>
               <td>
                 <span v-if="file.error" class="ui tooltip" :data-tooltip="labels.tooltips[file.error]">
-                  <span class="ui red icon label">
+                  <span class="ui danger icon label">
                     <i class="question circle outline icon" /> {{ file.error }}
                   </span>
                 </span>
-                <span v-else-if="file.success" class="ui green label">
+                <span v-else-if="file.success" class="ui success label">
                   <translate translate-context="Content/Library/Table" key="1">Uploaded</translate>
                 </span>
-                <span v-else-if="file.active" class="ui yellow label">
+                <span v-else-if="file.active" class="ui warning label">
                   <translate translate-context="Content/Library/Table" key="2">Uploading…</translate>
                   ({{ parseInt(file.progress) }}%)
                 </span>
@@ -137,7 +137,7 @@
                   </button>
                 </template>
                 <template v-else-if="!file.success">
-                  <button class="ui tiny basic red icon right floated button" @click.prevent="$refs.upload.remove(file)"><i class="delete icon"></i></button>
+                  <button class="ui tiny basic danger icon right floated button" @click.prevent="$refs.upload.remove(file)"><i class="delete icon"></i></button>
                 </template>
               </td>
             </tr>
@@ -396,16 +396,3 @@ export default {
   }
 };
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-.file-uploads.ui.button {
-  display: block;
-  padding: 2em 1em;
-  width: 100%;
-  box-shadow: none;
-  border-style: dashed !important;
-  border: 3px solid rgba(50, 50, 50, 0.5);
-  font-size: 1.5em;
-}
-</style>
diff --git a/front/src/components/library/FileUploadWidget.vue b/front/src/components/library/FileUploadWidget.vue
index 95c553d03..7557d61e4 100644
--- a/front/src/components/library/FileUploadWidget.vue
+++ b/front/src/components/library/FileUploadWidget.vue
@@ -38,7 +38,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/library/Home.vue b/front/src/components/library/Home.vue
index 20df9429a..fd053b10d 100644
--- a/front/src/components/library/Home.vue
+++ b/front/src/components/library/Home.vue
@@ -98,7 +98,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/library/Library.vue b/front/src/components/library/Library.vue
index c9b162be0..2d6e89366 100644
--- a/front/src/components/library/Library.vue
+++ b/front/src/components/library/Library.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="main library pusher">
+  <div class="main pusher page-library">
     <router-view :key="$router.currentRoute.name"></router-view>
   </div>
 </template>
@@ -21,35 +21,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style lang="scss">
-@import "../../style/vendor/media";
-
-.library {
-  .ui.segment.head {
-    background-size: cover;
-    background-position: center;
-    padding: 0;
-    .segment-content {
-      margin: 0 auto;
-      padding: 2em;
-      @include media(">tablet") {
-        padding: 4em;
-      }
-    }
-    &.with-background {
-      .header {
-        &,
-        .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);
-      }
-    }
-  }
-}
-</style>
diff --git a/front/src/components/library/Radios.vue b/front/src/components/library/Radios.vue
index da90a4f13..1f5b121dc 100644
--- a/front/src/components/library/Radios.vue
+++ b/front/src/components/library/Radios.vue
@@ -21,7 +21,7 @@
       <h3 class="ui header">
         <translate translate-context="Content/Radio/Title">User radios</translate>
       </h3>
-      <router-link class="ui green basic button" to="/library/radios/build" exact>
+      <router-link class="ui success basic button" to="/library/radios/build" exact>
         <translate translate-context="Content/Radio/Button.Label/Verb">Create your own radio</translate>
       </router-link>
       <div class="ui hidden divider"></div>
@@ -71,7 +71,7 @@
         <router-link
         v-if="$store.state.auth.authenticated"
         :to="{name: 'library.radios.build'}"
-        class="ui green button labeled icon">
+        class="ui success button labeled icon">
           <i class="rss icon"></i>
           <translate translate-context="Content/*/Verb">
             Create a radio
@@ -215,7 +215,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/library/TagDetail.vue b/front/src/components/library/TagDetail.vue
index a511d3f6f..9706153a4 100644
--- a/front/src/components/library/TagDetail.vue
+++ b/front/src/components/library/TagDetail.vue
@@ -2,7 +2,7 @@
   <main v-title="labels.title">
     <section class="ui vertical stripe segment">
       <h2 class="ui header">
-        <span class="ui circular huge hashtag label">
+        <span class="ui circular huge hashtag label component-label">
           {{ labels.title }}
         </span>
       </h2>
@@ -83,11 +83,3 @@ export default {
   },
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-.ui.circular.label {
-  padding-left: 1em !important;
-  padding-right: 1em !important;
-}
-</style>
diff --git a/front/src/components/library/TagsSelector.vue b/front/src/components/library/TagsSelector.vue
index 971f4e5f1..87a40401e 100644
--- a/front/src/components/library/TagsSelector.vue
+++ b/front/src/components/library/TagsSelector.vue
@@ -82,11 +82,3 @@ export default {
   }
 }
 </script>
-
-<style scoped>
-
-.ui.form .field > .selection.dropdown {
-  min-width: 200px;
-}
-
-</style>
diff --git a/front/src/components/library/TrackBase.vue b/front/src/components/library/TrackBase.vue
index 8af866daf..e5de582b7 100644
--- a/front/src/components/library/TrackBase.vue
+++ b/front/src/components/library/TrackBase.vue
@@ -17,7 +17,7 @@
               </h1>
             </div>
             <div class="eight wide right aligned column button-group">
-              <play-button class="orange" :track="track">
+              <play-button class="vibrant" :track="track">
                 <translate translate-context="*/Queue/Button.Label/Short, Verb">Play</translate>
               </play-button>
               &nbsp;
diff --git a/front/src/components/library/TrackDetail.vue b/front/src/components/library/TrackDetail.vue
index b0590d665..26ea23ab5 100644
--- a/front/src/components/library/TrackDetail.vue
+++ b/front/src/components/library/TrackDetail.vue
@@ -232,11 +232,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped lang="scss">
-.table.center.aligned {
-  margin-left: auto;
-  margin-right: auto;
-}
-</style>
diff --git a/front/src/components/library/radios/Builder.vue b/front/src/components/library/radios/Builder.vue
index 07c387cbe..636f161d8 100644
--- a/front/src/components/library/radios/Builder.vue
+++ b/front/src/components/library/radios/Builder.vue
@@ -31,7 +31,7 @@
               <label for="public"><translate translate-context="Content/Radio/Checkbox.Label/Verb">Display publicly</translate></label>
             </div>
             <div class="ui hidden divider"></div>
-            <button :disabled="!canSave" @click="save" :class="['ui', 'green', {loading: isLoading}, 'button']">
+            <button :disabled="!canSave" @click="save" :class="['ui', 'success', {loading: isLoading}, 'button']">
               <translate translate-context="Content/*/Button.Label/Verb">Save</translate>
             </button>
             <radio-button v-if="id" type="custom" :custom-radio-id="id"></radio-button>
diff --git a/front/src/components/library/radios/Filter.vue b/front/src/components/library/radios/Filter.vue
index 278fa3697..9830d870d 100644
--- a/front/src/components/library/radios/Filter.vue
+++ b/front/src/components/library/radios/Filter.vue
@@ -36,7 +36,7 @@
       <span
         @click="showCandidadesModal = !showCandidadesModal"
         v-if="checkResult"
-        :class="['ui', {'green': checkResult.candidates.count > 10}, 'label']">
+        :class="['ui', {'success': checkResult.candidates.count > 10}, 'label']">
         {{ checkResult.candidates.count }} tracks matching filter
       </span>
       <modal v-if="checkResult" :show.sync="showCandidadesModal">
@@ -49,14 +49,14 @@
           </div>
         </div>
         <div class="actions">
-          <div class="ui basic black deny button">
+          <div class="ui basic deny button">
             <translate translate-context="*/*/Button.Label/Verb">Cancel</translate>
           </div>
         </div>
       </modal>
     </td>
     <td>
-      <button @click="$emit('delete', index)" class="ui basic red button"><translate translate-context="Content/Radio/Button.Label/Verb">Remove</translate></button>
+      <button @click="$emit('delete', index)" class="ui basic danger button"><translate translate-context="Content/Radio/Button.Label/Verb">Remove</translate></button>
     </td>
   </tr>
 </template>
diff --git a/front/src/components/manage/ChannelsTable.vue b/front/src/components/manage/ChannelsTable.vue
index c8f87bd71..fa57aefde 100644
--- a/front/src/components/manage/ChannelsTable.vue
+++ b/front/src/components/manage/ChannelsTable.vue
@@ -200,7 +200,7 @@ export default {
         //   confirmationMessage: confirmationMessage,
         //   isDangerous: true,
         //   allowAll: false,
-        //   confirmColor: 'red',
+        //   confirmColor: 'danger',
         // },
       ]
     }
diff --git a/front/src/components/manage/library/AlbumsTable.vue b/front/src/components/manage/library/AlbumsTable.vue
index 3c9dd6a90..b9fd35764 100644
--- a/front/src/components/manage/library/AlbumsTable.vue
+++ b/front/src/components/manage/library/AlbumsTable.vue
@@ -194,7 +194,7 @@ export default {
           confirmationMessage: confirmationMessage,
           isDangerous: true,
           allowAll: false,
-          confirmColor: 'red',
+          confirmColor: 'danger',
         },
       ]
     }
diff --git a/front/src/components/manage/library/ArtistsTable.vue b/front/src/components/manage/library/ArtistsTable.vue
index 1bc69e02a..46f183364 100644
--- a/front/src/components/manage/library/ArtistsTable.vue
+++ b/front/src/components/manage/library/ArtistsTable.vue
@@ -201,7 +201,7 @@ export default {
           confirmationMessage: confirmationMessage,
           isDangerous: true,
           allowAll: false,
-          confirmColor: 'red',
+          confirmColor: 'danger',
         },
       ]
     }
diff --git a/front/src/components/manage/library/LibrariesTable.vue b/front/src/components/manage/library/LibrariesTable.vue
index 965ba9f91..f0b1276b3 100644
--- a/front/src/components/manage/library/LibrariesTable.vue
+++ b/front/src/components/manage/library/LibrariesTable.vue
@@ -211,7 +211,7 @@ export default {
           confirmationMessage: confirmationMessage,
           isDangerous: true,
           allowAll: false,
-          confirmColor: 'red',
+          confirmColor: 'danger',
         },
       ]
     }
diff --git a/front/src/components/manage/library/TagsTable.vue b/front/src/components/manage/library/TagsTable.vue
index 9016f5011..c2bcc2627 100644
--- a/front/src/components/manage/library/TagsTable.vue
+++ b/front/src/components/manage/library/TagsTable.vue
@@ -185,7 +185,7 @@ export default {
           confirmationMessage: confirmationMessage,
           isDangerous: true,
           allowAll: false,
-          confirmColor: 'red',
+          confirmColor: 'danger',
         },
       ]
     }
diff --git a/front/src/components/manage/library/TracksTable.vue b/front/src/components/manage/library/TracksTable.vue
index 4fec4c500..a9a36b5ac 100644
--- a/front/src/components/manage/library/TracksTable.vue
+++ b/front/src/components/manage/library/TracksTable.vue
@@ -196,7 +196,7 @@ export default {
           confirmationMessage: confirmationMessage,
           isDangerous: true,
           allowAll: false,
-          confirmColor: 'red',
+          confirmColor: 'danger',
         },
       ]
     }
diff --git a/front/src/components/manage/library/UploadsTable.vue b/front/src/components/manage/library/UploadsTable.vue
index 5215a538f..974a2176e 100644
--- a/front/src/components/manage/library/UploadsTable.vue
+++ b/front/src/components/manage/library/UploadsTable.vue
@@ -261,7 +261,7 @@ export default {
           confirmationMessage: confirmationMessage,
           isDangerous: true,
           allowAll: false,
-          confirmColor: 'red',
+          confirmColor: 'danger',
         },
       ]
     }
diff --git a/front/src/components/manage/moderation/DomainsTable.vue b/front/src/components/manage/moderation/DomainsTable.vue
index ef83fb916..94a067bda 100644
--- a/front/src/components/manage/moderation/DomainsTable.vue
+++ b/front/src/components/manage/moderation/DomainsTable.vue
@@ -54,7 +54,7 @@
           <td>
             <router-link :to="{name: 'manage.moderation.domains.detail', params: {id: scope.obj.name }}">
               {{ scope.obj.name }}
-              <i v-if="allowListEnabled && scope.obj.allowed" class="green check icon" :title="labels.allowListTitle"></i>
+              <i v-if="allowListEnabled && scope.obj.allowed" class="success check icon" :title="labels.allowListTitle"></i>
             </router-link>
           </td>
           <td>
diff --git a/front/src/components/manage/moderation/InstancePolicyCard.vue b/front/src/components/manage/moderation/InstancePolicyCard.vue
index 5ece6f1f9..ec5bf1c58 100644
--- a/front/src/components/manage/moderation/InstancePolicyCard.vue
+++ b/front/src/components/manage/moderation/InstancePolicyCard.vue
@@ -67,6 +67,3 @@ export default {
   }
 }
 </script>
-
-<style scoped>
-</style>
diff --git a/front/src/components/manage/moderation/InstancePolicyForm.vue b/front/src/components/manage/moderation/InstancePolicyForm.vue
index f6b554d00..5ba13b345 100644
--- a/front/src/components/manage/moderation/InstancePolicyForm.vue
+++ b/front/src/components/manage/moderation/InstancePolicyForm.vue
@@ -54,11 +54,11 @@
     <button @click.prevent="$emit('cancel')" class="ui basic left floated button">
       <translate translate-context="*/*/Button.Label/Verb">Cancel</translate>
     </button>
-    <button :class="['ui', 'right', 'floated', 'green', {'disabled loading': isLoading}, 'button']" :disabled="isLoading">
+    <button :class="['ui', 'right', 'floated', 'success', {'disabled loading': isLoading}, 'button']" :disabled="isLoading">
       <translate translate-context="Content/Moderation/Card.Button.Label/Verb" v-if="object" key="1">Update</translate>
       <translate translate-context="Content/Moderation/Card.Button.Label/Verb" v-else key="2">Create</translate>
     </button>
-    <dangerous-button v-if="object" class="ui right floated basic red button" @confirm="remove">
+    <dangerous-button v-if="object" class="ui right floated basic danger button" @confirm="remove">
       <translate translate-context="*/*/*/Verb">Delete</translate>
       <p slot="modal-header">
         <translate translate-context="Popup/Moderation/Title">Delete this moderation rule?</translate>
@@ -200,15 +200,3 @@ export default {
   }
 }
 </script>
-
-<style scoped>
-.ui.placeholder.segment .field,
-.ui.placeholder.segment textarea,
-.ui.placeholder.segment > .ui.input,
-.ui.placeholder.segment .button {
-  max-width: 100%;
-}
-.segment .right.floated.button {
-  margin-left: 1em;
-}
-</style>
diff --git a/front/src/components/manage/moderation/NotesThread.vue b/front/src/components/manage/moderation/NotesThread.vue
index 393e4fcbd..9c2d75700 100644
--- a/front/src/components/manage/moderation/NotesThread.vue
+++ b/front/src/components/manage/moderation/NotesThread.vue
@@ -18,7 +18,7 @@
         </div>
         <div class="meta">
           <dangerous-button
-            :class="['ui', {loading: isLoading}, 'basic borderless mini grey button']"
+            :class="['ui', {loading: isLoading}, 'basic borderless mini button']"
             @confirm="remove(note)">
             <i class="trash icon"></i>
             <translate translate-context="*/*/*/Verb">Delete</translate>
diff --git a/front/src/components/manage/moderation/ReportCard.vue b/front/src/components/manage/moderation/ReportCard.vue
index 5d167c9ac..bf8932b18 100644
--- a/front/src/components/manage/moderation/ReportCard.vue
+++ b/front/src/components/manage/moderation/ReportCard.vue
@@ -59,12 +59,12 @@
                   </td>
                   <td v-if="obj.is_handled">
                     <span v-if="obj.is_handled">
-                      <i class="green check icon"></i>
+                      <i class="success check icon"></i>
                       <translate translate-context="Content/*/*/Short">Resolved</translate>
                     </span>
                   </td>
                   <td v-else>
-                    <i class="red x icon"></i>
+                    <i class="dangerx icon"></i>
                     <translate translate-context="Content/*/*/Short">Unresolved</translate>
                   </td>
                 </tr>
@@ -215,14 +215,14 @@
               v-if="obj.is_handled === false"
               @click="resolve(true)"
               :class="['ui', {loading: isLoading}, 'button']">
-              <i class="green check icon"></i>&nbsp;
+              <i class="success check icon"></i>&nbsp;
               <translate translate-context="Content/*/Button.Label/Verb">Resolve</translate>
             </button>
             <button
               v-if="obj.is_handled === true"
               @click="resolve(false)"
               :class="['ui', {loading: isLoading}, 'button']">
-              <i class="yellow redo icon"></i>&nbsp;
+              <i class="warning redo icon"></i>&nbsp;
               <translate translate-context="Content/*/Button.Label">Unresolve</translate>
             </button>
             <template v-for="action in actions">
@@ -357,7 +357,7 @@ export default {
           modalContent: this.$pgettext('Content/Moderation/Popup,Paragraph', 'This will delete the object associated with this report and mark the report as resolved. The deletion is irreversible.'),
           modalConfirmLabel: this.$pgettext('*/*/*/Verb', 'Delete'),
           icon: 'x',
-          iconColor: 'red',
+          iconColor: 'danger',
           show: (obj) => { return !!obj.target },
           dangerous: true,
           handler: () => {
diff --git a/front/src/components/manage/moderation/UserRequestCard.vue b/front/src/components/manage/moderation/UserRequestCard.vue
index 8a67fa2cc..4d7a3526e 100644
--- a/front/src/components/manage/moderation/UserRequestCard.vue
+++ b/front/src/components/manage/moderation/UserRequestCard.vue
@@ -41,15 +41,15 @@
                   </td>
                   <td>
                     <template v-if="obj.status === 'pending'">
-                      <i class="yellow hourglass icon"></i>
+                      <i class="warning hourglass icon"></i>
                       <translate translate-context="Content/Library/*/Short">Pending</translate>
                     </template>
                     <template v-else-if="obj.status === 'refused'">
-                      <i class="red x icon"></i>
+                      <i class="dangerx icon"></i>
                       <translate translate-context="Content/*/*/Short">Refused</translate>
                     </template>
                     <template v-else-if="obj.status === 'approved'">
-                      <i class="green check icon"></i>
+                      <i class="success check icon"></i>
                       <translate translate-context="Content/*/*/Short">Approved</translate>
                     </template>
                   </td>
@@ -118,14 +118,14 @@
                 v-if="obj.status === 'pending' || obj.status === 'refused'"
                 @click="approve(true)"
                 :class="['ui', {loading: isLoading}, 'button']">
-                <i class="green check icon"></i>&nbsp;
+                <i class="success check icon"></i>&nbsp;
                 <translate translate-context="Content/*/Button.Label/Verb">Approve</translate>
               </button>
               <button
                 v-if="obj.status === 'pending'"
                 @click="approve(false)"
                 :class="['ui', {loading: isLoading}, 'button']">
-                <i class="red x icon"></i>&nbsp;
+                <i class="dangerx icon"></i>&nbsp;
                 <translate translate-context="Content/*/Button.Label">Refuse</translate>
               </button>
             </div>
diff --git a/front/src/components/manage/users/InvitationForm.vue b/front/src/components/manage/users/InvitationForm.vue
index 1d7023652..bfea62533 100644
--- a/front/src/components/manage/users/InvitationForm.vue
+++ b/front/src/components/manage/users/InvitationForm.vue
@@ -82,6 +82,3 @@ export default {
   }
 }
 </script>
-
-<style scoped>
-</style>
diff --git a/front/src/components/manage/users/InvitationsTable.vue b/front/src/components/manage/users/InvitationsTable.vue
index feeca041d..be5a92c7b 100644
--- a/front/src/components/manage/users/InvitationsTable.vue
+++ b/front/src/components/manage/users/InvitationsTable.vue
@@ -47,8 +47,8 @@
             <router-link :to="{name: 'manage.users.users.detail', params: {id: scope.obj.id }}">{{ scope.obj.owner.username }}</router-link>
           </td>
           <td>
-            <span v-if="scope.obj.users.length > 0" class="ui green basic label"><translate translate-context="Content/Admin/Table">Used</translate></span>
-            <span v-else-if="moment().isAfter(scope.obj.expiration_date)" class="ui red basic label"><translate translate-context="Content/Admin/Table">Expired</translate></span>
+            <span v-if="scope.obj.users.length > 0" class="ui success basic label"><translate translate-context="Content/Admin/Table">Used</translate></span>
+            <span v-else-if="moment().isAfter(scope.obj.expiration_date)" class="ui danger basic label"><translate translate-context="Content/Admin/Table">Expired</translate></span>
             <span v-else class="ui basic label"><translate translate-context="Content/Admin/Table">Not used</translate></span>
           </td>
           <td>
diff --git a/front/src/components/manage/users/UsersTable.vue b/front/src/components/manage/users/UsersTable.vue
index 33ceb2726..78afeae4e 100644
--- a/front/src/components/manage/users/UsersTable.vue
+++ b/front/src/components/manage/users/UsersTable.vue
@@ -52,8 +52,8 @@
             <span>{{ scope.obj.email }}</span>
           </td>
           <td>
-            <span v-if="scope.obj.is_active" class="ui basic green label"><translate translate-context="Content/Admin/Table">Active</translate></span>
-            <span v-else class="ui basic grey label"><translate translate-context="Content/Admin/Table">Inactive</translate></span>
+            <span v-if="scope.obj.is_active" class="ui basic success label"><translate translate-context="Content/Admin/Table">Active</translate></span>
+            <span v-else class="ui basic label"><translate translate-context="Content/Admin/Table">Inactive</translate></span>
           </td>
           <td>
             <human-date :date="scope.obj.date_joined"></human-date>
diff --git a/front/src/components/moderation/FilterModal.vue b/front/src/components/moderation/FilterModal.vue
index bad0b2a23..60a159998 100644
--- a/front/src/components/moderation/FilterModal.vue
+++ b/front/src/components/moderation/FilterModal.vue
@@ -38,7 +38,7 @@
     </div>
     <div class="actions">
       <div class="ui basic cancel button"><translate translate-context="*/*/Button.Label/Verb">Cancel</translate></div>
-      <div :class="['ui', 'green', {loading: isLoading}, 'button']" @click="hide"><translate translate-context="Popup/*/Button.Label">Hide content</translate></div>
+      <div :class="['ui', 'success', {loading: isLoading}, 'button']" @click="hide"><translate translate-context="Popup/*/Button.Label">Hide content</translate></div>
     </div>
   </modal>
 </template>
@@ -102,7 +102,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/moderation/ReportModal.vue b/front/src/components/moderation/ReportModal.vue
index 103cb203d..349e74992 100644
--- a/front/src/components/moderation/ReportModal.vue
+++ b/front/src/components/moderation/ReportModal.vue
@@ -74,7 +74,7 @@
       <div class="ui basic cancel button"><translate translate-context="*/*/Button.Label/Verb">Cancel</translate></div>
       <button
         v-if="canSubmit"
-        :class="['ui', 'green', {loading: isLoading}, 'button']"
+        :class="['ui', 'success', {loading: isLoading}, 'button']"
         type="submit" form="report-form">
         <translate translate-context="Popup/*/Button.Label">Submit report</translate>
       </button>
@@ -204,7 +204,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/notifications/NotificationRow.vue b/front/src/components/notifications/NotificationRow.vue
index 047c7b999..9c6b635f2 100644
--- a/front/src/components/notifications/NotificationRow.vue
+++ b/front/src/components/notifications/NotificationRow.vue
@@ -21,10 +21,10 @@
     </td>
     <td><human-date :date="item.activity.creation_date" /></td>
     <td class="read collapsing">
-      <span @click="markRead(false)" v-if="item.is_read" :title="labels.markUnread">
+      <span @click="markRead(false)" role="button" v-if="item.is_read" :title="labels.markUnread">
         <i class="redo icon" />
       </span>
-      <span @click="markRead(true)" v-else :title="labels.markRead">
+      <span @click="markRead(true)" role="button" v-else :title="labels.markRead">
         <i class="check icon" />
       </span>
     </td>
@@ -68,13 +68,13 @@ export default {
           if (a.related_object.approved === null) {
             message = this.labels.libraryPendingFollowMessage
             acceptFollow = {
-              buttonClass: 'green',
+              buttonClass: 'success',
               icon: 'check',
               label: this.$pgettext('Content/*/Button.Label/Verb', 'Approve'),
               handler: () => { self.approveLibraryFollow(a.related_object) }
             },
             rejectFollow = {
-              buttonClass: 'red',
+              buttonClass: 'danger',
               icon: 'x',
               label: this.$pgettext('Content/*/Button.Label/Verb', 'Reject'),
               handler: () => { self.rejectLibraryFollow(a.related_object) }
@@ -146,8 +146,3 @@ export default {
   }
 }
 </script>
-<style scoped>
-.read > span {
-  cursor: pointer;
-}
-</style>
diff --git a/front/src/components/playlists/Card.vue b/front/src/components/playlists/Card.vue
index 1d4b4ad2c..1cd22fa0c 100644
--- a/front/src/components/playlists/Card.vue
+++ b/front/src/components/playlists/Card.vue
@@ -4,7 +4,7 @@
       @click="$router.push({name: 'library.playlists.detail', params: {id: playlist.id }})"
       :class="['ui', 'head-image', 'squares']">
       <img v-lazy="url" v-for="(url, idx) in images" :key="idx" />
-      <play-button :icon-only="true" :is-playable="playlist.is_playable" :button-classes="['ui', 'circular', 'large', 'orange', 'icon', 'button']" :playlist="playlist"></play-button>
+      <play-button :icon-only="true" :is-playable="playlist.is_playable" :button-classes="['ui', 'circular', 'large', 'vibrant', 'icon', 'button']" :playlist="playlist"></play-button>
     </div>
     <div class="content">
       <strong>
@@ -18,7 +18,7 @@
     </div>
     <div class="extra content">
       <translate translate-context="*/*/*" :translate-params="{count: playlist.tracks_count}" :translate-n="playlist.tracks_count" translate-plural="%{ count } tracks">%{ count } track</translate>
-      <play-button class="right floated basic icon" :dropdown-only="true" :is-playable="playlist.is_playable" :dropdown-icon-classes="['ellipsis', 'horizontal', 'large', 'grey']" :playlist="playlist"></play-button>
+      <play-button class="right floated basic icon" :dropdown-only="true" :is-playable="playlist.is_playable" :dropdown-icon-classes="['ellipsis', 'horizontal', 'large really discrete']" :playlist="playlist"></play-button>
     </div>
   </div>
 </template>
diff --git a/front/src/components/playlists/CardList.vue b/front/src/components/playlists/CardList.vue
index 8ed740568..fe9d91620 100644
--- a/front/src/components/playlists/CardList.vue
+++ b/front/src/components/playlists/CardList.vue
@@ -21,7 +21,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/playlists/Editor.vue b/front/src/components/playlists/Editor.vue
index bb732301f..e508784b7 100644
--- a/front/src/components/playlists/Editor.vue
+++ b/front/src/components/playlists/Editor.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="ui text container">
+  <div class="ui text container component-playlist-editor">
     <playlist-form @updated="$emit('playlist-updated', $event)" :title="false" :playlist="playlist"></playlist-form>
     <h3 class="ui top attached header">
       <translate translate-context="Content/Playlist/Title">Playlist editor</translate>
@@ -10,7 +10,7 @@
         <translate translate-context="Content/Playlist/Paragraph">Syncing changes to server…</translate>
       </template>
       <template v-else-if="status === 'errored'">
-        <i class="red close icon"></i>
+        <i class="dangerclose icon"></i>
         <translate translate-context="Content/Playlist/Error message.Title">An error occurred while saving your changes</translate>
         <div v-if="errors.length > 0" class="ui negative message">
           <ul class="list">
@@ -21,15 +21,15 @@
       <div v-else-if="status === 'confirmDuplicateAdd'" class="ui warning message">
         <p translate-context="Content/Playlist/Paragraph"
             v-translate="{playlist: playlist.name}">Some tracks in your queue are already in this playlist:</p>
-        <ul id="duplicateTrackList" class="ui relaxed divided list">
+        <ul class="ui relaxed divided list duplicate-tracks-list">
           <li v-for="track in duplicateTrackAddInfo.tracks" class="ui item">{{ track }}</li>
         </ul>
         <button
-          class="ui small green button"
+          class="ui small success button"
           @click="insertMany(queueTracks, true)"><translate translate-context="*/Playlist/Button.Label/Verb">Add anyways</translate></button>
       </div>
       <template v-else-if="status === 'saved'">
-        <i class="green check icon"></i> <translate translate-context="Content/Playlist/Paragraph">Changes synced with server</translate>
+        <i class="success check icon"></i> <translate translate-context="Content/Playlist/Paragraph">Changes synced with server</translate>
       </template>
     </div>
     <div class="ui bottom attached segment">
@@ -47,7 +47,7 @@
           </translate>
         </div>
 
-      <dangerous-button :disabled="plts.length === 0" class="ui labeled right floated yellow icon button" :action="clearPlaylist">
+      <dangerous-button :disabled="plts.length === 0" class="ui labeled right floated warning icon button" :action="clearPlaylist">
         <i class="eraser icon"></i> <translate translate-context="*/Playlist/Button.Label/Verb">Clear playlist</translate>
         <p slot="modal-header" v-translate="{playlist: playlist.name}" translate-context="Popup/Playlist/Title"  :translate-params="{playlist: playlist.name}">
           Do you want to clear the playlist "%{ playlist }"?
@@ -72,7 +72,7 @@
                     {{ plt.track.artist.name }}
                 </td>
                 <td class="right aligned">
-                  <i @click.stop="removePlt(index)" class="circular red trash icon"></i>
+                  <i @click.stop="removePlt(index)" class="circular danger trash icon"></i>
                 </td>
               </tr>
             </draggable>
@@ -220,11 +220,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-#duplicateTrackList {
-  max-height: 10em;
-  overflow-y: auto;
-}
-</style>
diff --git a/front/src/components/playlists/Form.vue b/front/src/components/playlists/Form.vue
index 4fc905fc6..c0ebbe65d 100644
--- a/front/src/components/playlists/Form.vue
+++ b/front/src/components/playlists/Form.vue
@@ -130,7 +130,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/playlists/PlaylistModal.vue b/front/src/components/playlists/PlaylistModal.vue
index 567ee572c..19960287a 100644
--- a/front/src/components/playlists/PlaylistModal.vue
+++ b/front/src/components/playlists/PlaylistModal.vue
@@ -28,7 +28,7 @@
             class="ui small basic cancel button"><translate translate-context="*/*/Button.Label/Verb">Cancel</translate>
           </button>
           <button
-            class="ui small green button"
+            class="ui small success button"
             @click="addToPlaylist(lastSelectedPlaylist, true)">
               <translate translate-context="*/Playlist/Button.Label/Verb">Add anyways</translate></button>
         </div>
@@ -71,7 +71,7 @@
               <td>
                 <div
                   v-if="track"
-                  class="ui green icon basic small right floated button"
+                  class="ui success icon basic small right floated button"
                   :title="labels.addToPlaylist"
                   @click="addToPlaylist(playlist.id, false)">
                   <i class="plus icon"></i> <translate translate-context="Popup/Playlist/Table.Button.Label/Verb">Add track</translate>
@@ -81,7 +81,7 @@
           </tbody>
         </table>
         <template v-else>
-          <div class="ui small placeholder segment">
+          <div class="ui small placeholder segment component-placeholder">
             <div class="ui header">
                <translate translate-context="Popup/Playlist/EmptyState">No results matching your filter</translate>
             </div>
@@ -197,10 +197,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-.ui.small.placeholder.segment {
-  min-height: auto;
-}
-</style>
diff --git a/front/src/components/playlists/TrackPlaylistIcon.vue b/front/src/components/playlists/TrackPlaylistIcon.vue
index 3bfd4ce85..aafdfe4da 100644
--- a/front/src/components/playlists/TrackPlaylistIcon.vue
+++ b/front/src/components/playlists/TrackPlaylistIcon.vue
@@ -38,7 +38,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/components/playlists/Widget.vue b/front/src/components/playlists/Widget.vue
index 594abe395..a811c5b43 100644
--- a/front/src/components/playlists/Widget.vue
+++ b/front/src/components/playlists/Widget.vue
@@ -19,7 +19,7 @@
       <button
         v-if="$store.state.auth.authenticated"
         @click="$store.commit('playlists/chooseTrack', null)"
-        class="ui green icon labeled button"
+        class="ui success icon labeled button"
         >
         <i class="list icon"></i>
         <translate translate-context="Content/Home/CreatePlaylist">
@@ -105,8 +105,3 @@ export default {
   }
 }
 </script>
-<style scoped>
-.refresh.icon {
-  float: right;
-}
-</style>
diff --git a/front/src/components/radios/Button.vue b/front/src/components/radios/Button.vue
index 7e1ff40c5..38502bca6 100644
--- a/front/src/components/radios/Button.vue
+++ b/front/src/components/radios/Button.vue
@@ -1,6 +1,6 @@
 <template>
-  <button @click="toggleRadio" :class="['ui', 'blue', {'inverted': running}, 'icon', 'labeled', 'button']">
-    <i class="ui feed icon"></i>
+  <button @click="toggleRadio" :class="['ui', 'primary', {'inverted': running}, 'icon', 'labeled', 'button']">
+    <i class="ui feed icon" role="button"></i>
     <template v-if="running"><translate translate-context="*/Player/Button.Label/Short, Verb">Stop radio</translate></template>
     <template v-else><translate translate-context="*/Queue/Button.Label/Short, Verb">Start radio</translate></template>
   </button>
@@ -43,10 +43,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-i {
-  cursor: pointer;
-}
-</style>
diff --git a/front/src/components/radios/Card.vue b/front/src/components/radios/Card.vue
index 55ccb4005..1303b03b0 100644
--- a/front/src/components/radios/Card.vue
+++ b/front/src/components/radios/Card.vue
@@ -18,7 +18,7 @@
         <div class="ui hidden divider"></div>
         <radio-button class="right floated button" :type="type" :custom-radio-id="customRadioId" :object-id="objectId"></radio-button>
         <router-link
-          class="ui basic yellow button right floated"
+          class="ui basic warning button right floated"
           v-if="$store.state.auth.authenticated && type === 'custom' && radio.user.id === $store.state.auth.profile.id"
           :to="{name: 'library.radios.edit', params: {id: customRadioId }}">
           <translate translate-context="Content/*/Button.Label/Verb">Edit</translate>
@@ -55,8 +55,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped lang="scss">
-
-</style>
diff --git a/front/src/components/semantic/Modal.vue b/front/src/components/semantic/Modal.vue
index e3334c685..78f953421 100644
--- a/front/src/components/semantic/Modal.vue
+++ b/front/src/components/semantic/Modal.vue
@@ -62,7 +62,3 @@ export default {
 
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped lang="scss">
-</style>
diff --git a/front/src/components/tags/List.vue b/front/src/components/tags/List.vue
index e5bba5b58..c3210010a 100644
--- a/front/src/components/tags/List.vue
+++ b/front/src/components/tags/List.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="tag-list">
+  <div class="component-tags-list">
     <router-link
       :to="{name: detailRoute, params: {id: tag}}"
       :class="['ui', 'circular', 'hashtag', 'label', labelClasses]"
@@ -38,12 +38,3 @@ export default {
   }
 }
 </script>
-<style lang="scss" scoped>
-.ui.circular.label {
-  padding-left: 1em !important;
-  padding-right: 1em !important;
-}
-.hashtag {
-  margin: 0.25em;
-}
-</style>
diff --git a/front/src/style/_css_vars.scss b/front/src/style/_css_vars.scss
new file mode 100644
index 000000000..a218896fd
--- /dev/null
+++ b/front/src/style/_css_vars.scss
@@ -0,0 +1,5 @@
+& {
+  @each $name, $value in $fwVars {
+    --#{$name}: #{$value};
+  }
+}
diff --git a/front/src/style/_main.scss b/front/src/style/_main.scss
index ce47d9736..bcd41d204 100644
--- a/front/src/style/_main.scss
+++ b/front/src/style/_main.scss
@@ -1,786 +1,56 @@
-/*
-
-███████╗███████╗███╗   ███╗ █████╗ ███╗   ██╗████████╗██╗ ██████╗    ██╗   ██╗██╗
-██╔════╝██╔════╝████╗ ████║██╔══██╗████╗  ██║╚══██╔══╝██║██╔════╝    ██║   ██║██║
-███████╗█████╗  ██╔████╔██║███████║██╔██╗ ██║   ██║   ██║██║         ██║   ██║██║
-╚════██║██╔══╝  ██║╚██╔╝██║██╔══██║██║╚██╗██║   ██║   ██║██║         ██║   ██║██║
-███████║███████╗██║ ╚═╝ ██║██║  ██║██║ ╚████║   ██║   ██║╚██████╗    ╚██████╔╝██║
-╚══════╝╚══════╝╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═══╝   ╚═╝   ╚═╝ ╚═════╝     ╚═════╝ ╚═╝
-
-  Import this file into your LESS project to use Semantic UI without build tools
-*/
-
-// Those fomantic-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 "~fomantic-ui-css/components/reset.css";
-// we use our custom site css here to avoid loading google font
-@import "./site";
-
-/* Elements */
-@import "~fomantic-ui-css/components/button.css";
-@import "~fomantic-ui-css/components/container.css";
-@import "~fomantic-ui-css/components/divider.css";
-// @import "~fomantic-ui-css/components/flag.css";
-@import "~fomantic-ui-css/components/header.css";
-@import "~fomantic-ui-css/components/icon.css";
-@import "~fomantic-ui-css/components/image.css";
-@import "~fomantic-ui-css/components/input.css";
-@import "~fomantic-ui-css/components/label.css";
-@import "~fomantic-ui-css/components/list.css";
-@import "~fomantic-ui-css/components/loader.css";
-@import "~fomantic-ui-css/components/placeholder.css";
-// @import "~fomantic-ui-css/components/rail.css";
-// @import "~fomantic-ui-css/components/reveal.css";
-@import "~fomantic-ui-css/components/segment.css";
-@import "~fomantic-ui-css/components/step.css";
-
-/* Collections */
-// @import "~fomantic-ui-css/components/breadcrumb.css";
-@import "~fomantic-ui-css/components/form.css";
-@import "~fomantic-ui-css/components/grid.css";
-@import "~fomantic-ui-css/components/menu.css";
-@import "~fomantic-ui-css/components/message.css";
-@import "~fomantic-ui-css/components/table.css";
-
-/* Views */
-// @import "~fomantic-ui-css/components/ad.css";
-@import "~fomantic-ui-css/components/card.css";
-// @import "~fomantic-ui-css/components/comment.css";
-@import "~fomantic-ui-css/components/feed.css";
-@import "~fomantic-ui-css/components/item.css";
-@import "~fomantic-ui-css/components/statistic.css";
-
-/* Modules */
-// @import "~fomantic-ui-css/components/accordion.css";
-@import "~fomantic-ui-css/components/checkbox.css";
-@import "~fomantic-ui-css/components/dimmer.css";
-@import "~fomantic-ui-css/components/dropdown.css";
-// @import "~fomantic-ui-css/components/embed.css";
-@import "~fomantic-ui-css/components/modal.css";
-// @import "~fomantic-ui-css/components/nag.css";
-@import "~fomantic-ui-css/components/popup.css";
-@import "~fomantic-ui-css/components/progress.css";
-// @import "~fomantic-ui-css/components/rating.css";
-@import "~fomantic-ui-css/components/search.css";
-// @import "~fomantic-ui-css/components/shape.css";
-@import "~fomantic-ui-css/components/sidebar.css";
-@import "~fomantic-ui-css/components/sticky.css";
-@import "~fomantic-ui-css/components/tab.css";
-@import "~fomantic-ui-css/components/text.css";
-@import "~fomantic-ui-css/components/toast.css";
-@import "~fomantic-ui-css/components/transition.css";
-
-
-
-// we do the import here instead in main.js
-// as resolve order is not deterministric in webpack
-// and we end up with CSS rules not applied,
-// see https://github.com/webpack/webpack/issues/215
-@import "./vendor/media";
+@use "./_vars" as *;
 
+// not in vars because not meant to be overriden
 $desktop-sidebar-width: 275px;
 $widedesktop-sidebar-width: 275px;
 $bottom-player-height: 4rem;
 
-html,
-body {
-  @include media("<=desktop") {
-    font-size: 90%;
-  }
-}
-
-html {
-  scroll-behavior: smooth;
-}
-@media screen and (prefers-reduced-motion: reduce) {
-	html {
-		scroll-behavior: auto;
-	}
-}
-#app {
-  font-family: "Avenir", Helvetica, Arial, sans-serif;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  display: flex;
-  min-height: 100vh;
-  flex-direction: column;
-  &.has-bottom-player {
-    padding-bottom: $bottom-player-height;
-    .toast-container {
-      bottom: $bottom-player-height + 1rem;
-    }
-  }
-}
-
-#footer {
-  border-bottom: none;
-  border-top: 1px solid rgba(34, 36, 38, 0.15);
-}
-#app > main, #app > .main {
-  flex: 1;
-}
-
-.instance-chooser {
-  margin-top: 2em;
-}
-
-.ui.wide.left.sidebar {
-  @include media(">desktop") {
-    width: $desktop-sidebar-width;
-  }
-
-  @include media(">widedesktop") {
-    width: $widedesktop-sidebar-width;
-  }
-}
-
-#app {
-  > .main.pusher,
-  > .footer {
-    position: relative;
-    @include media(">desktop") {
-      margin-left: $desktop-sidebar-width !important;
-    }
-
-    @include media(">widedesktop") {
-      margin-left: $widedesktop-sidebar-width !important;;
-    }
-    transform: none !important;
-  }
-}
-
-.main.pusher.hidden {
-  display: none;
-}
-.main.pusher > .ui.secondary.menu {
-  margin-left: 0;
-  margin-right: 0;
-  border: none;
-  overflow-y: auto;
-  .ui.item {
-    border: none;
-    border-bottom-style: none;
-    margin-bottom: 0px;
-  }
-  @include media(">tablet") {
-    padding: 0 2.5rem;
-  }
-  .item {
-    padding-top: 1.5em;
-    padding-bottom: 1.5em;
-  }
-}
-
-.ui.stripe.segment,
-#footer {
-  padding: 1em;
-  &.ui.container {
-    margin: 0;
-  }
-  @include media(">tablet") {
-    padding: 2em;
-  }
-  @include media(">widedesktop") {
-    padding: 3em;
-  }
-}
-
-.stripe.segment > .secondary.menu:last-child {
-  position: absolute;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  border-bottom: none;
-}
-.center.aligned.menu {
-  justify-content: center;
-}
-.text.center.aligned {
-  text-align: center;
-}
-.ellipsis:not(.icon) {
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  overflow: hidden;
-}
-.ellipsis-rows tr > {
-  td:nth-child(1) {
-    max-width: 4em;
-  }
-  td:nth-child(2) {
-    position: relative;
-    &:before {
-      content: '&nbsp;';
-      visibility: hidden;
-    }
-    > * {
-      position: absolute;
-      left: 0;
-      right: 0;
-      white-space: nowrap;
-      overflow: hidden;
-      text-overflow: ellipsis;
-      margin-right: 0.5em;
-    }
-  }
-}
-.ui.small.text.container {
-  max-width: 500px !important;
-}
-
-.button.icon.tiny {
-  padding: 0.5em !important;
-}
-
-.sidebar {
-  .logo {
-    &.bordered.icon {
-      padding: .5em .41em !important;
-    }
-    path {
-      fill: white;
-    }
-  }
-  .tab {
-    flex-direction: column;
-  }
-}
-
-.link {
-  cursor: pointer;
-}
-
-.ui.really.basic.button {
-  &:not(:focus) {
-    box-shadow: none !important;
-    background-color: none !important;
-  }
-}
-
-.floated.buttons .button ~ .dropdown {
-  border-left: none;
-}
-
-.ui.icon.header .circular.icon {
-  display: flex;
-  justify-content: center;
-}
-
-.header-buttons > .buttons {
-  display: inline-block;
-  padding: 0.2em;
-  margin: 0;
-  font-size: 1em;
-  .buttons {
-    margin: 0;
-  }
-
-}
-
-a {
-  cursor: pointer;
-}
-.segment.hidden {
-  display: none;
-}
-.hidden:not(.divider) {
-  display: none !important;
-}
-
-.nomargin {
-  margin: 0 !important;
-}
-button.reset {
-  border: none;
-  margin: 0;
-  padding: 0;
-  width: auto;
-  overflow: visible;
-
-  background: transparent;
-
-  /* inherit font & color from ancestor */
-  color: inherit;
-  font: inherit;
-
-  /* Normalize `line-height`. Cannot be changed from `normal` in Firefox 4+. */
-  line-height: normal;
-
-  /* Corrects font smoothing for webkit */
-  -webkit-font-smoothing: inherit;
-  -moz-osx-font-smoothing: inherit;
-  /* Corrects inability to style clickable `input` types in iOS */
-  -webkit-appearance: none;
-  text-align: inherit;
-}
-
-.text.align.left {
-  text-align: left;
-}
-.ui.table > caption {
-  font-weight: bold;
-  padding: 0.5em;
-  text-align: left;
-}
-[role="button"] {
-  cursor: pointer;
-}
-
-.left.floated {
-  float: left;
-}
-
-.right.floated {
-  float: right;
-}
-
-
-[data-tooltip]::after {
-  white-space: normal;
-  width: 500px;
-  max-width: 500px;
-  z-index: 999;
-}
-
-label .tooltip {
-  margin-left: 1em;
-}
-
-.ui.list .list.icon {
-  padding-left: 0;
-}
-
-
-.ui.dropdown .item[disabled] {
-  display: none;
-}
-
-span.diff.added {
-  background-color:rgba(0, 255, 0, 0.25);
-}
-
-
-span.diff.removed {
-  background-color: rgba(255, 0, 0, 0.25);
-}
-
-.table-wrapper {
-  display: block;
-  overflow-x: auto;
-}
-
-td.align.right {
-  text-align: right;
-}
-
-.ui.pagination.menu {
-  margin-top: 1em;
-  + span {
-    margin-left: 1em;
-  }
-}
-
-.card .description {
-  word-wrap: break-word;
-}
-
-.ui.checkbox label {
-  cursor: pointer;
-}
-
-input + .help {
-  margin-top: 0.5em;
-}
-
-
-.expandable {
-  &:not(.expanded) {
-    overflow: hidden;
-    max-height: 15vh;
-    background: linear-gradient(top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 90%, rgba(0, 0, 0, 0.3) 100%);
-  }
-}
-
-.ui.borderless.button {
-  border: none !important;
-  box-shadow: none !important;
-  padding-left: 0;
-  padding-right: 0;
-}
-.column .ui.text.container {
-  max-width: 100% !important;
-}
-
-.ui.small.divider {
-  margin: 0.5rem 0;
-}
-.ui.very.small.divider {
-  margin: 0.25rem 0;
-}
-.ui.horizontal.divider {
-  display: inline-block;
-  margin: 0 0.5em;
-}
-
-.queue.segment.player-focused #queue-grid #player {
-  @include media("<desktop") {
-    padding-bottom: $bottom-player-height + 2rem;
-  }
-}
-.queue-controls {
-
-  @include media("<desktop") {
-    height: $bottom-player-height;
-  }
-}
-
-.desktop-and-up {
-  @include media("<desktop") {
-    display: none !important;
-  }
-}
-.tablet-and-up {
-  @include media("<tablet") {
-    display: none !important;
-  }
-}
-.tablet-and-below {
-  @include media(">desktop") {
-    display: none !important;
-  }
-}
-.mobile-only {
-  @include media(">tablet") {
-    display: none !important;
-  }
-}
-:not(.menu) > {
-  a, .link {
-    &:not(.button):not(.list) {
-      &:hover {
-        text-decoration: underline;
-      }
-    }
-  }
-}
-.ui.cards.app-cards {
-  $card-width: 14em;
-  $card-height: 23em;
-  $small-card-width: 11em;
-  $small-card-height: 20em;
-  .app-card {
-    display: flex;
-    width: $small-card-width;
-    height: $small-card-height;
-    font-size: 95%;
-    @include media(">tablet") {
-      font-size: 100%;
-      width: $card-width;
-      height: $card-height;
-    }
-    .content:not(.extra) {
-      padding: 0.25em 0.5em 0;
-      @include media(">tablet") {
-        padding: 0.5em 1em 0;
-      }
-    }
-    .content.extra {
-      padding: 0.25em 0.5em;
-      @include media(">tablet") {
-        padding: 0.5em 1em;
-      }
-    }
-    .head-image {
-      height: $small-card-width;
-      @include media(">tablet") {
-        height: $card-width;
-      }
-      background-size: cover !important;
-      background-position: center !important;
-      display: flex !important;
-      justify-content: flex-end !important;
-      align-items: flex-end !important;
-      .button {
-        margin: 0;
-      }
-      &.circular {
-        overflow: visible;
-        border-radius: 50% !important;
-        width: $small-card-width - 0.5em;
-        height: $small-card-width - 0.5em;
-        margin: 0.25em;
-        @include media(">tablet") {
-          width: $card-width - 1em;
-          height: $card-width - 1em;
-          margin: 0.5em;
-        }
-
-      }
-      &.padded {
-        margin: 0.5em;
-        border-radius: 0.25em !important;
-      }
-      &.squares {
-        display: block !important;
-        position: relative;
-        margin-bottom: -1px;
-        .button {
-          position: absolute !important;
-          bottom: 0.5em;
-          right: 0.5em;
-        }
-        img {
-          display: inline-block;
-          width: 50%;
-          height: 50%;
-          margin: 0;
-          border-radius: 0;
-          margin-bottom: -4px;
-        }
-        img:nth-child(1) {
-          border-top-left-radius: 3px;
-        }
-        img:nth-child(2) {
-          border-top-right-radius: 3px;
-        }
-      }
-    }
-    .extra {
-      border-top: 0 !important;
-    }
-    .content:not(.extra) {
-      &, & * {
-        white-space: nowrap;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        padding-bottom: 0;
-      }
-    }
-    .floating.dropdown > .icon {
-      margin-right: 0;
-    }
-  }
-}
-
-//  channels stuff
-@mixin two-images {
-  margin-right: 1em;
-  position: relative;
-  width: 3.5em;
-  height: 3.5em;
-  &.large {
-    width: 15em;
-    height: 15em;
-    img {
-      width: 11em;
-    }
-  }
-  img {
-    width: 2.5em;
-    position: absolute;
-    &:last-child {
-      bottom: 0;
-      left: 0;
-    }
-    &:first-child {
-      top: 0;
-      right: 0;
-    }
-  }
-}
-.two-images {
-  @include two-images;
-}
-.channel-entry-card, .channel-serie-card {
-  display: flex;
-  width: 100%;
-  align-items: center;
-  justify-content: space-between;
-  .controls {
-    margin-right: 1em;
-  }
-  .image {
-    width: 3em;
-    height: 3em;
-    margin-right: 1em;
-    line-height: 3em;
-    text-align: center;
-    font-weight: bold;
-  }
-  .two-images {
-    @include two-images;
-  }
-  .content {
-    flex-grow: 1;
-  }
-}
-.album-entries {
-  > div {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-  }
-  .content {
-    flex-grow: 1;
-  }
-}
-.ui.artist-label {
-  .icon {
-    width: 2em;
-  }
-  &.rounded {
-    border-radius: 5em;
-    padding: 0.2em 0.75em 0.2em 0.2em;
-    line-height: 2em;
-    img {
-      border-radius: 50%;
-      vertical-align: middle;
-    }
-  }
-}
-.album-entry, .channel-entry-card {
-  border-radius: 5px;
-  padding: 0.5em;
-  .meta {
-    text-align: right;
-    min-width: 7em;
-  }
-  > div {
-    padding: 0.25em;
-    &:not(:last-child) {
-      margin-right: 0.25em;
-    }
-  }
-  &.active {
-    background: rgba(155, 155, 155, 0.2);
-  }
-  &:hover {
-    background: rgba(155, 155, 155, 0.1);
-  }
-  .favorite-icon.tiny.button {
-    border: none !important;
-    padding: 0 !important;
-    margin: 0 0.5em;
-  }
-}
-.channel-image {
-  border: 1px solid rgba(0, 0, 0, 0.15);
-  background-color: white;
-  border-radius: 0.3em;
-  &.large {
-    width: 8em !important;
-  }
-}
-.content-form {
-  .segment:first-child {
-    min-height: 15em;
-  }
-  .ui.secondary.menu {
-    margin-top: -0.5em;
-  }
-  .input {
-    width: 100%;
-  }
-}
-.placeholder.image {
-  background-color: rgba(0,0,0,.08);
-  width: 3em;
-  height: 3em;
-  &.large {
-    width: 8em;
-    height: 8em;
-  }
-  max-width: 100%;
-  display: block;
-  &.circular {
-    border-radius: 50%;
-  }
-  &.static {
-    animation: none;
-  }
-}
-.channel-type.field .radio {
-  display: block;
-  padding: 1.5em;
-  &.selected {
-    background-color: rgba(0, 0, 0, 0.05);
-  }
-}
-.header.with-actions {
-  @include media(">tablet") {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-  }
-  .actions {
-    font-weight: normal;
-    font-size: 0.6em;
-  }
-
-}
-.file-uploads.channels.ui.button {
-  display: block;
-  padding: 2em 1em;
-  width: 100%;
-  box-shadow: none;
-  border-style: dashed !important;
-  border: 2px solid rgba(50, 50, 50, 0.5);
-  font-size: 1.2em;
-  padding: 0;
-  > div:not(.divider) {
-    padding: 1em;
-  }
-}
-
-.channel-file {
-  display: flex;
-  align-items: top;
-  margin-bottom: 1em;
-  > :first-child {
-    width: 3em;
-  }
-  .header {
-    margin: 0 1em;
-    .sub.header {
-      margin-top: 0.5em;
-    }
-  }
-}
-.modal > .header {
-  text-align: center;
-}
-.ui.header .content {
-  display: block;
-}
-.with-image.item {
-  display: flex !important;
-  align-items: center;
-  height: 3em;
-  img.image {
-    width: 3em;
-    height: 3em;
-    margin-right: 1em;
-  }
-  .icon.image {
-    width: 3em;
-    margin-right: 1em;
-    display: block;
-  }
-  .content {
-    font-size: 1em;
-  }
-  .meta {
-    margin-top: 0.5em;
-    font-size: 0.8em;
-  }
-}
-.button-group {
-  > *:not(:first-child) {
-    margin-left: 0.5em;
-  }
-}
-@import "./themes/_light.scss";
-@import "./themes/_dark.scss";
+@import "./globals/_fomantic.scss";
+@import "./vendor/_media.scss";
+
+@import "./globals/_app.scss";
+@import "./globals/_channels.scss";
+@import "./globals/_layout.scss";
+@import "./globals/_typography.scss";
+@import "./globals/_utils.scss";
+
+@import "./components/_action_table.scss";
+@import "./components/_album_card.scss";
+@import "./components/_avatar.scss";
+@import "./components/_button.scss";
+@import "./components/_card.scss";
+@import "./components/_content_form.scss";
+@import "./components/_copy_input.scss";
+@import "./components/_empty_state.scss";
+@import "./components/_form.scss";
+@import "./components/_file_upload.scss";
+@import "./components/_header.scss";
+@import "./components/_label.scss";
+@import "./components/_modal.scss";
+@import "./components/_pagination.scss";
+@import "./components/_placeholder.scss";
+@import "./components/_play_button.scss";
+@import "./components/_player.scss";
+@import "./components/_playlist_editor.scss";
+@import "./components/_queue.scss";
+@import "./components/_settings_group.scss";
+@import "./components/_sidebar.scss";
+@import "./components/_table.scss";
+@import "./components/_tags_list.scss";
+@import "./components/_tooltip.scss";
+@import "./components/_track_widget.scss";
+@import "./components/_track_table.scss";
+@import "./components/_user_link.scss";
+@import "./components/_volume_control.scss";
+
+@import "./pages/_about.scss";
+@import "./pages/_admin_account_detail.scss";
+@import "./pages/_admin_domain_detail.scss";
+@import "./pages/_admin_library.scss";
+@import "./pages/_home.scss";
+@import "./pages/_library.scss";
+@import "./pages/_notifications.scss";
+@import "./pages/_profile.scss";
+
+@import "./themes/light/_main.scss";
+@import "./themes/dark/_main.scss";
diff --git a/front/src/style/_site.scss b/front/src/style/_site.scss
deleted file mode 100644
index eba288882..000000000
--- a/front/src/style/_site.scss
+++ /dev/null
@@ -1,202 +0,0 @@
-/*!
- * # Semantic UI 2.4.1 - Site
- * http://github.com/semantic-org/semantic-ui/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-
-/*******************************
-             Page
-*******************************/
-
-html,
-body {
-  height: 100%;
-}
-html {
-  font-size: 14px;
-}
-body {
-  margin: 0px;
-  padding: 0px;
-  overflow-x: hidden;
-  min-width: 320px;
-  background: #FFFFFF;
-  font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;
-  font-size: 14px;
-  line-height: 1.4285em;
-  color: rgba(0, 0, 0, 0.87);
-  font-smoothing: antialiased;
-}
-
-
-/*******************************
-             Headers
-*******************************/
-
-h1,
-h2,
-h3,
-h4,
-h5 {
-  font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;
-  line-height: 1.28571429em;
-  margin: calc(2rem -  0.14285714em ) 0em 1rem;
-  font-weight: bold;
-  padding: 0em;
-}
-h1 {
-  min-height: 1rem;
-  font-size: 2rem;
-}
-h2 {
-  font-size: 1.71428571rem;
-}
-h3 {
-  font-size: 1.28571429rem;
-}
-h4 {
-  font-size: 1.07142857rem;
-}
-h5 {
-  font-size: 1rem;
-}
-h1:first-child,
-h2:first-child,
-h3:first-child,
-h4:first-child,
-h5:first-child {
-  margin-top: 0em;
-}
-h1:last-child,
-h2:last-child,
-h3:last-child,
-h4:last-child,
-h5:last-child {
-  margin-bottom: 0em;
-}
-
-
-/*******************************
-             Text
-*******************************/
-
-p {
-  margin: 0em 0em 1em;
-  line-height: 1.4285em;
-}
-p:first-child {
-  margin-top: 0em;
-}
-p:last-child {
-  margin-bottom: 0em;
-}
-
-/*-------------------
-        Links
---------------------*/
-
-a {
-  color: #4183C4;
-  text-decoration: none;
-}
-a:hover {
-  color: #1e70bf;
-  text-decoration: none;
-}
-
-
-/*******************************
-         Scrollbars
-*******************************/
-
-
-
-/*******************************
-          Highlighting
-*******************************/
-
-
-/* Site */
-::-webkit-selection {
-  background-color: #CCE2FF;
-  color: rgba(0, 0, 0, 0.87);
-}
-::-moz-selection {
-  background-color: #CCE2FF;
-  color: rgba(0, 0, 0, 0.87);
-}
-::selection {
-  background-color: #CCE2FF;
-  color: rgba(0, 0, 0, 0.87);
-}
-
-/* Form */
-textarea::-webkit-selection,
-input::-webkit-selection {
-  background-color: rgba(100, 100, 100, 0.4);
-  color: rgba(0, 0, 0, 0.87);
-}
-textarea::-moz-selection,
-input::-moz-selection {
-  background-color: rgba(100, 100, 100, 0.4);
-  color: rgba(0, 0, 0, 0.87);
-}
-textarea::selection,
-input::selection {
-  background-color: rgba(100, 100, 100, 0.4);
-  color: rgba(0, 0, 0, 0.87);
-}
-
-/* Force Simple Scrollbars */
-body ::-webkit-scrollbar {
-  -webkit-appearance: none;
-  width: 10px;
-  height: 10px;
-}
-body ::-webkit-scrollbar-track {
-  background: rgba(0, 0, 0, 0.1);
-  border-radius: 0px;
-}
-body ::-webkit-scrollbar-thumb {
-  cursor: pointer;
-  border-radius: 5px;
-  background: rgba(0, 0, 0, 0.25);
-  -webkit-transition: color 0.2s ease;
-  transition: color 0.2s ease;
-}
-body ::-webkit-scrollbar-thumb:window-inactive {
-  background: rgba(0, 0, 0, 0.15);
-}
-body ::-webkit-scrollbar-thumb:hover {
-  background: rgba(128, 135, 139, 0.8);
-}
-
-/* Inverted UI */
-body .ui.inverted::-webkit-scrollbar-track {
-  background: rgba(255, 255, 255, 0.1);
-}
-body .ui.inverted::-webkit-scrollbar-thumb {
-  background: rgba(255, 255, 255, 0.25);
-}
-body .ui.inverted::-webkit-scrollbar-thumb:window-inactive {
-  background: rgba(255, 255, 255, 0.15);
-}
-body .ui.inverted::-webkit-scrollbar-thumb:hover {
-  background: rgba(255, 255, 255, 0.35);
-}
-
-
-/*******************************
-        Global Overrides
-*******************************/
-
-
-
-/*******************************
-         Site Overrides
-*******************************/
diff --git a/front/src/style/_vars.scss b/front/src/style/_vars.scss
new file mode 100644
index 000000000..2d574c68b
--- /dev/null
+++ b/front/src/style/_vars.scss
@@ -0,0 +1,115 @@
+// All vars in this file are made available as CSS vars.
+// For instance, $vibrant-color: #f2711c !default;
+// will be exposed as --vibrant-color: #f2711c
+$font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif !default;
+$vibrant-color: #f2711c !default;
+$vibrant-hover-color: #f26202 !default;
+$vibrant-active-color: #cf590c !default;
+$vibrant-focus-color: var(--vibrant-hover-color) !default;
+
+$success-color: #21BA45 !default;
+$success-hover-color: #16ab39 !default;
+$success-active-color: #198f35 !default;
+$success-focus-color: var(--$success-hover-color) !default;
+
+
+$primary-color: #2185D0 !default;
+$primary-hover-color: #1678c2 !default;
+$primary-active-color: #1a69a4 !default;
+$primary-focus-color: var(--primary-hover-color) !default;
+
+$warning-color: #FBBD08 !default;
+$warning-hover-color: #eaae00 !default;
+$warning-active-color: #cd9903 !default;
+$warning-focus-color: var(--warning-hover-color) !default;
+
+$danger-color: #DB2828 !default;
+$danger-hover-color: #ff392b !default;
+$danger-active-color: #b21e1e !default;
+$danger-focus-color: var(--danger-hover-color) !default;
+
+$link-color: #4183C4 !default;
+$link-text-decoration: none !default;
+$link-hover-color: #1e70bf !default;
+$link-hover-text-decoration: underline !default;
+
+$sidebar-background: #2D2F33 !default;
+$sidebar-header-background: var(--sidebar-background) !default;
+$sidebar-header-color: var(--sidebar-color) !default;
+$sidebar-active-item-background: rgba(255, 255, 255, 0.15) !default;
+$sidebar-header-box-shadow: none !default;
+$sidebar-box-shadow: 0 0 20px rgba(34,36,38,.15) !default;
+$site-background: white !default;
+
+$text-color: rgba(0, 0, 0, 0.87) !default;
+$discrete-text-color: rgba(0, 0, 0, 0.72) !default;
+$really-discrete-text-color: rgba(0, 0, 0, 0.57) !default;
+$text-selection-background: #CCE2FF !default;
+$text-selection-color: rgba(0, 0, 0, 0.87) !default;
+
+$header-color: var(--text-color) !default;
+
+$main-color: var(--text-color) !default;
+$main-background: var(--site-background) !default;
+
+$menu-item-color: var(--text-color) !default;
+$secondary-menu-background: var(--site-background) !default;
+$secondary-menu-box-shadow: inset 0px -2px 0px 0px rgba(34, 36, 38, 0.15) !default;
+$secondary-menu-active-item-box-shadow: inset 0 -2px 0 0 #000 !default;
+$secondary-menu-active-item-color: rgba(0, 0, 0, 0.95) !default;
+$secondary-menu-hover-item-color: var(--secondary-menu-active-item-color) !default;
+$inverted-menu-item-color: hsla(0,0%,100%,.75) !default;
+$inverted-menu-item-header-color: hsla(0,0%,100%,.95) !default;
+$menu-inverted-active-item-color: white !default;
+
+$button-basic-background: transparent !default;
+$button-basic-color: rgba(0, 0, 0, 0.6) !default;
+$button-basic-box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.15) inset !default;
+$button-basic-hover-background: var(--site-background) !default;
+$button-basic-hover-color: var(--text-color) !default;
+$button-basic-hover-box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.35) inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset !default;
+
+$button-orange-background: var(--vibrant-color) !default;
+
+$form-label-color: var(--text-color) !default;
+$input-selection-background: rgba(100, 100, 100, 0.4) !default;
+$input-selection-color: var(--text-selection-color) !default;
+$input-background: white !default;
+$input-color: var(--text-color) !default;
+$input-focus-color: var(--input-color) !default;
+$input-focus-background: var(--input-background) !default;
+$input-placeholder-color: var(--really-discrete-text-color) !default;
+$input-focus-placeholder-color: var(--discrete-text-color) !default;
+
+$divider-color: rgba(34, 36, 38, 0.15) !default;
+$divider: 1px solid var(--divider-color) !default;
+
+$modal-background: var(--site-background) !default;
+
+$dropdown-background: var(--input-background) !default;
+$dropdown-color: var(--input-color) !default;
+$dropdown-header-color: var(--dropdown-color) !default;
+$dropdown-item-color: var(--dropdown-color) !default;
+$dropdown-item-hover-color: var(--dropdown-item-color) !default;
+$dropdown-item-hover-background: rgba(0, 0, 0, 0.05) !default;
+$dropdown-item-selected-color: var(--dropdown-item-hover-color) !default;
+$dropdown-item-selected-background: var(--dropdown-item-hover-background) !default;
+
+$segment-color: var(--text-color) !default;
+$segment-background: var(--site-background) !default;
+
+$player-color: rgba(255, 255, 255, 0.9) !default;
+$player-background: #1B1C1D !default;
+
+$table-background: transparent !default;
+$table-border: var(--divider) !default;
+
+$tooltip-background: var(--site-background) !default;
+$tooltip-color: var(--text-color) !default;
+
+$card-background: var(--site-background) !default;
+$card-text-colar: var(-text-color) !default;
+$card-box-shadow: 0 1px 3px 0 #D4D4D5, 0 0 0 1px #D4D4D5 !default;
+
+$dimmer-background: rgba(255, 255, 255, 0.85) !default;
+$dimmer-color: var(--text-color) !default;
diff --git a/front/src/style/components/_action_table.scss b/front/src/style/components/_action_table.scss
new file mode 100644
index 000000000..babe47dc9
--- /dev/null
+++ b/front/src/style/components/_action_table.scss
@@ -0,0 +1,8 @@
+.component-action-table {
+  .count.field {
+    font-weight: normal;
+  }
+  .ui.form .inline.fields {
+    margin: 0;
+  }
+}
diff --git a/front/src/style/components/_album_card.scss b/front/src/style/components/_album_card.scss
new file mode 100644
index 000000000..96a8a2d81
--- /dev/null
+++ b/front/src/style/components/_album_card.scss
@@ -0,0 +1,7 @@
+.component-album-card {
+
+  &.card.app-card > .head-image > .icon {
+    margin: 0.5em;
+
+  }
+}
diff --git a/front/src/style/components/_avatar.scss b/front/src/style/components/_avatar.scss
new file mode 100644
index 000000000..f7c3506ef
--- /dev/null
+++ b/front/src/style/components/_avatar.scss
@@ -0,0 +1,12 @@
+
+.ui.tiny.avatar.image {
+  position: relative;
+  top: -0.5em;
+  width: 3em;
+}
+
+.ui.circular.avatar {
+  width: 28px;
+  height: 28px;
+  font-size: 1em !important;
+}
diff --git a/front/src/style/components/_button.scss b/front/src/style/components/_button.scss
new file mode 100644
index 000000000..3b198b5a4
--- /dev/null
+++ b/front/src/style/components/_button.scss
@@ -0,0 +1,83 @@
+:not(.active) button.title {
+  outline-color: white;
+}
+button, *[role="button"] {
+  * {
+    cursor: pointer;
+  }
+}
+
+.reset.button {
+  margin-top: 0.5em;
+}
+
+button.reset {
+  border: none;
+  margin: 0;
+  padding: 0;
+  width: auto;
+  overflow: visible;
+
+  background: transparent;
+
+  /* inherit font & color from ancestor */
+  color: inherit;
+  font: inherit;
+
+  /* Normalize `line-height`. Cannot be changed from `normal` in Firefox 4+. */
+  line-height: normal;
+
+  /* Corrects font smoothing for webkit */
+  -webkit-font-smoothing: inherit;
+  -moz-osx-font-smoothing: inherit;
+  /* Corrects inability to style clickable `input` types in iOS */
+  -webkit-appearance: none;
+  text-align: inherit;
+}
+
+.button.icon.tiny {
+  padding: 0.5em !important;
+}
+
+.floated.buttons .button ~ .dropdown {
+  border-left: none;
+}
+
+.header-buttons > .buttons {
+  display: inline-block;
+  padding: 0.2em;
+  margin: 0;
+  font-size: 1em;
+  .buttons {
+    margin: 0;
+  }
+
+}
+
+.ui.really.basic.button {
+  &:not(:focus) {
+    box-shadow: none !important;
+    background-color: none !important;
+  }
+}
+
+[role="button"] {
+  cursor: pointer;
+}
+
+
+.button-group {
+  > *:not(:first-child) {
+    margin-left: 0.5em;
+  }
+}
+
+.ui.borderless.button {
+  border: none !important;
+  box-shadow: none !important;
+  padding-left: 0;
+  padding-right: 0;
+}
+.ui.dropdown .menu > .item {
+  background: var(--dropdown-background);
+}
diff --git a/front/src/style/components/_card.scss b/front/src/style/components/_card.scss
new file mode 100644
index 000000000..894d9eeed
--- /dev/null
+++ b/front/src/style/components/_card.scss
@@ -0,0 +1,125 @@
+
+
+.card .description {
+  word-wrap: break-word;
+}
+
+.ui.cards > .card,
+.ui.card {
+  color: var(--card-text-color);
+  background: var(--card-background);
+  &:not(.flat) {
+    box-shadow: var(--card-box-shadow);
+  }
+  .content,
+  .header,
+  .content .header,
+  .content .description,
+  .description {
+    color: var(--card-text-color);
+  }
+  .extra,
+  .meta {
+    color: var(--discrete-text-color);
+  }
+}
+
+
+.ui.cards.app-cards {
+  $card-width: 14em;
+  $card-height: 23em;
+  $small-card-width: 11em;
+  $small-card-height: 20em;
+  .app-card {
+    display: flex;
+    width: $small-card-width;
+    height: $small-card-height;
+    font-size: 95%;
+    @include media(">tablet") {
+      font-size: 100%;
+      width: $card-width;
+      height: $card-height;
+    }
+    .content:not(.extra) {
+      padding: 0.25em 0.5em 0;
+      @include media(">tablet") {
+        padding: 0.5em 1em 0;
+      }
+    }
+    .content.extra {
+      padding: 0.25em 0.5em;
+      @include media(">tablet") {
+        padding: 0.5em 1em;
+      }
+    }
+    .head-image {
+      height: $small-card-width;
+      @include media(">tablet") {
+        height: $card-width;
+      }
+      background-size: cover !important;
+      background-position: center !important;
+      display: flex !important;
+      justify-content: flex-end !important;
+      align-items: flex-end !important;
+      .button {
+        margin: 0;
+      }
+      &.circular {
+        overflow: visible;
+        border-radius: 50% !important;
+        width: $small-card-width - 0.5em;
+        height: $small-card-width - 0.5em;
+        margin: 0.25em;
+        @include media(">tablet") {
+          width: $card-width - 1em;
+          height: $card-width - 1em;
+          margin: 0.5em;
+        }
+
+      }
+      &.padded {
+        margin: 0.5em;
+        border-radius: 0.25em !important;
+      }
+      &.squares {
+        display: block !important;
+        position: relative;
+        margin-bottom: -1px;
+        .button {
+          position: absolute !important;
+          bottom: 0.5em;
+          right: 0.5em;
+        }
+        img {
+          display: inline-block;
+          width: 50%;
+          height: 50%;
+          margin: 0;
+          border-radius: 0;
+          margin-bottom: -4px;
+        }
+        img:nth-child(1) {
+          border-top-left-radius: 3px;
+        }
+        img:nth-child(2) {
+          border-top-right-radius: 3px;
+        }
+      }
+    }
+    .extra {
+      border-top: 0 !important;
+    }
+    .content:not(.extra) {
+      &, & * {
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        padding-bottom: 0;
+      }
+    }
+    .floating.dropdown > .icon {
+      margin-right: 0;
+    }
+  }
+}
diff --git a/front/src/style/components/_content_form.scss b/front/src/style/components/_content_form.scss
new file mode 100644
index 000000000..dc237d363
--- /dev/null
+++ b/front/src/style/components/_content_form.scss
@@ -0,0 +1,17 @@
+
+.content-form {
+  background: var(--input-background);
+  .segment {
+    background: none;
+  }
+  .segment:first-child {
+    min-height: 15em;
+  }
+  .ui.secondary.menu {
+    background: none;
+    margin-top: -0.5em;
+  }
+  .input {
+    width: 100%;
+  }
+}
diff --git a/front/src/style/components/_copy_input.scss b/front/src/style/components/_copy_input.scss
new file mode 100644
index 000000000..9353c29c7
--- /dev/null
+++ b/front/src/style/components/_copy_input.scss
@@ -0,0 +1,11 @@
+.component-copy-input {
+  .message {
+    position: absolute;
+    right: 0;
+    bottom: -3em;
+    padding: 0.3em;
+    box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.3);
+    background-color: white;
+    z-index: 999;
+  }
+}
diff --git a/front/src/style/components/_empty_state.scss b/front/src/style/components/_empty_state.scss
new file mode 100644
index 000000000..824719c90
--- /dev/null
+++ b/front/src/style/components/_empty_state.scss
@@ -0,0 +1,6 @@
+.component-empty-state {
+  .ui.header .content {
+    text-align: center;
+    display: block;
+  }
+}
diff --git a/front/src/style/components/_file_upload.scss b/front/src/style/components/_file_upload.scss
new file mode 100644
index 000000000..9a1a353b3
--- /dev/null
+++ b/front/src/style/components/_file_upload.scss
@@ -0,0 +1,34 @@
+
+.component-file-upload {
+  .file-uploads.ui.button {
+    display: block;
+    padding: 2em 1em;
+    width: 100%;
+    box-shadow: none;
+    border-style: dashed !important;
+    border: 2px solid rgba(50, 50, 50, 0.5);
+    font-size: 1.5em;
+  }
+  .file-uploads.channels.ui.button {
+    font-size: 1.2em;
+    padding: 0;
+    > div:not(.divider) {
+      padding: 1em;
+    }
+  }
+
+}
+.channel-file {
+  display: flex;
+  align-items: top;
+  margin-bottom: 1em;
+  > :first-child {
+    width: 3em;
+  }
+  .header {
+    margin: 0 1em;
+    .sub.header {
+      margin-top: 0.5em;
+    }
+  }
+}
diff --git a/front/src/style/components/_form.scss b/front/src/style/components/_form.scss
new file mode 100644
index 000000000..9983d1fd3
--- /dev/null
+++ b/front/src/style/components/_form.scss
@@ -0,0 +1,30 @@
+
+.ui.checkbox label {
+  cursor: pointer;
+}
+
+input + .help {
+  margin-top: 0.5em;
+}
+
+.component-form {
+  .parent.checkbox {
+    margin: 1em 0;
+  }
+  .child.checkbox {
+    margin-left: 1em;
+  }
+}
+
+.ui.labeled.input {
+  input,
+  .label {
+    background-color: var(--input-background);
+    &::placeholder {
+      color: var(--input-placeholder-color);
+    }
+  }
+}
+.ui.icon.input > .icon {
+  color: var(--input-color);
+}
diff --git a/front/src/style/components/_header.scss b/front/src/style/components/_header.scss
new file mode 100644
index 000000000..782a91b6e
--- /dev/null
+++ b/front/src/style/components/_header.scss
@@ -0,0 +1,17 @@
+
+.header.with-actions {
+  @include media(">tablet") {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+  .actions {
+    font-weight: normal;
+    font-size: 0.6em;
+  }
+
+}
+
+.ui.header .content {
+  display: block;
+}
diff --git a/front/src/style/components/_label.scss b/front/src/style/components/_label.scss
new file mode 100644
index 000000000..07ac474b4
--- /dev/null
+++ b/front/src/style/components/_label.scss
@@ -0,0 +1,10 @@
+.component-label {
+  &.huge.circular.label {
+    padding-left: 1em !important;
+    padding-right: 1em !important;
+  }
+  &.vertically-spaced.label.ui {
+    margin-top: 0.5em;
+    margin-bottom: 0.5em;
+  }
+}
diff --git a/front/src/style/components/_modal.scss b/front/src/style/components/_modal.scss
new file mode 100644
index 000000000..af6f2eaea
--- /dev/null
+++ b/front/src/style/components/_modal.scss
@@ -0,0 +1,4 @@
+
+.modal > .header {
+  text-align: center;
+}
diff --git a/front/src/style/components/_pagination.scss b/front/src/style/components/_pagination.scss
new file mode 100644
index 000000000..822c29d63
--- /dev/null
+++ b/front/src/style/components/_pagination.scss
@@ -0,0 +1,12 @@
+.component-pagination {
+  .ui.pagination.menu .item {
+    cursor: pointer;
+  }
+}
+
+.ui.pagination.menu {
+  margin-top: 1em;
+  + span {
+    margin-left: 1em;
+  }
+}
diff --git a/front/src/style/components/_placeholder.scss b/front/src/style/components/_placeholder.scss
new file mode 100644
index 000000000..2f78d06d6
--- /dev/null
+++ b/front/src/style/components/_placeholder.scss
@@ -0,0 +1,31 @@
+
+.placeholder.image {
+  background-color: rgba(0,0,0,.08);
+  width: 3em;
+  height: 3em;
+  &.large {
+    width: 8em;
+    height: 8em;
+  }
+  max-width: 100%;
+  display: block;
+  &.circular {
+    border-radius: 50%;
+  }
+  &.static {
+    animation: none;
+  }
+}
+
+.component-placeholder {
+  width: 100%;
+  &.ui.small.placeholder.segment {
+    min-height: auto;
+  }
+
+  &.ui.segment {
+    .field, textarea, .ui.input, .button {
+      max-width: 100%;
+    }
+  }
+}
diff --git a/front/src/style/components/_play_button.scss b/front/src/style/components/_play_button.scss
new file mode 100644
index 000000000..2ec68d5e2
--- /dev/null
+++ b/front/src/style/components/_play_button.scss
@@ -0,0 +1,5 @@
+.component-play-button {
+  button.item {
+    width: 100%;
+  }
+}
diff --git a/front/src/style/components/_player.scss b/front/src/style/components/_player.scss
new file mode 100644
index 000000000..f18de5985
--- /dev/null
+++ b/front/src/style/components/_player.scss
@@ -0,0 +1,219 @@
+
+.ui.bottom-player {
+  z-index: 999999;
+  width: 100%;
+  width: 100vw;
+  .ui.top.attached.progress {
+    top: 0;
+  }
+}
+.ui.bottom-player > .segment.fixed-controls {
+  color: var(--player-color);
+  background: var(--player-background);
+  width: 100%;
+  width: 100vw;
+  border-radius: 0;
+  padding: 0em;
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  margin: 0;
+  z-index: 1001;
+  height: $bottom-player-height;
+  .controls-row {
+    height: $bottom-player-height;
+    margin: 0 auto;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    @include media(">desktop") {
+      padding: 0 1em;
+      justify-content: space-around;
+    }
+  }
+  cursor: pointer;
+  .indicating.progress {
+    overflow: hidden;
+  }
+
+  .ui.progress .bar {
+    transition: none;
+  }
+
+  .ui.progress .buffer.bar {
+    position: absolute;
+  }
+
+  @keyframes MOVE-BG {
+    from {
+      transform: translateX(0px);
+    }
+    to {
+      transform: translateX(46px);
+    }
+  }
+  .discrete.link {
+    color: inherit;
+  }
+  .indicating.progress .bar {
+    left: -46px;
+    width: 200% !important;
+    color: grey;
+    background: repeating-linear-gradient(
+      -55deg,
+      grey 1px,
+      grey 10px,
+      transparent 10px,
+      transparent 20px
+    ) !important;
+
+    animation-name: MOVE-BG;
+    animation-duration: 2s;
+    animation-timing-function: linear;
+    animation-iteration-count: infinite;
+  }
+  .ui.progress:not([data-percent]):not(.indeterminate)
+    .bar.position:not(.buffer) {
+    background: var(--vibrant-color);
+    min-width: 0;
+  }
+
+  .track-controls {
+    display: flex;
+    align-items: center;
+    justify-content: start;
+    flex-grow: 1;
+    .image {
+      padding: 0.5em;
+      width: auto;
+      margin-right: 0.5em;
+      > img {
+        max-height: 3.7em;
+        max-width: 4.7em;
+      }
+    }
+  }
+  .controls {
+    min-width: 8em;
+    font-size: 1.1em;
+    @include media(">desktop") {
+      &:not(.fluid) {
+        width: 20%;
+      }
+      &.queue-controls {
+        width: 32.5%;
+      }
+      &.progress-controls {
+        width: 10%;
+      }
+      &.player-controls {
+        width: 15%;
+      }
+    }
+    &.small, .small {
+      @include media(">desktop") {
+        font-size: 0.9em;
+      }
+    }
+    .icon {
+      font-size: 1.1em;
+    }
+    .icon.large {
+      font-size: 1.4em;
+    }
+    &:not(.track-controls) {
+      @include media(">desktop") {
+        line-height: 1em;
+      }
+      justify-content: center;
+      align-items: center;
+      &.align-right {
+        justify-content: flex-end;
+      }
+      &.align-left {
+        justify-content: flex-start;
+      }
+      > * {
+        padding: 0.5em;
+      }
+    }
+    &.player-controls {
+      .icon {
+        margin: 0;
+      }
+    }
+
+  }
+}
+
+.component-player {
+  .controls {
+    display: flex;
+    justify-content: space-between;
+  }
+
+  .controls .icon.big {
+    cursor: pointer;
+    font-size: 2em !important;
+  }
+
+  .controls .icon {
+    cursor: pointer;
+    vertical-align: middle;
+  }
+  .timer {
+    font-size: 1.2em;
+  }
+  .looping {
+    i {
+      position: relative;
+    }
+    .ui.circular.label {
+      font-family: sans-serif;
+      position: absolute;
+      font-size: 0.5em !important;
+      bottom: -0.7rem;
+      right: -0.7rem;
+      padding: 2px 0 !important;
+      width: 15px !important;
+      height: 15px !important;
+      min-width: 15px !important;
+      min-height: 15px !important;
+      @include media(">desktop") {
+        font-size: 0.6em !important;
+      }
+    }
+  }
+  .shuffling.loader.inline {
+    margin: 0;
+  }
+  .control.circular.button {
+    padding: 0;
+    border: none;
+    background-color: transparent;
+    color: inherit;
+    &:focus {
+      box-shadow: none;
+    }
+
+  }
+  .fake-dropdown {
+    border: 1px solid gray;
+    border-radius: 3px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    min-width: 10em;
+    > * {
+      padding: 0.5em;
+
+    }
+    .position.control {
+      padding-right: 1em;
+      flex-grow: 1;
+    }
+    .angle.icon {
+      margin-right: 0;
+    }
+  }
+}
diff --git a/front/src/style/components/_playlist_editor.scss b/front/src/style/components/_playlist_editor.scss
new file mode 100644
index 000000000..f8bb78ed5
--- /dev/null
+++ b/front/src/style/components/_playlist_editor.scss
@@ -0,0 +1,7 @@
+.component-playlist-editor {
+
+  .duplicate-tracks-list {
+    max-height: 10em;
+    overflow-y: auto;
+  }
+}
diff --git a/front/src/style/components/_queue.scss b/front/src/style/components/_queue.scss
new file mode 100644
index 000000000..c4ad81df6
--- /dev/null
+++ b/front/src/style/components/_queue.scss
@@ -0,0 +1,233 @@
+
+.queue.segment.player-focused #queue-grid #player {
+  @include media("<desktop") {
+    padding-bottom: $bottom-player-height + 2rem;
+  }
+}
+.queue-controls {
+
+  @include media("<desktop") {
+    height: $bottom-player-height;
+  }
+}
+.ui.fixed-header.segment {
+  background-color: var(--site-background);
+  box-shadow: var(--secondary-menu-box-shadow);
+}
+.queue-enter-active, .queue-leave-active {
+  transition: all 0.2s ease-in-out;
+  .current-track, .queue-column {
+    opacity: 0;
+  }
+}
+.queue-enter, .queue-leave-to {
+  transform: translateY(100vh);
+  opacity: 0;
+}
+
+.component-queue {
+
+  .queue-controls {
+    @include media("<desktop") {
+      background: var(--site-background);
+    }
+  }
+  &.main {
+    position: absolute;
+    min-height: 100vh;
+    width: 100vw;
+    z-index: 1000;
+    padding-bottom: 3em;
+  }
+  &.main > .button {
+    position: fixed;
+    top: 1em;
+    right: 1em;
+    z-index: 9999999;
+    @include media("<desktop") {
+      display: none;
+    }
+  }
+  .queue.segment:not(.player-focused) {
+    #player {
+      @include media("<desktop") {
+        height: 0;
+        display: none;
+      }
+    }
+  }
+  .queue.segment #player {
+    padding: 0em;
+    > * {
+      padding: 0.5em;
+    }
+  }
+  .player-focused .grid > .ui.queue-column {
+    @include media("<desktop") {
+      display: none;
+    }
+  }
+  .queue-column {
+    overflow-y: auto;
+  }
+  .queue-column .table {
+    margin-top: 4em !important;
+    margin-bottom: 4rem;
+  }
+  .ui.table > tbody > tr > td.controls {
+    text-align: right;
+  }
+  .ui.table > tbody > tr > td {
+    border: none;
+  }
+  td:first-child {
+    padding-left: 1em !important;
+  }
+  td:last-child {
+    padding-right: 1em !important;
+  }
+  .image-cell {
+    width: 4em;
+  }
+  .queue.segment {
+    @include media("<desktop") {
+      padding: 0;
+    }
+    > .container {
+      margin: 0 !important;
+    }
+  }
+  .handle {
+    @include media("<desktop") {
+      display: none;
+    }
+  }
+  .duration-cell {
+    @include media("<tablet") {
+      display: none;
+    }
+  }
+  .fixed-header {
+    position: fixed;
+    right: 0;
+    left: 0;
+    top: 0;
+    z-index: 9;
+    @include media("<desktop") {
+      padding: 1em;
+    }
+    @include media(">desktop") {
+      right: 1em;
+      left: 38%;
+    }
+    .header .content {
+      display: block;
+    }
+  }
+  .current-track #player {
+    font-size: 1.8em;
+    padding: 1em;
+    text-align: center;
+    display: flex;
+    position: fixed;
+    height: 100vh;
+    align-items: center;
+    justify-content: center;
+    flex-direction: column;
+    bottom: 0;
+    top: 0;
+    width: 32%;
+    @include media("<desktop") {
+      padding: 0.5em;
+      font-size: 1.5em;
+      width: 100%;
+      width: 100vw;
+      left: 0;
+      right: 0;
+      > .image {
+        max-height: 50vh;
+      }
+    }
+    > *:not(.image) {
+      width: 100%;
+    }
+    h1 {
+      margin: 0;
+      min-height: auto;
+    }
+  }
+  .progress-area {
+    overflow: hidden;
+  }
+  .progress-wrapper, .warning.message {
+    max-width: 25em;
+    margin: 0 auto;
+  }
+  .ui.progress .buffer.bar {
+    position: absolute;
+    background-color: rgba(255, 255, 255, 0.15);
+  }
+  .ui.progress:not([data-percent]):not(.indeterminate)
+    .bar.position:not(.buffer) {
+    background: var(--vibrant-color);
+  }
+
+  .indicating.progress .bar {
+    left: -46px;
+    width: 200% !important;
+    color: grey;
+    background: repeating-linear-gradient(
+      -55deg,
+      grey 1px,
+      grey 10px,
+      transparent 10px,
+      transparent 20px
+    ) !important;
+
+    animation-name: MOVE-BG;
+    animation-duration: 2s;
+    animation-timing-function: linear;
+    animation-iteration-count: infinite;
+  }
+  .ui.progress {
+    margin: 0.5rem 0;
+  }
+  .timer {
+    font-size: 0.7em;
+  }
+  .progress {
+    cursor: pointer;
+    .bar {
+      min-width: 0 !important;
+    }
+  }
+
+  .player-controls {
+    .control:not(:first-child) {
+      margin-left: 1em;
+    }
+    .icon {
+      font-size: 1.1em;
+    }
+  }
+
+  .handle {
+    cursor: grab;
+  }
+  .sortable-chosen {
+    cursor: grabbing;
+  }
+  .queue-item.sortable-ghost {
+    td {
+      border-top: 3px dashed rgba(0, 0, 0, 0.15) !important;
+      border-bottom: 3px dashed rgba(0, 0, 0, 0.15) !important;
+      &:first-child {
+        border-left: 3px dashed rgba(0, 0, 0, 0.15) !important;
+      }
+      &:last-child {
+        border-right: 3px dashed rgba(0, 0, 0, 0.15) !important;
+      }
+    }
+  }
+
+}
diff --git a/front/src/style/components/_settings_group.scss b/front/src/style/components/_settings_group.scss
new file mode 100644
index 000000000..9c625f1ca
--- /dev/null
+++ b/front/src/style/components/_settings_group.scss
@@ -0,0 +1,5 @@
+.component-settings-group {
+  .ui.checkbox p {
+    margin-top: 1rem;
+  }
+}
diff --git a/front/src/style/components/_sidebar.scss b/front/src/style/components/_sidebar.scss
new file mode 100644
index 000000000..c5037d0d1
--- /dev/null
+++ b/front/src/style/components/_sidebar.scss
@@ -0,0 +1,244 @@
+
+.ui.wide.left.sidebar {
+  @include media(">desktop") {
+    width: $desktop-sidebar-width;
+  }
+
+  @include media(">widedesktop") {
+    width: $widedesktop-sidebar-width;
+  }
+}
+
+
+.sidebar {
+  .logo {
+    &.bordered.icon {
+      padding: .5em .41em !important;
+    }
+    path {
+      fill: white;
+    }
+  }
+  .tab {
+    flex-direction: column;
+  }
+}
+
+.component-sidebar {
+  .ui.search .input {
+    flex: 1;
+    .prompt {
+      border-radius: 0;
+    }
+  }
+  .ui.search .results {
+    vertical-align: middle;
+  }
+  .ui.search .name {
+    vertical-align: middle;
+  }
+
+  &.sidebar {
+    overflow-y: visible !important;
+    background: var(--sidebar-background);
+    z-index: 1;
+    @include media(">desktop") {
+      display: flex;
+      flex-direction: column;
+      justify-content: space-between;
+      padding-bottom: 4em;
+    }
+    > nav {
+      flex-grow: 1;
+      overflow-y: auto;
+    }
+    @include media(">desktop") {
+      .menu .item.collapse-button-wrapper {
+        padding: 0;
+      }
+      .collapse.button {
+        display: none !important;
+      }
+    }
+    @include media("<=desktop") {
+      position: static !important;
+      width: 100% !important;
+      &.collapsed {
+        .player-wrapper,
+        .search,
+        .signup.segment,
+        nav.secondary {
+          display: none;
+        }
+      }
+    }
+
+    > div {
+      margin: 0;
+      background-color: var(--sidebar-background);
+    }
+    .menu.vertical {
+      background: transparent;
+    }
+  }
+
+  .ui.vertical.menu {
+    .item .item {
+      font-size: 1em;
+      > i.icon {
+        float: none;
+        margin: 0 0.5em 0 0;
+      }
+    }
+    .item.active {
+      border-right: 5px solid var(--vibrant-color);
+      border-radius: 0 !important;
+      background: var(--sidebar-active-item-background) !important;
+    }
+    .item.collapsed {
+      &:not(:focus) > .menu {
+        display: none;
+      }
+      .header {
+        margin-bottom: 0;
+      }
+    }
+    .collaspable.item .header {
+      cursor: pointer;
+    }
+  }
+  .ui.secondary.menu {
+    margin-left: 0;
+    margin-right: 0;
+  }
+  .tabs {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    overflow-y: auto;
+    justify-content: space-between;
+    @include media("<=desktop") {
+      max-height: 500px;
+    }
+  }
+  .ui.tab.active {
+    display: flex;
+  }
+  .tab[data-tab="queue"] {
+    flex-direction: column;
+    tr {
+      cursor: pointer;
+    }
+    td:nth-child(2) {
+      width: 55px;
+    }
+  }
+  .item .header .angle.icon {
+    float: right;
+    margin: 0;
+  }
+  .tab[data-tab="library"] {
+    flex-direction: column;
+    flex: 1 1 auto;
+    > .menu {
+      flex: 1;
+      flex-grow: 1;
+    }
+    > .player-wrapper {
+      width: 100%;
+    }
+  }
+  .sidebar .segment {
+    margin: 0;
+    border-radius: 0;
+  }
+
+  .ui.menu .item.inline.admin-dropdown.dropdown > .menu {
+    left: 0;
+    right: auto;
+  }
+  .ui.segment.header-wrapper {
+    background: var(--sidebar-header-background);
+    color: var(--sidebar-header-color);
+    box-shadow: var(--sidebar-header-box-shadow);
+    padding: 0;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    height: 4em;
+    margin-bottom: 0;
+    nav {
+      > .item, > .menu > .item > .item {
+        &:hover {
+          background-color: transparent;
+        }
+      }
+    }
+  }
+
+  nav.top.title-menu {
+    flex-grow: 1;
+    .item {
+      font-size: 1.5em;
+    }
+  }
+
+  .logo {
+    cursor: pointer;
+    display: inline-block;
+    margin: 0px;
+  }
+
+  &.collapsed .search-wrapper {
+    @include media("<desktop") {
+      padding: 0;
+    }
+  }
+  .ui.search {
+    display: flex;
+  }
+  .ui.message.black {
+    background: var(--sidebar-background);
+  }
+
+  .ui.mini.image {
+    width: 100%;
+  }
+  nav.top {
+    align-items: self-end;
+    padding: 0.5em 0;
+    > .item, > .right.menu > .item {
+      // color: rgba(255, 255, 255, 0.9) !important;
+      font-size: 1.2em;
+      &:hover, > .dropdown > .icon {
+        // color: rgba(255, 255, 255, 0.9) !important;
+      }
+      > .label, > .dropdown > .label {
+        font-size: 0.5em;
+        right: 1.7em;
+        bottom: -0.5em;
+        z-index: 0 !important;
+      }
+    }
+  }
+  .ui.user-dropdown > .text > .label {
+    margin-right: 0;
+  }
+  .logo-wrapper {
+    display: inline-block;
+    margin: 0 auto;
+    @include media("<desktop") {
+      margin: 0;
+    }
+    img {
+      height: 1em;
+      display: inline-block;
+      margin: 0 auto;
+    }
+    @include media(">tablet") {
+      img {
+        height: 1.5em;
+      }
+    }
+  }
+}
diff --git a/front/src/style/components/_table.scss b/front/src/style/components/_table.scss
new file mode 100644
index 000000000..61497f9ac
--- /dev/null
+++ b/front/src/style/components/_table.scss
@@ -0,0 +1,38 @@
+
+.ellipsis-rows tr > {
+  td:nth-child(1) {
+    max-width: 4em;
+  }
+  td:nth-child(2) {
+    position: relative;
+    &:before {
+      content: '&nbsp;';
+      visibility: hidden;
+    }
+    > * {
+      position: absolute;
+      left: 0;
+      right: 0;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      margin-right: 0.5em;
+    }
+  }
+}
+
+
+.ui.table > caption {
+  font-weight: bold;
+  padding: 0.5em;
+  text-align: left;
+}
+
+.table-wrapper {
+  display: block;
+  overflow-x: auto;
+}
+
+td.align.right {
+  text-align: right;
+}
diff --git a/front/src/style/components/_tags_list.scss b/front/src/style/components/_tags_list.scss
new file mode 100644
index 000000000..8b770cfa7
--- /dev/null
+++ b/front/src/style/components/_tags_list.scss
@@ -0,0 +1,9 @@
+.component-tags-list {
+  .ui.circular.label {
+    padding-left: 1em !important;
+    padding-right: 1em !important;
+  }
+  .hashtag {
+    margin: 0.25em;
+  }
+}
diff --git a/front/src/style/components/_tooltip.scss b/front/src/style/components/_tooltip.scss
new file mode 100644
index 000000000..a455ffc1b
--- /dev/null
+++ b/front/src/style/components/_tooltip.scss
@@ -0,0 +1,14 @@
+
+[data-tooltip]::after {
+  white-space: normal;
+  width: 500px;
+  max-width: 500px;
+  z-index: 999;
+
+  background-color: var(--tooltip-background);
+  color: var(--tooltip-color);
+}
+
+label .tooltip {
+  margin-left: 1em;
+}
diff --git a/front/src/style/components/_track_table.scss b/front/src/style/components/_track_table.scss
new file mode 100644
index 000000000..6dc7ea288
--- /dev/null
+++ b/front/src/style/components/_track_table.scss
@@ -0,0 +1,17 @@
+.component-track-table {
+  tr:not(:hover) .favorite-icon:not(.favorited) {
+    display: none;
+  }
+  pre {
+    overflow-x: scroll;
+  }
+  &.table-wrapper {
+    overflow: visible;
+  }
+  tr:not(:hover) {
+    .favorite-icon:not(.favorited),
+    .playlist-icon {
+      visibility: hidden;
+    }
+  }
+}
diff --git a/front/src/style/components/_track_widget.scss b/front/src/style/components/_track_widget.scss
new file mode 100644
index 000000000..35a804c70
--- /dev/null
+++ b/front/src/style/components/_track_widget.scss
@@ -0,0 +1,31 @@
+.component-track-widget {
+  .play-overlay {
+    position: absolute;
+    top: 4em;
+    left: 4em;
+    @include media(">tablet") {
+      top: 2.5em;
+      left: 2.5em;
+    }
+  }
+  .refresh.icon {
+    float: right;
+  }
+  .ui.divided.items > .item:last-child {
+    padding-bottom: 1em !important;
+  }
+
+  @include media(">tablet") {
+    .divided.items > .track-item.inline {
+      width: 25em;
+      float: left;
+      border-top: none;
+      &,
+      &:first-child {
+        margin-top: 0.5em !important;
+        margin-right: 0.5em !important;
+        padding: 1em 0 !important;
+      }
+    }
+  }
+}
diff --git a/front/src/style/components/_user_link.scss b/front/src/style/components/_user_link.scss
new file mode 100644
index 000000000..09a476ae1
--- /dev/null
+++ b/front/src/style/components/_user_link.scss
@@ -0,0 +1,7 @@
+
+.component-user-link {
+  .tiny.circular.avatar {
+    width: 1.7em;
+    height: 1.7em;
+  }
+}
diff --git a/front/src/style/components/_volume_control.scss b/front/src/style/components/_volume_control.scss
new file mode 100644
index 000000000..d034e80ca
--- /dev/null
+++ b/front/src/style/components/_volume_control.scss
@@ -0,0 +1,31 @@
+.component-volume-control {
+  display: flex;
+  line-height: inherit;
+  align-items: center;
+  position: relative;
+  overflow: visible;
+  input {
+    max-width: 5.5em;
+    height: 4px;
+  }
+  &.expandable {
+    .popup {
+      background-color: #1B1C1D;
+      position: absolute;
+      left: -4em;
+      top: -7em;
+      transform: rotate(-90deg);
+      display: flex;
+      align-items: center;
+      height: 2.5em;
+      padding: 0 0.5em;
+      box-shadow: 1px 1px 3px rgba(125, 125, 125, 0.5);
+    }
+    input {
+      max-width: 8.5em;
+    }
+    &:not(:hover):not(.expanded) .popup {
+      display: none;
+    }
+  }
+}
diff --git a/front/src/style/globals/_app.scss b/front/src/style/globals/_app.scss
new file mode 100644
index 000000000..63cea66af
--- /dev/null
+++ b/front/src/style/globals/_app.scss
@@ -0,0 +1,9 @@
+
+html {
+  scroll-behavior: smooth;
+}
+@media screen and (prefers-reduced-motion: reduce) {
+  html {
+    scroll-behavior: auto;
+  }
+}
diff --git a/front/src/style/globals/_channels.scss b/front/src/style/globals/_channels.scss
new file mode 100644
index 000000000..fd90cee29
--- /dev/null
+++ b/front/src/style/globals/_channels.scss
@@ -0,0 +1,117 @@
+
+//  channels stuff
+@mixin two-images {
+  margin-right: 1em;
+  position: relative;
+  width: 3.5em;
+  height: 3.5em;
+  &.large {
+    width: 15em;
+    height: 15em;
+    img {
+      width: 11em;
+    }
+  }
+  img {
+    width: 2.5em;
+    position: absolute;
+    &:last-child {
+      bottom: 0;
+      left: 0;
+    }
+    &:first-child {
+      top: 0;
+      right: 0;
+    }
+  }
+}
+.two-images {
+  @include two-images;
+}
+.channel-entry-card, .channel-serie-card {
+  display: flex;
+  width: 100%;
+  align-items: center;
+  justify-content: space-between;
+  .controls {
+    margin-right: 1em;
+  }
+  .image {
+    width: 3em;
+    height: 3em;
+    margin-right: 1em;
+    line-height: 3em;
+    text-align: center;
+    font-weight: bold;
+  }
+  .two-images {
+    @include two-images;
+  }
+  .content {
+    flex-grow: 1;
+  }
+}
+.album-entries {
+  > div {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+  .content {
+    flex-grow: 1;
+  }
+}
+.ui.artist-label {
+  .icon {
+    width: 2em;
+  }
+  &.rounded {
+    border-radius: 5em;
+    padding: 0.2em 0.75em 0.2em 0.2em;
+    line-height: 2em;
+    img {
+      border-radius: 50%;
+      vertical-align: middle;
+    }
+  }
+}
+.album-entry, .channel-entry-card {
+  border-radius: 5px;
+  padding: 0.5em;
+  .meta {
+    text-align: right;
+    min-width: 7em;
+  }
+  > div {
+    padding: 0.25em;
+    &:not(:last-child) {
+      margin-right: 0.25em;
+    }
+  }
+  &.active {
+    background: rgba(155, 155, 155, 0.2);
+  }
+  &:hover {
+    background: rgba(155, 155, 155, 0.1);
+  }
+  .favorite-icon.tiny.button {
+    border: none !important;
+    padding: 0 !important;
+    margin: 0 0.5em;
+  }
+}
+.channel-image {
+  border: 1px solid rgba(0, 0, 0, 0.15);
+  background-color: white;
+  border-radius: 0.3em;
+  &.large {
+    width: 8em !important;
+  }
+}
+.channel-type.field .radio {
+  display: block;
+  padding: 1.5em;
+  &.selected {
+    background-color: rgba(0, 0, 0, 0.05);
+  }
+}
diff --git a/front/src/style/globals/_fomantic.scss b/front/src/style/globals/_fomantic.scss
new file mode 100644
index 000000000..70b75109e
--- /dev/null
+++ b/front/src/style/globals/_fomantic.scss
@@ -0,0 +1,73 @@
+/*
+
+███████╗███████╗███╗   ███╗ █████╗ ███╗   ██╗████████╗██╗ ██████╗    ██╗   ██╗██╗
+██╔════╝██╔════╝████╗ ████║██╔══██╗████╗  ██║╚══██╔══╝██║██╔════╝    ██║   ██║██║
+███████╗█████╗  ██╔████╔██║███████║██╔██╗ ██║   ██║   ██║██║         ██║   ██║██║
+╚════██║██╔══╝  ██║╚██╔╝██║██╔══██║██║╚██╗██║   ██║   ██║██║         ██║   ██║██║
+███████║███████╗██║ ╚═╝ ██║██║  ██║██║ ╚████║   ██║   ██║╚██████╗    ╚██████╔╝██║
+╚══════╝╚══════╝╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═══╝   ╚═╝   ╚═╝ ╚═════╝     ╚═════╝ ╚═╝
+
+  Import this file into your LESS project to use Semantic UI without build tools
+*/
+
+// Those fomantic-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 "~fomantic-ui-css/tweaked/reset.css";
+@import "~fomantic-ui-css/tweaked/site.css";
+
+/* Elements */
+@import "~fomantic-ui-css/tweaked/button.css";
+@import "~fomantic-ui-css/tweaked/container.css";
+@import "~fomantic-ui-css/tweaked/divider.css";
+// @import "~fomantic-ui-css/tweaked/flag.css";
+@import "~fomantic-ui-css/tweaked/header.css";
+@import "~fomantic-ui-css/tweaked/icon.css";
+@import "~fomantic-ui-css/tweaked/image.css";
+@import "~fomantic-ui-css/tweaked/input.css";
+@import "~fomantic-ui-css/tweaked/label.css";
+@import "~fomantic-ui-css/tweaked/list.css";
+@import "~fomantic-ui-css/tweaked/loader.css";
+@import "~fomantic-ui-css/tweaked/placeholder.css";
+// @import "~fomantic-ui-css/tweaked/rail.css";
+// @import "~fomantic-ui-css/tweaked/reveal.css";
+@import "~fomantic-ui-css/tweaked/segment.css";
+@import "~fomantic-ui-css/tweaked/step.css";
+
+/* Collections */
+// @import "~fomantic-ui-css/tweaked/breadcrumb.css";
+@import "~fomantic-ui-css/tweaked/form.css";
+@import "~fomantic-ui-css/tweaked/grid.css";
+@import "~fomantic-ui-css/tweaked/menu.css";
+@import "~fomantic-ui-css/tweaked/message.css";
+@import "~fomantic-ui-css/tweaked/table.css";
+
+/* Views */
+// @import "~fomantic-ui-css/tweaked/ad.css";
+@import "~fomantic-ui-css/tweaked/card.css";
+// @import "~fomantic-ui-css/tweaked/comment.css";
+@import "~fomantic-ui-css/tweaked/feed.css";
+@import "~fomantic-ui-css/tweaked/item.css";
+@import "~fomantic-ui-css/tweaked/statistic.css";
+
+/* Modules */
+// @import "~fomantic-ui-css/tweaked/accordion.css";
+@import "~fomantic-ui-css/tweaked/checkbox.css";
+@import "~fomantic-ui-css/tweaked/dimmer.css";
+@import "~fomantic-ui-css/tweaked/dropdown.css";
+// @import "~fomantic-ui-css/tweaked/embed.css";
+@import "~fomantic-ui-css/tweaked/modal.css";
+// @import "~fomantic-ui-css/tweaked/nag.css";
+@import "~fomantic-ui-css/tweaked/popup.css";
+@import "~fomantic-ui-css/tweaked/progress.css";
+// @import "~fomantic-ui-css/tweaked/rating.css";
+@import "~fomantic-ui-css/tweaked/search.css";
+// @import "~fomantic-ui-css/tweaked/shape.css";
+@import "~fomantic-ui-css/tweaked/sidebar.css";
+@import "~fomantic-ui-css/tweaked/sticky.css";
+@import "~fomantic-ui-css/tweaked/tab.css";
+@import "~fomantic-ui-css/tweaked/text.css";
+@import "~fomantic-ui-css/tweaked/toast.css";
+@import "~fomantic-ui-css/tweaked/transition.css";
diff --git a/front/src/style/globals/_layout.scss b/front/src/style/globals/_layout.scss
new file mode 100644
index 000000000..90aa2742b
--- /dev/null
+++ b/front/src/style/globals/_layout.scss
@@ -0,0 +1,167 @@
+
+.main.with-background, .opaque {
+  background: var(--site-background);
+}
+
+.main.pusher > .ui.secondary.menu {
+  box-shadow: var(--secondary-menu-box-shadow);
+  background: var(--secondary-menu-background);
+  .ui.item {
+    &.active {
+      box-shadow: var(--secondary-menu-active-item-box-shadow);
+    }
+  }
+}
+
+.when-queue-focused {
+  .group {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    font-size: 1.1em;
+    > * {
+      margin-left: 0.5em;
+    }
+  }
+  @include media("<desktop") {
+    width: 100%;
+    justify-content: space-between !important;
+  }
+}
+#app:not(.queue-focused) {
+  .when-queue-focused {
+    @include media("<desktop") {
+      display: none;
+    }
+  }
+}
+
+#app {
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  display: flex;
+  min-height: 100vh;
+  flex-direction: column;
+  &.has-bottom-player {
+    padding-bottom: $bottom-player-height;
+    .toast-container {
+      bottom: $bottom-player-height + 1rem;
+    }
+  }
+}
+
+#footer {
+  border-bottom: none;
+  border-top: 1px solid rgba(34, 36, 38, 0.15);
+}
+#app > main, #app > .main {
+  flex: 1;
+}
+
+
+#app {
+  > .main.pusher,
+  > .footer {
+    position: relative;
+    @include media(">desktop") {
+      margin-left: $desktop-sidebar-width !important;
+    }
+
+    @include media(">widedesktop") {
+      margin-left: $widedesktop-sidebar-width !important;;
+    }
+    transform: none !important;
+  }
+}
+
+.ui.small.text.container {
+  max-width: 500px !important;
+}
+
+.column .ui.text.container {
+  max-width: 100% !important;
+}
+
+.main.pusher.hidden {
+  display: none;
+}
+.main.pusher > .ui.secondary.menu {
+  margin-left: 0;
+  margin-right: 0;
+  border: none;
+  overflow-y: auto;
+  .ui.item {
+    border: none;
+    border-bottom-style: none;
+    margin-bottom: 0px;
+  }
+  @include media(">tablet") {
+    padding: 0 2.5rem;
+  }
+  .item {
+    padding-top: 1.5em;
+    padding-bottom: 1.5em;
+  }
+}
+
+.ui.stripe.segment,
+#footer {
+  padding: 1em;
+  &.ui.container {
+    margin: 0;
+  }
+  @include media(">tablet") {
+    padding: 2em;
+  }
+  @include media(">widedesktop") {
+    padding: 3em;
+  }
+}
+
+.stripe.segment > .secondary.menu:last-child {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  border-bottom: none;
+}
+
+.ui.small.divider {
+  margin: 0.5rem 0;
+}
+.ui.very.small.divider {
+  margin: 0.25rem 0;
+}
+.ui.horizontal.divider {
+  display: inline-block;
+  margin: 0 0.5em;
+}
+
+.dimmed {
+  .ui.bottom-player {
+    @include media("<desktop") {
+      z-index: 0;
+    }
+  }
+}
+#app.queue-focused {
+  .queue-not-focused {
+    @include media("<desktop") {
+      display: none;
+    }
+  }
+}
+
+.main.pusher,
+.ui.vertical.segment {
+  color: var(--main-color);
+  background: var(--main-background);
+}
+.ui.segment:not(.basic):not(.vertical) {
+  color: var(--segment-color);
+  background: var(--segment-background);
+}
+
+.segment .ui.list .item {
+  background-color: transparent;
+}
diff --git a/front/src/style/globals/_typography.scss b/front/src/style/globals/_typography.scss
new file mode 100644
index 000000000..a13759423
--- /dev/null
+++ b/front/src/style/globals/_typography.scss
@@ -0,0 +1,18 @@
+
+html,
+body {
+  @include media("<=desktop") {
+    font-size: 90%;
+  }
+}
+
+.link:not(.list) {
+  text-decoration: var(--link-text-decoration);
+  &:hover {
+    text-decoration: var(--link-hover-text-decoration);
+  }
+}
+
+.ui.inverted.menu .item .header {
+  color: var(--inverted-menu-item-header-color);
+}
diff --git a/front/src/style/globals/_utils.scss b/front/src/style/globals/_utils.scss
new file mode 100644
index 000000000..da427f9fa
--- /dev/null
+++ b/front/src/style/globals/_utils.scss
@@ -0,0 +1,106 @@
+
+.center.aligned.menu {
+  justify-content: center;
+}
+.text.center.aligned {
+  text-align: center;
+}
+.ellipsis:not(.icon) {
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  overflow: hidden;
+}
+
+.link {
+  cursor: pointer;
+}
+
+.ui.icon.header .circular.icon {
+  display: flex;
+  justify-content: center;
+}
+
+
+a {
+  cursor: pointer;
+}
+.segment.hidden {
+  display: none;
+}
+.hidden:not(.divider) {
+  display: none !important;
+}
+
+.nomargin {
+  margin: 0 !important;
+}
+
+.text.align.left {
+  text-align: left;
+}
+
+.left.floated {
+  float: left;
+}
+
+.right.floated {
+  float: right;
+}
+
+
+.desktop-and-up {
+  @include media("<desktop") {
+    display: none !important;
+  }
+}
+.tablet-and-up {
+  @include media("<tablet") {
+    display: none !important;
+  }
+}
+.tablet-and-below {
+  @include media(">desktop") {
+    display: none !important;
+  }
+}
+.mobile-only {
+  @include media(">tablet") {
+    display: none !important;
+  }
+}
+
+.expandable {
+  &:not(.expanded) {
+    overflow: hidden;
+    max-height: 15vh;
+    background: linear-gradient(top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 90%, rgba(0, 0, 0, 0.3) 100%);
+  }
+}
+
+.ui.list .list.icon {
+  padding-left: 0;
+}
+
+.ui.dropdown .item[disabled] {
+  display: none;
+}
+
+span.diff.added {
+  background-color:rgba(0, 255, 0, 0.25);
+}
+
+
+span.diff.removed {
+  background-color: rgba(255, 0, 0, 0.25);
+}
+
+.default-cover {
+  background-image: url("./assets/audio/default-cover.png") !important;
+}
+
+.discrete {
+  color: var(--discrete-text-color);
+}
+.really.discrete {
+  color: var(--really-discrete-text-color);
+}
diff --git a/front/src/style/pages/_about.scss b/front/src/style/pages/_about.scss
new file mode 100644
index 000000000..608ea008b
--- /dev/null
+++ b/front/src/style/pages/_about.scss
@@ -0,0 +1,31 @@
+.page-about {
+  .ui.list .list.icon {
+    padding: 0;
+  }
+
+  h1.header, h1 .sub.header {
+    text-shadow: 0 2px 0 rgba(0,0,0,.8);
+    color: #fff !important;
+  }
+  h1.ui.header {
+    font-size: 3em;
+  }
+  h1.ui.header .sub.header {
+    font-size: 0.8em;
+  }
+  .main.pusher {
+    margin-top: 0;
+    min-height: 10em;
+  }
+  section.segment.head {
+    padding: 8em 3em;
+    background: linear-gradient(90deg, rgba(40,88,125,1) 0%, rgba(64,130,180,1) 100%);
+    background-repeat: no-repeat;
+    background-size: cover;
+  }
+  #pod {
+    font-size: 110%;
+    display: block;
+  }
+
+}
diff --git a/front/src/style/pages/_admin_account_detail.scss b/front/src/style/pages/_admin_account_detail.scss
new file mode 100644
index 000000000..bbedbf78a
--- /dev/null
+++ b/front/src/style/pages/_admin_account_detail.scss
@@ -0,0 +1,9 @@
+.page-admin-account-detail {
+  .placeholder.segment {
+    width: 100%;
+  }
+
+  .ui.input input[name="quota"] {
+    max-width: 7em;
+  }
+}
diff --git a/front/src/style/pages/_admin_domain_detail.scss b/front/src/style/pages/_admin_domain_detail.scss
new file mode 100644
index 000000000..d7432cfdf
--- /dev/null
+++ b/front/src/style/pages/_admin_domain_detail.scss
@@ -0,0 +1,5 @@
+.page-admin-domain-detail {
+  .placeholder.segment {
+    width: 100%;
+  }
+}
diff --git a/front/src/style/pages/_admin_library.scss b/front/src/style/pages/_admin_library.scss
new file mode 100644
index 000000000..b38d43a74
--- /dev/null
+++ b/front/src/style/pages/_admin_library.scss
@@ -0,0 +1,6 @@
+.page-admin-library {
+  .ui.menu .item > .label {
+    position: absolute;
+    right: -2em;
+  }
+}
diff --git a/front/src/style/pages/_home.scss b/front/src/style/pages/_home.scss
new file mode 100644
index 000000000..9bbe15a12
--- /dev/null
+++ b/front/src/style/pages/_home.scss
@@ -0,0 +1,33 @@
+
+.page_home {
+  .ui.list .list.icon {
+    padding: 0;
+  }
+
+  h1.header, h1 .sub.header {
+    text-shadow: 1px 1px 2px rgba(0,0,0,.8);
+    color: #fff !important;
+  }
+  h1.ui.header {
+    @include media(">tablet") {
+      font-size: 3em;
+    }
+  }
+  h1.ui.header .sub.header {
+    font-size: 0.8em;
+  }
+  .main.pusher {
+    margin-top: 0;
+    min-height: 10em;
+  }
+  section.segment.head {
+    padding: 8em 3em;
+    background: linear-gradient(90deg, rgba(40,88,125,1) 0%, rgba(64,130,180,1) 100%);
+    background-repeat: no-repeat;
+    background-size: cover;
+  }
+  #pod {
+    font-size: 110%;
+    display: block;
+  }
+}
diff --git a/front/src/style/pages/_library.scss b/front/src/style/pages/_library.scss
new file mode 100644
index 000000000..ccea2af4c
--- /dev/null
+++ b/front/src/style/pages/_library.scss
@@ -0,0 +1,27 @@
+
+.page-library {
+  .ui.segment.head {
+    background-size: cover;
+    background-position: center;
+    padding: 0;
+    .segment-content {
+      margin: 0 auto;
+      padding: 2em;
+      @include media(">tablet") {
+        padding: 4em;
+      }
+    }
+    &.with-background {
+      .header {
+        &,
+        .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);
+      }
+    }
+  }
+}
diff --git a/front/src/style/pages/_notifications.scss b/front/src/style/pages/_notifications.scss
new file mode 100644
index 000000000..274d8265b
--- /dev/null
+++ b/front/src/style/pages/_notifications.scss
@@ -0,0 +1,7 @@
+.page-notifications {
+  .event .ui.label.avatar {
+    font-size: 1.5em;
+    position: relative;
+    top: 0.35em;
+  }
+}
diff --git a/front/src/style/pages/_profile.scss b/front/src/style/pages/_profile.scss
new file mode 100644
index 000000000..69421cd1f
--- /dev/null
+++ b/front/src/style/pages/_profile.scss
@@ -0,0 +1,6 @@
+.page-profile {
+
+  .ui.header > img.image {
+    width: 8em;
+  }
+}
diff --git a/front/src/style/themes/_dark.scss b/front/src/style/themes/_dark.scss
deleted file mode 100644
index e72ae6e85..000000000
--- a/front/src/style/themes/_dark.scss
+++ /dev/null
@@ -1,300 +0,0 @@
-/* purgecss start ignore */
-
-$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;
-    }
-  }
-  .main.with-background, .opaque {
-    background-color: $background-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;
-    }
-    .link {
-      color: $link-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;
-      }
-    }
-    .segment .ui.list .item {
-      background-color: transparent;
-    }
-    .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;
-      &:not(.flat) {
-        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.list > .item .description {
-    color: $text-color;
-  }
-  .ui.link.list.list a.item:hover,
-  .ui.link.list.list .item a:not(.ui):not(.button):hover, .ui.list > .item a.header {
-    color: $link-color !important;
-  }
-  [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;
-    }
-  }
-
-  .ui.fixed-header.segment {
-    background-color: $background-color;
-    box-shadow: inset 0px -1px 0px 0px rgba(34, 36, 38, 0.15);
-  }
-  .ui.fixed-footer.segment {
-    box-shadow: inset 0px 1px 0px 0px rgba(34, 36, 38, 0.15);
-  }
-  @include media("<desktop") {
-    background-color: $background-color;
-  }
-}
-
-/* purgecss end ignore */
diff --git a/front/src/style/themes/_light.scss b/front/src/style/themes/_light.scss
deleted file mode 100644
index acfeee911..000000000
--- a/front/src/style/themes/_light.scss
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-.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;
-      }
-    }
-  }
-  .main.with-background, .opaque {
-    background-color: white;
-  }
-
-  .discrete {
-    color: rgba(0, 0, 0, 0.87);
-  }
-  .really.discrete {
-    color: rgba(0, 0, 0, 0.57);
-  }
-  .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);
-  }
-  .ui.fixed-header.segment {
-    background-color: white;
-    box-shadow: inset 0px -1px 0px 0px rgba(34, 36, 38, 0.15);
-  }
-  .ui.fixed-footer.segment {
-    box-shadow: inset 0px 1px 0px 0px rgba(34, 36, 38, 0.15);
-  }
-  .queue.segment .queue-controls {
-    @include media("<desktop") {
-      background-color: white;
-    }
-  }
-}
diff --git a/front/src/style/themes/dark/_main.scss b/front/src/style/themes/dark/_main.scss
new file mode 100644
index 000000000..6a9c54f75
--- /dev/null
+++ b/front/src/style/themes/dark/_main.scss
@@ -0,0 +1,7 @@
+@use "sass:meta";
+@use "./_vars.scss" as vars;
+
+.theme-dark {
+  $fwVars: meta.module-variables("vars");
+  @import "../../_css_vars.scss";
+}
diff --git a/front/src/style/themes/dark/_vars.scss b/front/src/style/themes/dark/_vars.scss
new file mode 100644
index 000000000..e0f3ef8a3
--- /dev/null
+++ b/front/src/style/themes/dark/_vars.scss
@@ -0,0 +1,33 @@
+/* purgecss start ignore */
+
+$site-background: rgb(43, 58, 66);
+$light-background-color: rgb(51, 71, 82);
+$input-background: rgb(65, 86, 97);
+$dimmer-background: rgba(43, 58, 66, 0.9);
+$text-color: rgb(223, 235, 240);
+$discrete-text-color: rgba(223, 235, 240, 0.904);
+$really-discrete-text-color: rgba(223, 235, 240, 0.804);
+$border-color: rgba(104, 136, 155, 0.5);
+$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);
+$link-hover-color: $link-color;
+
+$button-basic-box-shadow: 0px 0px 0px 1px $light-shadow-color inset;
+$button-basic-color: $text-color;
+$button-basic-hover-box-shadow: 0px 0px 0px 1px $text-color inset;
+$dropdown-item-hover-background: $light-background-color;
+$input-selection-background: $text-color;
+
+$divider-color: $border-color;
+
+$secondary-meny-box-shadow: inset 0px -2px 0px 0px $light-background-color;
+$secondary-menu-active-item-box-shadow: inset 0px -2px 0px 0px $shadow-color;
+$secondary-menu-active-item-color: $text-color;
+
+$segment-background: $light-background-color;
+$table-background: $input-background;
+
+@import "../../_vars";
diff --git a/front/src/style/themes/light/_main.scss b/front/src/style/themes/light/_main.scss
new file mode 100644
index 000000000..cbd52cc28
--- /dev/null
+++ b/front/src/style/themes/light/_main.scss
@@ -0,0 +1,9 @@
+@use "sass:meta";
+@use "../../_vars" as vars;
+
+$fwVars: meta.module-variables("vars");
+.theme-light {
+  & {
+    @import "../../_css_vars.scss";
+  }
+}
diff --git a/front/src/views/Notifications.vue b/front/src/views/Notifications.vue
index 3a10d399d..229c6fc97 100644
--- a/front/src/views/Notifications.vue
+++ b/front/src/views/Notifications.vue
@@ -1,5 +1,5 @@
 <template>
-  <main class="main pusher" v-title="labels.title">
+  <main class="main pusher page-notifications" v-title="labels.title">
     <section class="ui vertical aligned stripe segment">
       <div class="ui container">
         <div class="ui container" v-if="additionalNotifications">
@@ -205,12 +205,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style>
-.event .ui.label.avatar {
-  font-size: 1.5em;
-  position: relative;
-  top: 0.35em;
-}
-</style>
diff --git a/front/src/views/admin/ChannelDetail.vue b/front/src/views/admin/ChannelDetail.vue
index d5de3a7cb..e7f562e9f 100644
--- a/front/src/views/admin/ChannelDetail.vue
+++ b/front/src/views/admin/ChannelDetail.vue
@@ -60,7 +60,7 @@
                 </div>
                 <div class="ui buttons">
                   <dangerous-button
-                    :class="['ui', {loading: isLoading}, 'basic red button']"
+                    :class="['ui', {loading: isLoading}, 'basic danger button']"
                     :action="remove">
                     <translate translate-context="*/*/*/Verb">Delete</translate>
                     <p slot="modal-header"><translate translate-context="Popup/Library/Title">Delete this channel?</translate></p>
diff --git a/front/src/views/admin/library/AlbumDetail.vue b/front/src/views/admin/library/AlbumDetail.vue
index 9cce0c8f4..d90b36bea 100644
--- a/front/src/views/admin/library/AlbumDetail.vue
+++ b/front/src/views/admin/library/AlbumDetail.vue
@@ -74,7 +74,7 @@
                 </div>
                 <div class="ui buttons">
                   <dangerous-button
-                    :class="['ui', {loading: isLoading}, 'basic red button']"
+                    :class="['ui', {loading: isLoading}, 'basic danger button']"
                     :action="remove">
                     <translate translate-context="*/*/*/Verb">Delete</translate>
                     <p slot="modal-header"><translate translate-context="Popup/Library/Title">Delete this album?</translate></p>
diff --git a/front/src/views/admin/library/ArtistDetail.vue b/front/src/views/admin/library/ArtistDetail.vue
index f3db10de2..c6e64b2af 100644
--- a/front/src/views/admin/library/ArtistDetail.vue
+++ b/front/src/views/admin/library/ArtistDetail.vue
@@ -73,7 +73,7 @@
                 </div>
                 <div class="ui buttons">
                   <dangerous-button
-                    :class="['ui', {loading: isLoading}, 'basic red button']"
+                    :class="['ui', {loading: isLoading}, 'basic danger button']"
                     :action="remove">
                     <translate translate-context="*/*/*/Verb">Delete</translate>
                     <p slot="modal-header"><translate translate-context="Popup/Library/Title">Delete this artist?</translate></p>
diff --git a/front/src/views/admin/library/Base.vue b/front/src/views/admin/library/Base.vue
index 311fe9e77..8b2587d22 100644
--- a/front/src/views/admin/library/Base.vue
+++ b/front/src/views/admin/library/Base.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="main pusher"  v-title="labels.title">
+  <div class="main pusher page-admin-library" v-title="labels.title">
     <nav class="ui secondary pointing menu" role="navigation" :aria-label="labels.secondaryMenu">
       <router-link
         class="ui item"
@@ -44,10 +44,3 @@ export default {
   }
 }
 </script>
-
-<style scoped>
-.ui.menu .item > .label {
-  position: absolute;
-  right: -2em;
-}
-</style>
diff --git a/front/src/views/admin/library/EditsList.vue b/front/src/views/admin/library/EditsList.vue
index ec1ddb15e..8af28804c 100644
--- a/front/src/views/admin/library/EditsList.vue
+++ b/front/src/views/admin/library/EditsList.vue
@@ -27,7 +27,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/admin/library/LibraryDetail.vue b/front/src/views/admin/library/LibraryDetail.vue
index 412795d71..1e7cd35bf 100644
--- a/front/src/views/admin/library/LibraryDetail.vue
+++ b/front/src/views/admin/library/LibraryDetail.vue
@@ -54,7 +54,7 @@
                 </div>
                 <div class="ui buttons">
                   <dangerous-button
-                    :class="['ui', {loading: isLoading}, 'basic red button']"
+                    :class="['ui', {loading: isLoading}, 'basic danger button']"
                     :action="remove">
                     <translate translate-context="*/*/*/Verb">Delete</translate>
                     <p slot="modal-header"><translate translate-context="Popup/Library/Title">Delete this library?</translate></p>
diff --git a/front/src/views/admin/library/TagDetail.vue b/front/src/views/admin/library/TagDetail.vue
index 5d5dac235..bb896036e 100644
--- a/front/src/views/admin/library/TagDetail.vue
+++ b/front/src/views/admin/library/TagDetail.vue
@@ -37,7 +37,7 @@
                 </div>
                 <div class="ui buttons">
                   <dangerous-button
-                    :class="['ui', {loading: isLoading}, 'basic red button']"
+                    :class="['ui', {loading: isLoading}, 'basic danger button']"
                     :action="remove">
                     <translate translate-context="*/*/*/Verb">Delete</translate>
                     <p slot="modal-header"><translate translate-context="Popup/Library/Title">Delete this tag?</translate></p>
diff --git a/front/src/views/admin/library/TrackDetail.vue b/front/src/views/admin/library/TrackDetail.vue
index dab02cc7b..55d8a1aa2 100644
--- a/front/src/views/admin/library/TrackDetail.vue
+++ b/front/src/views/admin/library/TrackDetail.vue
@@ -74,7 +74,7 @@
                 </div>
                 <div class="ui buttons">
                   <dangerous-button
-                    :class="['ui', {loading: isLoading}, 'basic red button']"
+                    :class="['ui', {loading: isLoading}, 'basic danger button']"
                     :action="remove">
                     <translate translate-context="*/*/*/Verb">Delete</translate>
                     <p slot="modal-header"><translate translate-context="Popup/Library/Title">Delete this track?</translate></p>
diff --git a/front/src/views/admin/library/UploadDetail.vue b/front/src/views/admin/library/UploadDetail.vue
index eaef1cc4a..ea338b05f 100644
--- a/front/src/views/admin/library/UploadDetail.vue
+++ b/front/src/views/admin/library/UploadDetail.vue
@@ -61,7 +61,7 @@
                 </div>
                 <div class="ui buttons">
                   <dangerous-button
-                    :class="['ui', {loading: isLoading}, 'basic red button']"
+                    :class="['ui', {loading: isLoading}, 'basic danger button']"
                     :action="remove">
                     <translate translate-context="*/*/*/Verb">Delete</translate>
                     <p slot="modal-header"><translate translate-context="Popup/Library/Title">Delete this upload?</translate></p>
diff --git a/front/src/views/admin/moderation/AccountsDetail.vue b/front/src/views/admin/moderation/AccountsDetail.vue
index 5dbacb18e..aead3f1cc 100644
--- a/front/src/views/admin/moderation/AccountsDetail.vue
+++ b/front/src/views/admin/moderation/AccountsDetail.vue
@@ -1,5 +1,5 @@
 <template>
-  <main>
+  <main class="page-admin-account-detail">
     <div v-if="isLoading" class="ui vertical segment">
       <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
     </div>
@@ -59,7 +59,7 @@
             </div>
           </div>
           <div class="ui column">
-            <div v-if="!object.user" class="ui compact clearing placeholder segment">
+            <div v-if="!object.user" class="ui compact clearing placeholder segment component-placeholder">
               <template v-if="isLoadingPolicy">
                 <div class="paragraph">
                   <div class="line"></div>
@@ -556,13 +556,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-.placeholder.segment {
-  width: 100%;
-}
-.ui.input input[name="quota"] {
-  max-width: 7em;
-}
-</style>
diff --git a/front/src/views/admin/moderation/AccountsList.vue b/front/src/views/admin/moderation/AccountsList.vue
index 1505619f5..1fbc57ae6 100644
--- a/front/src/views/admin/moderation/AccountsList.vue
+++ b/front/src/views/admin/moderation/AccountsList.vue
@@ -27,7 +27,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/admin/moderation/DomainsDetail.vue b/front/src/views/admin/moderation/DomainsDetail.vue
index 651a4063e..38915b87d 100644
--- a/front/src/views/admin/moderation/DomainsDetail.vue
+++ b/front/src/views/admin/moderation/DomainsDetail.vue
@@ -1,5 +1,5 @@
 <template>
-  <main>
+  <main class="page-admin-domain-detail">
     <div v-if="isLoading" class="ui vertical segment">
       <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
     </div>
@@ -51,7 +51,7 @@
             </div>
           </div>
           <div class="ui column">
-            <div class="ui compact clearing placeholder segment">
+            <div class="ui compact clearing placeholder segment component-placeholder">
               <template v-if="isLoadingPolicy">
                 <div class="paragraph">
                   <div class="line"></div>
@@ -434,10 +434,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-.placeholder.segment {
-  width: 100%;
-}
-</style>
diff --git a/front/src/views/admin/moderation/DomainsList.vue b/front/src/views/admin/moderation/DomainsList.vue
index b42d4b228..325729230 100644
--- a/front/src/views/admin/moderation/DomainsList.vue
+++ b/front/src/views/admin/moderation/DomainsList.vue
@@ -19,7 +19,7 @@
             <label for="allowed"><translate translate-context="Content/Moderation/Action/Verb">Add to allow-list</translate></label>
           </div>
           <div class="field">
-            <button :class="['ui', {'loading': isCreating}, 'green', 'button']" type="submit" :disabled="isCreating">
+            <button :class="['ui', {'loading': isCreating}, 'success', 'button']" type="submit" :disabled="isCreating">
               <label for="domain"><translate translate-context="Content/Moderation/Button/Verb">Add</translate></label>
             </button>
           </div>
@@ -74,7 +74,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/admin/moderation/ReportsList.vue b/front/src/views/admin/moderation/ReportsList.vue
index dc6c17f61..3d3ccb991 100644
--- a/front/src/views/admin/moderation/ReportsList.vue
+++ b/front/src/views/admin/moderation/ReportsList.vue
@@ -227,7 +227,3 @@ export default {
 }
 
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/admin/moderation/RequestsList.vue b/front/src/views/admin/moderation/RequestsList.vue
index 8d38371c3..88f5eb0b9 100644
--- a/front/src/views/admin/moderation/RequestsList.vue
+++ b/front/src/views/admin/moderation/RequestsList.vue
@@ -162,7 +162,3 @@ export default {
 }
 
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/admin/users/InvitationsList.vue b/front/src/views/admin/users/InvitationsList.vue
index 5071dc661..96738983f 100644
--- a/front/src/views/admin/users/InvitationsList.vue
+++ b/front/src/views/admin/users/InvitationsList.vue
@@ -27,7 +27,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/admin/users/UsersList.vue b/front/src/views/admin/users/UsersList.vue
index 781a2e2d1..e54194ca1 100644
--- a/front/src/views/admin/users/UsersList.vue
+++ b/front/src/views/admin/users/UsersList.vue
@@ -24,7 +24,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/auth/EmailConfirm.vue b/front/src/views/auth/EmailConfirm.vue
index 466a13c9f..c08957055 100644
--- a/front/src/views/auth/EmailConfirm.vue
+++ b/front/src/views/auth/EmailConfirm.vue
@@ -17,7 +17,7 @@
           <router-link :to="{path: '/login'}">
             <translate translate-context="Content/Signup/Link/Verb">Return to login</translate>
           </router-link>
-          <button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
+          <button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'success', 'button']" type="submit">
             {{ labels.confirm }}</button>
         </form>
         <div v-else class="ui positive message">
@@ -74,7 +74,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/auth/Login.vue b/front/src/views/auth/Login.vue
index 22285bc9c..3a5b076da 100644
--- a/front/src/views/auth/Login.vue
+++ b/front/src/views/auth/Login.vue
@@ -34,7 +34,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/auth/PasswordReset.vue b/front/src/views/auth/PasswordReset.vue
index b60a41146..9f24346c3 100644
--- a/front/src/views/auth/PasswordReset.vue
+++ b/front/src/views/auth/PasswordReset.vue
@@ -25,7 +25,7 @@
           <router-link :to="{path: '/login'}">
             <translate translate-context="Content/Signup/Link">Back to login</translate>
           </router-link>
-          <button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
+          <button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'success', 'button']" type="submit">
             <translate translate-context="Content/Signup/Button.Label/Verb">Ask for a password reset</translate></button>
         </form>
       </div>
@@ -83,7 +83,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/auth/PasswordResetConfirm.vue b/front/src/views/auth/PasswordResetConfirm.vue
index 025458ba1..3fa335020 100644
--- a/front/src/views/auth/PasswordResetConfirm.vue
+++ b/front/src/views/auth/PasswordResetConfirm.vue
@@ -18,7 +18,7 @@
             <router-link :to="{path: '/login'}">
               <translate translate-context="Content/Signup/Link">Back to login</translate>
             </router-link>
-            <button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
+            <button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'success', 'button']" type="submit">
               <translate translate-context="Content/Signup/Button.Label">Update your password</translate></button>
           </template>
           <template v-else>
@@ -88,7 +88,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/auth/ProfileBase.vue b/front/src/views/auth/ProfileBase.vue
index 80ec6ad7d..3d43e6cb2 100644
--- a/front/src/views/auth/ProfileBase.vue
+++ b/front/src/views/auth/ProfileBase.vue
@@ -1,5 +1,5 @@
 <template>
-  <main class="main pusher" v-title="labels.usernameProfile">
+  <main class="main pusher page-profile" v-title="labels.usernameProfile">
     <div v-if="isLoading" class="ui vertical segment">
       <div class="ui centered active inline loader"></div>
     </div>
@@ -26,7 +26,7 @@
             </div>
           </div>
           <h1 class="ui center aligned icon header">
-            <i v-if="!object.icon" class="circular inverted user green icon"></i>
+            <i v-if="!object.icon" class="circular inverted user success icon"></i>
             <img class="ui big circular image" v-else v-lazy="$store.getters['instance/absoluteUrl'](object.icon.square_crop)" />
             <div class="ellispsis content">
               <div class="ui very small hidden divider"></div>
@@ -38,7 +38,7 @@
             </div>
             <template  v-if="object.full_username === $store.state.auth.fullUsername">
               <div class="ui very small hidden divider"></div>
-              <div class="ui basic green label">
+              <div class="ui basic success label">
                 <translate translate-context="Content/Profile/Button.Paragraph">This is you!</translate>
               </div>
             </template>
@@ -148,10 +148,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-.ui.header > img.image {
-  width: 8em;
-}
-</style>
diff --git a/front/src/views/auth/Signup.vue b/front/src/views/auth/Signup.vue
index 37b918c72..9b56d87a7 100644
--- a/front/src/views/auth/Signup.vue
+++ b/front/src/views/auth/Signup.vue
@@ -42,7 +42,3 @@ export default {
   },
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/channels/DetailBase.vue b/front/src/views/channels/DetailBase.vue
index 19ce17cab..0aaa735be 100644
--- a/front/src/views/channels/DetailBase.vue
+++ b/front/src/views/channels/DetailBase.vue
@@ -144,7 +144,7 @@
                 </button>
               </div>
               <div class="ui buttons">
-                <play-button :is-playable="isPlayable" class="orange" :artist="object.artist">
+                <play-button :is-playable="isPlayable" class="vibrant" :artist="object.artist">
                   <translate translate-context="Content/Channels/Button.Label/Verb">Play</translate>
                 </play-button>
               </div>
diff --git a/front/src/views/content/libraries/Form.vue b/front/src/views/content/libraries/Form.vue
index 9918eb42d..ac445303f 100644
--- a/front/src/views/content/libraries/Form.vue
+++ b/front/src/views/content/libraries/Form.vue
@@ -26,7 +26,7 @@
       <translate translate-context="Content/Library/Button.Label/Verb" v-if="library">Update library</translate>
       <translate translate-context="Content/Library/Button.Label/Verb" v-else>Create library</translate>
     </button>
-    <dangerous-button v-if="library" class="ui right floated basic red button" @confirm="remove()">
+    <dangerous-button v-if="library" class="ui right floated basic danger button" @confirm="remove()">
       <translate translate-context="*/*/*/Verb">Delete</translate>
       <p slot="modal-header">
         <translate translate-context="Popup/Library/Title">Delete this library?</translate>
@@ -134,6 +134,3 @@ export default {
   }
 }
 </script>
-
-<style scoped>
-</style>
diff --git a/front/src/views/content/libraries/Home.vue b/front/src/views/content/libraries/Home.vue
index 2e5e39498..aeb61e9b2 100644
--- a/front/src/views/content/libraries/Home.vue
+++ b/front/src/views/content/libraries/Home.vue
@@ -67,7 +67,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/content/libraries/Quota.vue b/front/src/views/content/libraries/Quota.vue
index 8b1fcba7e..2a6f2f200 100644
--- a/front/src/views/content/libraries/Quota.vue
+++ b/front/src/views/content/libraries/Quota.vue
@@ -4,7 +4,7 @@
     <div v-if="isLoading" :class="['ui', {'active': isLoading}, 'inverted', 'dimmer']">
       <div class="ui text loader"><translate translate-context="Content/Library/Paragraph">Loading usage data…</translate></div>
     </div>
-    <div :class="['ui', {'success': progress < 60}, {'yellow': progress >= 60 && progress < 96}, {'error': progress >= 95}, 'progress']" data-percent=progress>
+    <div :class="['ui', {'success': progress < 60}, {'warning': progress >= 60 && progress < 96}, {'error': progress >= 95}, 'progress']" data-percent=progress>
       <div class="bar" :style="{width: `${progress}%`}">
         <div class="progress">{{ progress }}%</div>
       </div>
@@ -15,7 +15,7 @@
     <div class="ui hidden divider"></div>
     <div v-if="quotaStatus" class="ui stackable three column grid">
       <div v-if="quotaStatus.pending > 0" class="column">
-        <div class="ui tiny yellow statistic">
+        <div class="ui tiny warning statistic">
           <div class="value">
             {{ humanSize(quotaStatus.pending * 1000 * 1000) }}
           </div>
@@ -25,13 +25,13 @@
         </div>
         <div>
           <router-link
-            class="ui basic blue tiny button"
+            class="ui basic primary tiny button"
             :to="{name: 'content.libraries.files', query: {q: compileTokens([{field: 'status', value: 'pending'}])}}">
             <translate translate-context="Content/Library/Link/Verb">View files</translate>
           </router-link>
 
           <dangerous-button
-            class="ui basic tiny grey button"
+            class="ui basic tiny button"
             :action="purgePendingFiles">
             <translate translate-context="*/*/*/Verb">Purge</translate>
             <p slot="modal-header"><translate translate-context="Popup/Library/Title">Purge pending files?</translate></p>
@@ -41,7 +41,7 @@
         </div>
       </div>
       <div v-if="quotaStatus.skipped > 0" class="column">
-        <div class="ui tiny grey statistic">
+        <div class="ui tiny statistic">
           <div class="value">
             {{ humanSize(quotaStatus.skipped * 1000 * 1000) }}
           </div>
@@ -51,12 +51,12 @@
         </div>
         <div>
           <router-link
-            class="ui basic blue tiny button"
+            class="ui basic primary tiny button"
             :to="{name: 'content.libraries.files', query: {q: compileTokens([{field: 'status', value: 'skipped'}])}}">
             <translate translate-context="Content/Library/Link/Verb">View files</translate>
           </router-link>
           <dangerous-button
-            class="ui basic tiny grey button"
+            class="ui basic tiny button"
             :action="purgeSkippedFiles">
             <translate translate-context="*/*/*/Verb">Purge</translate>
             <p slot="modal-header"><translate translate-context="Popup/Library/Title">Purge skipped files?</translate></p>
@@ -66,7 +66,7 @@
         </div>
       </div>
       <div v-if="quotaStatus.errored > 0" class="column">
-        <div class="ui tiny red statistic">
+        <div class="ui tiny danger statistic">
           <div class="value">
             {{ humanSize(quotaStatus.errored * 1000 * 1000) }}
           </div>
@@ -76,12 +76,12 @@
         </div>
         <div>
           <router-link
-            class="ui basic blue tiny button"
+            class="ui basic primary tiny button"
             :to="{name: 'content.libraries.files', query: {q: compileTokens([{field: 'status', value: 'errored'}])}}">
             <translate translate-context="Content/Library/Link/Verb">View files</translate>
           </router-link>
           <dangerous-button
-            class="ui basic tiny grey button"
+            class="ui basic tiny button"
             :action="purgeErroredFiles">
             <translate translate-context="*/*/*/Verb">Purge</translate>
             <p slot="modal-header"><translate translate-context="Popup/Library/Title">Purge errored files?</translate></p>
diff --git a/front/src/views/content/remote/Card.vue b/front/src/views/content/remote/Card.vue
index decddc8fc..78d6d4361 100644
--- a/front/src/views/content/remote/Card.vue
+++ b/front/src/views/content/remote/Card.vue
@@ -6,7 +6,7 @@
           {{ library.name }}
         </router-link>
         <div class="ui right floated dropdown">
-          <i class="ellipsis vertical grey large icon nomargin"></i>
+          <i class="ellipsis vertical large icon nomargin"></i>
           <div class="menu">
             <div
               role="button"
@@ -22,13 +22,13 @@
           v-if="library.privacy_level === 'me'"
           class="right floated"
           :data-tooltip="labels.tooltips.me">
-          <i class="small lock grey icon"></i>
+          <i class="small lock icon"></i>
         </span>
         <span
           v-else-if="library.privacy_level === 'everyone'"
           class="right floated"
           :data-tooltip="labels.tooltips.everyone">
-          <i class="small globe grey icon"></i>
+          <i class="small globe icon"></i>
         </span>
       </div>
       <div class="meta">
@@ -55,15 +55,15 @@
           <translate translate-context="Content/Library/Card.List item" :translate-params="{progress: scanProgress}">Scanning… (%{ progress }%)</translate>
         </template>
         <template v-else-if="latestScan.status === 'errored'">
-          <i class="red download icon"></i>
+          <i class="dangerdownload icon"></i>
           <translate translate-context="Content/Library/Card.List item">Problem during scanning</translate>
         </template>
         <template v-else-if="latestScan.status === 'finished' && latestScan.errored_files === 0">
-          <i class="green download icon"></i>
+          <i class="success download icon"></i>
           <translate translate-context="Content/Library/Card.List item">Scanned</translate>
         </template>
         <template v-else-if="latestScan.status === 'finished' && latestScan.errored_files > 0">
-          <i class="yellow download icon"></i>
+          <i class="warning download icon"></i>
           <translate translate-context="Content/Library/Card.List item">Scanned with errors</translate>
         </template>
         <span class="link right floated" @click="showScan = !showScan">
@@ -101,7 +101,7 @@
         <button
           v-if="!library.follow"
           @click="follow()"
-          :class="['ui', 'green', {'loading': isLoadingFollow}, 'button']">
+          :class="['ui', 'success', {'loading': isLoadingFollow}, 'button']">
           <translate translate-context="Content/Library/Card.Button.Label/Verb">Follow</translate>
         </button>
         <template v-else-if="!library.follow.approved">
diff --git a/front/src/views/content/remote/Home.vue b/front/src/views/content/remote/Home.vue
index 35d56cd95..d9fc81ebc 100644
--- a/front/src/views/content/remote/Home.vue
+++ b/front/src/views/content/remote/Home.vue
@@ -70,7 +70,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/library/Edit.vue b/front/src/views/library/Edit.vue
index e14ffcca0..fb44dab21 100644
--- a/front/src/views/library/Edit.vue
+++ b/front/src/views/library/Edit.vue
@@ -27,21 +27,21 @@
         <td><actor-link :actor="follow.actor" /></td>
         <td><human-date :date="follow.creation_date" /></td>
         <td>
-          <span :class="['ui', 'yellow', 'basic', 'label']" v-if="follow.approved === null">
+          <span :class="['ui', 'warning', 'basic', 'label']" v-if="follow.approved === null">
             <translate translate-context="Content/Library/Table/Short">Pending approval</translate>
           </span>
-          <span :class="['ui', 'green', 'basic', 'label']" v-else-if="follow.approved === true">
+          <span :class="['ui', 'success', 'basic', 'label']" v-else-if="follow.approved === true">
             <translate translate-context="Content/Library/Table/Short">Accepted</translate>
           </span>
-          <span :class="['ui', 'red', 'basic', 'label']" v-else-if="follow.approved === false">
+          <span :class="['ui', 'danger', 'basic', 'label']" v-else-if="follow.approved === false">
             <translate translate-context="Content/Library/*/Short">Rejected</translate>
           </span>
         </td>
         <td>
-          <div @click="updateApproved(follow, true)" :class="['ui', 'mini', 'icon', 'labeled', 'green', 'button']" v-if="follow.approved === null || follow.approved === false">
+          <div @click="updateApproved(follow, true)" :class="['ui', 'mini', 'icon', 'labeled', 'success', 'button']" v-if="follow.approved === null || follow.approved === false">
             <i class="ui check icon"></i> <translate translate-context="Content/Library/Button.Label">Accept</translate>
           </div>
-          <div @click="updateApproved(follow, false)" :class="['ui', 'mini', 'icon', 'labeled', 'red', 'button']" v-if="follow.approved === null || follow.approved === true">
+          <div @click="updateApproved(follow, false)" :class="['ui', 'mini', 'icon', 'labeled', 'danger', 'button']" v-if="follow.approved === null || follow.approved === true">
             <i class="ui x icon"></i> <translate translate-context="Content/Library/Button.Label">Reject</translate>
           </div>
         </td>
diff --git a/front/src/views/playlists/Detail.vue b/front/src/views/playlists/Detail.vue
index 99bf84761..bfd509555 100644
--- a/front/src/views/playlists/Detail.vue
+++ b/front/src/views/playlists/Detail.vue
@@ -6,7 +6,7 @@
     <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>
+          <i class="circular inverted list warning icon"></i>
           <div class="content">
             {{ playlist.name }}
             <div class="sub header">
@@ -22,7 +22,7 @@
           </div>
         </h2>
         <div class="ui hidden divider"></div>
-        <play-button class="orange" :is-playable="playlist.is_playable" :tracks="tracks"><translate translate-context="Content/Queue/Button.Label/Short, Verb">Play all</translate></play-button>
+        <play-button class="vibrant" :is-playable="playlist.is_playable" :tracks="tracks"><translate translate-context="Content/Queue/Button.Label/Short, Verb">Play all</translate></play-button>
         <button
           class="ui icon labeled button"
           v-if="$store.state.auth.profile && playlist.user.id === $store.state.auth.profile.id"
@@ -39,7 +39,7 @@
           <translate translate-context="Content/*/Button.Label/Verb">Embed</translate>
         </button>
 
-        <dangerous-button v-if="$store.state.auth.profile && playlist.user.id === $store.state.auth.profile.id" class="ui labeled red icon button" :action="deletePlaylist">
+        <dangerous-button v-if="$store.state.auth.profile && playlist.user.id === $store.state.auth.profile.id" class="ui labeled danger icon button" :action="deletePlaylist">
           <i class="trash icon"></i> <translate translate-context="*/*/*/Verb">Delete</translate>
           <p slot="modal-header" v-translate="{playlist: playlist.name}" translate-context="Popup/Playlist/Title/Call to action" :translate-params="{playlist: playlist.name}">
             Do you want to delete the playlist "%{ playlist }"?
@@ -80,7 +80,7 @@
           <i class="list icon"></i>
           <translate translate-context="Content/Home/Placeholder">There are no tracks in this playlist yet</translate>
         </div>
-        <button @click="edit = !edit" class="ui green icon labeled button">
+        <button @click="edit = !edit" class="ui success icon labeled button">
           <i class="pencil icon"></i>
           <translate translate-context="Content/Home/CreatePlaylist">Edit</translate>
         </button>
diff --git a/front/src/views/playlists/List.vue b/front/src/views/playlists/List.vue
index b74175030..237c2becc 100644
--- a/front/src/views/playlists/List.vue
+++ b/front/src/views/playlists/List.vue
@@ -6,7 +6,7 @@
         <template v-if="$store.state.auth.authenticated">
           <button
             @click="$store.commit('playlists/chooseTrack', null)"
-            class="ui basic green button"><translate translate-context="Content/Playlist/Button.Label/Verb">Manage your playlists</translate></button>
+            class="ui basic success button"><translate translate-context="Content/Playlist/Button.Label/Verb">Manage your playlists</translate></button>
           <div class="ui hidden divider"></div>
         </template>
         <div class="fields">
@@ -51,7 +51,7 @@
         <button
         v-if="$store.state.auth.authenticated"
         @click="$store.commit('playlists/chooseTrack', null)"
-        class="ui green button labeled icon">
+        class="ui success button labeled icon">
         <i class="list icon"></i>
         <translate translate-context="Content/*/Verb">
           Create a playlist
@@ -182,7 +182,3 @@ export default {
   }
 }
 </script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-</style>
diff --git a/front/src/views/radios/Detail.vue b/front/src/views/radios/Detail.vue
index 4610d6873..37c2b9acc 100644
--- a/front/src/views/radios/Detail.vue
+++ b/front/src/views/radios/Detail.vue
@@ -6,7 +6,7 @@
     <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>
+          <i class="circular inverted feed primary icon"></i>
           <div class="content">
             {{ radio.name }}
             <div class="sub header">
@@ -22,7 +22,7 @@
             <i class="pencil icon"></i>
             Edit…
           </router-link>
-          <dangerous-button class="ui labeled red icon button" :action="deleteRadio">
+          <dangerous-button class="ui labeled danger icon button" :action="deleteRadio">
             <i class="trash icon"></i> Delete
             <p slot="modal-header" v-translate="{radio: radio.name}"  translate-context="Popup/Radio/Title" :translate-params="{radio: radio.name}">Do you want to delete the radio "%{ radio }"?</p>
             <p slot="modal-content"><translate translate-context="Popup/Radio/Paragraph">This will completely delete this radio and cannot be undone.</translate></p>
@@ -53,7 +53,7 @@
       </div>
       <router-link
       v-if="$store.state.auth.username === radio.user.username"
-      class="ui green icon labeled button"
+      class="ui success icon labeled button"
       :to="{name: 'library.radios.edit', params: {id: radio.id}}" exact>
       <i class="pencil icon"></i>
         Edit…
diff --git a/front/vue.config.js b/front/vue.config.js
index 5d2d1ea44..d01bb0087 100644
--- a/front/vue.config.js
+++ b/front/vue.config.js
@@ -152,5 +152,12 @@ module.exports = {
     disableHostCheck: true,
     // use https://node1.funkwhale.test/front-server/ if you use docker with federation
     public: process.env.FRONT_DEVSERVER_URL || ('http://localhost:' + (process.env.VUE_PORT || '8080'))
+  },
+  css: {
+    loaderOptions: {
+      sass: {
+        implementation: require('sass')
+      }
+    }
   }
 }
diff --git a/front/yarn.lock b/front/yarn.lock
index fa9eb362b..8d39ab4d2 100644
--- a/front/yarn.lock
+++ b/front/yarn.lock
@@ -1657,11 +1657,6 @@ alphanum-sort@^1.0.0:
   resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
   integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
 
-amdefine@>=0.0.4:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
-  integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=
-
 ansi-colors@3.2.3:
   version "3.2.3"
   resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813"
@@ -1735,6 +1730,14 @@ anymatch@^2.0.0:
     micromatch "^3.1.4"
     normalize-path "^2.1.1"
 
+anymatch@~3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142"
+  integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==
+  dependencies:
+    normalize-path "^3.0.0"
+    picomatch "^2.0.4"
+
 aproba@^1.0.3, aproba@^1.1.1:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
@@ -1785,11 +1788,6 @@ array-filter@~0.0.0:
   resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec"
   integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw=
 
-array-find-index@^1.0.1:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
-  integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=
-
 array-flatten@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
@@ -1891,11 +1889,6 @@ async-each@^1.0.1:
   resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
   integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
 
-async-foreach@^0.1.3:
-  version "0.1.3"
-  resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542"
-  integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=
-
 async-limiter@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
@@ -2081,12 +2074,10 @@ binary-extensions@^1.0.0:
   resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
   integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
 
-block-stream@*:
-  version "0.0.9"
-  resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
-  integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=
-  dependencies:
-    inherits "~2.0.0"
+binary-extensions@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
+  integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
 
 bluebird@^3.1.1, bluebird@^3.5.1:
   version "3.5.5"
@@ -2160,7 +2151,7 @@ braces@^2.3.1, braces@^2.3.2:
     split-string "^3.0.2"
     to-regex "^3.0.1"
 
-braces@^3.0.1:
+braces@^3.0.1, braces@~3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
   integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
@@ -2412,29 +2403,11 @@ camel-case@3.0.x:
     no-case "^2.2.0"
     upper-case "^1.1.1"
 
-camelcase-keys@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
-  integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc=
-  dependencies:
-    camelcase "^2.0.0"
-    map-obj "^1.0.0"
-
 camelcase@^1.0.2:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
   integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=
 
-camelcase@^2.0.0:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
-  integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=
-
-camelcase@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
-  integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo=
-
 camelcase@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
@@ -2495,7 +2468,7 @@ chai@^4.1.2:
     pathval "^1.1.0"
     type-detect "^4.0.5"
 
-chalk@^1.1.1, chalk@^1.1.3:
+chalk@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
   integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
@@ -2557,6 +2530,21 @@ cheerio@^1.0.0-rc.2:
     lodash "^4.15.0"
     parse5 "^3.0.1"
 
+"chokidar@>=2.0.0 <4.0.0":
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.0.tgz#b30611423ce376357c765b9b8f904b9fba3c0be8"
+  integrity sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==
+  dependencies:
+    anymatch "~3.1.1"
+    braces "~3.0.2"
+    glob-parent "~5.1.0"
+    is-binary-path "~2.1.0"
+    is-glob "~4.0.1"
+    normalize-path "~3.0.0"
+    readdirp "~3.4.0"
+  optionalDependencies:
+    fsevents "~2.1.2"
+
 chokidar@^2.0.0, chokidar@^2.1.8:
   version "2.1.8"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
@@ -2694,15 +2682,6 @@ cliui@^2.1.0:
     right-align "^0.1.1"
     wordwrap "0.0.2"
 
-cliui@^3.2.0:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
-  integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=
-  dependencies:
-    string-width "^1.0.1"
-    strip-ansi "^3.0.1"
-    wrap-ansi "^2.0.0"
-
 cliui@^4.0.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
@@ -3060,14 +3039,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
     safe-buffer "^5.0.1"
     sha.js "^2.4.8"
 
-cross-spawn@^3.0.0:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
-  integrity sha1-ElYDfsufDF9549bvE14wdwGEuYI=
-  dependencies:
-    lru-cache "^4.0.1"
-    which "^1.2.9"
-
 cross-spawn@^5.0.1:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
@@ -3308,13 +3279,6 @@ current-script-polyfill@^1.0.0:
   resolved "https://registry.yarnpkg.com/current-script-polyfill/-/current-script-polyfill-1.0.0.tgz#f31cf7e4f3e218b0726e738ca92a02d3488ef615"
   integrity sha1-8xz35PPiGLBybnOMqSoC00iO9hU=
 
-currently-unhandled@^0.4.1:
-  version "0.4.1"
-  resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
-  integrity sha1-mI3zP+qxke95mmE2nddsF635V+o=
-  dependencies:
-    array-find-index "^1.0.1"
-
 custom-event-polyfill@^1.0.7:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz#9bc993ddda937c1a30ccd335614c6c58c4f87aee"
@@ -3379,7 +3343,7 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
   dependencies:
     ms "^2.1.1"
 
-decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0:
+decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
   integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
@@ -3795,7 +3759,7 @@ errno@^0.1.3, errno@~0.1.7:
   dependencies:
     prr "~1.0.1"
 
-error-ex@^1.2.0, error-ex@^1.3.1:
+error-ex@^1.3.1:
   version "1.3.2"
   resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
   integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
@@ -4526,15 +4490,10 @@ fsevents@^1.2.7:
     nan "^2.12.1"
     node-pre-gyp "^0.12.0"
 
-fstream@^1.0.0, fstream@^1.0.12:
-  version "1.0.12"
-  resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045"
-  integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==
-  dependencies:
-    graceful-fs "^4.1.2"
-    inherits "~2.0.0"
-    mkdirp ">=0.5 0"
-    rimraf "2"
+fsevents@~2.1.2:
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e"
+  integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
 
 function-bind@^1.1.1:
   version "1.1.1"
@@ -4560,13 +4519,6 @@ gauge@~2.7.3:
     strip-ansi "^3.0.1"
     wide-align "^1.1.0"
 
-gaze@^1.0.0:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a"
-  integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==
-  dependencies:
-    globule "^1.0.0"
-
 gensync@^1.0.0-beta.1:
   version "1.0.0-beta.1"
   resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
@@ -4592,11 +4544,6 @@ get-own-enumerable-property-symbols@^3.0.0:
   resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664"
   integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==
 
-get-stdin@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
-  integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=
-
 get-stream@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
@@ -4651,6 +4598,13 @@ glob-parent@^3.1.0:
     is-glob "^3.1.0"
     path-dirname "^1.0.0"
 
+glob-parent@~5.1.0:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
+  integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
+  dependencies:
+    is-glob "^4.0.1"
+
 glob-to-regexp@^0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
@@ -4680,7 +4634,7 @@ glob@7.1.3:
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
-glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1:
+glob@^7.0.3, glob@^7.0.5, glob@^7.1.2, glob@^7.1.3:
   version "7.1.4"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
   integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
@@ -4760,15 +4714,6 @@ globby@^9.2.0:
     pify "^4.0.1"
     slash "^2.0.0"
 
-globule@^1.0.0:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d"
-  integrity sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==
-  dependencies:
-    glob "~7.1.1"
-    lodash "~4.17.10"
-    minimatch "~3.0.2"
-
 graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
   version "4.1.15"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
@@ -5188,18 +5133,6 @@ imurmurhash@^0.1.4:
   resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
   integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
 
-in-publish@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51"
-  integrity sha1-4g/146KvwmkDILbcVSaCqcf631E=
-
-indent-string@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
-  integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=
-  dependencies:
-    repeating "^2.0.0"
-
 indent-string@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
@@ -5223,7 +5156,7 @@ inflight@^1.0.4:
     once "^1.3.0"
     wrappy "1"
 
-inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
+inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
   integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
@@ -5343,6 +5276,13 @@ is-binary-path@^1.0.0:
   dependencies:
     binary-extensions "^1.0.0"
 
+is-binary-path@~2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
+  integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
+  dependencies:
+    binary-extensions "^2.0.0"
+
 is-buffer@^1.1.5:
   version "1.1.6"
   resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
@@ -5449,13 +5389,6 @@ is-extglob@^2.1.0, is-extglob@^2.1.1:
   resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
   integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
 
-is-finite@^1.0.0:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa"
-  integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=
-  dependencies:
-    number-is-nan "^1.0.0"
-
 is-fullwidth-code-point@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
@@ -5480,7 +5413,7 @@ is-glob@^3.1.0:
   dependencies:
     is-extglob "^2.1.0"
 
-is-glob@^4.0.0, is-glob@^4.0.1:
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
   integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
@@ -5586,11 +5519,6 @@ is-typedarray@~1.0.0:
   resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
   integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
 
-is-utf8@^0.2.0:
-  version "0.2.1"
-  resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
-  integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
-
 is-windows@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
@@ -5656,11 +5584,6 @@ jquery@^3.4.0:
   resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.4.1.tgz#714f1f8d9dde4bdfa55764ba37ef214630d80ef2"
   integrity sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==
 
-js-base64@^2.1.8:
-  version "2.5.1"
-  resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.1.tgz#1efa39ef2c5f7980bb1784ade4a8af2de3291121"
-  integrity sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==
-
 js-logger@^1.4.1:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/js-logger/-/js-logger-1.6.0.tgz#7abae5cfaf208c965f3ef20754533bb9e79c7aef"
@@ -5927,17 +5850,6 @@ lines-and-columns@^1.1.6:
   resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
   integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
 
-load-json-file@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
-  integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=
-  dependencies:
-    graceful-fs "^4.1.2"
-    parse-json "^2.2.0"
-    pify "^2.0.0"
-    pinkie-promise "^2.0.0"
-    strip-bom "^2.0.0"
-
 loader-fs-cache@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz#54cedf6b727e1779fd8f01205f05f6e88706f086"
@@ -6083,7 +5995,7 @@ lodash.uniq@^4.5.0:
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
   integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
 
-lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.3, lodash@^4.17.4, lodash@~4.17.10:
+lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.3, lodash@^4.17.4:
   version "4.17.11"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
   integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
@@ -6127,14 +6039,6 @@ loose-envify@^1.0.0:
   dependencies:
     js-tokens "^3.0.0 || ^4.0.0"
 
-loud-rejection@^1.0.0:
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
-  integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=
-  dependencies:
-    currently-unhandled "^0.4.1"
-    signal-exit "^3.0.0"
-
 lower-case@^1.1.1:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
@@ -6187,11 +6091,6 @@ map-cache@^0.2.2:
   resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
   integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
 
-map-obj@^1.0.0, map-obj@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
-  integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=
-
 map-visit@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
@@ -6242,22 +6141,6 @@ memory-fs@^0.4.0, memory-fs@^0.4.1:
     errno "^0.1.3"
     readable-stream "^2.0.1"
 
-meow@^3.7.0:
-  version "3.7.0"
-  resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
-  integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=
-  dependencies:
-    camelcase-keys "^2.0.0"
-    decamelize "^1.1.2"
-    loud-rejection "^1.0.0"
-    map-obj "^1.0.1"
-    minimist "^1.1.3"
-    normalize-package-data "^2.3.4"
-    object-assign "^4.0.1"
-    read-pkg-up "^1.0.1"
-    redent "^1.0.0"
-    trim-newlines "^1.0.0"
-
 merge-descriptors@1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
@@ -6377,7 +6260,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
   resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
   integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
 
-minimatch@3.0.4, minimatch@^3.0.4, minimatch@~3.0.2:
+minimatch@3.0.4, minimatch@^3.0.4:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
   integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@@ -6394,7 +6277,7 @@ minimist@^0.1.0:
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.1.0.tgz#99df657a52574c21c9057497df742790b2b4c0de"
   integrity sha1-md9lelJXTCHJBXSX33QnkLK0wN4=
 
-minimist@^1.1.3, minimist@^1.2.0:
+minimist@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
   integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
@@ -6466,7 +6349,7 @@ mixin-deep@^1.2.0:
     for-in "^1.0.2"
     is-extendable "^1.0.1"
 
-mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1:
+mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1:
   version "0.5.1"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
   integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
@@ -6606,7 +6489,7 @@ mz@^2.4.0:
     object-assign "^4.0.1"
     thenify-all "^1.0.0"
 
-nan@^2.12.1, nan@^2.13.2:
+nan@^2.12.1:
   version "2.14.0"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
   integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
@@ -6688,24 +6571,6 @@ node-forge@0.9.0:
   resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"
   integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==
 
-node-gyp@^3.8.0:
-  version "3.8.0"
-  resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c"
-  integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==
-  dependencies:
-    fstream "^1.0.0"
-    glob "^7.0.3"
-    graceful-fs "^4.1.2"
-    mkdirp "^0.5.0"
-    nopt "2 || 3"
-    npmlog "0 || 1 || 2 || 3 || 4"
-    osenv "0"
-    request "^2.87.0"
-    rimraf "2"
-    semver "~5.3.0"
-    tar "^2.0.0"
-    which "1"
-
 node-ipc@^9.1.1:
   version "9.1.1"
   resolved "https://registry.yarnpkg.com/node-ipc/-/node-ipc-9.1.1.tgz#4e245ed6938e65100e595ebc5dc34b16e8dd5d69"
@@ -6774,41 +6639,11 @@ node-releases@^1.1.49:
   dependencies:
     semver "^6.3.0"
 
-node-sass@^4.9.3:
-  version "4.12.0"
-  resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.12.0.tgz#0914f531932380114a30cc5fa4fa63233a25f017"
-  integrity sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==
-  dependencies:
-    async-foreach "^0.1.3"
-    chalk "^1.1.1"
-    cross-spawn "^3.0.0"
-    gaze "^1.0.0"
-    get-stdin "^4.0.1"
-    glob "^7.0.3"
-    in-publish "^2.0.0"
-    lodash "^4.17.11"
-    meow "^3.7.0"
-    mkdirp "^0.5.1"
-    nan "^2.13.2"
-    node-gyp "^3.8.0"
-    npmlog "^4.0.0"
-    request "^2.88.0"
-    sass-graph "^2.2.4"
-    stdout-stream "^1.4.0"
-    "true-case-path" "^1.0.2"
-
 nodent-runtime@^3.2.1:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/nodent-runtime/-/nodent-runtime-3.2.1.tgz#9e2755d85e39f764288f0d4752ebcfe3e541e00e"
   integrity sha512-7Ws63oC+215smeKJQCxzrK21VFVlCFBkwl0MOObt0HOpVQXs3u483sAmtkF33nNqZ5rSOQjB76fgyPBmAUrtCA==
 
-"nopt@2 || 3":
-  version "3.0.6"
-  resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
-  integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k=
-  dependencies:
-    abbrev "1"
-
 nopt@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
@@ -6817,7 +6652,7 @@ nopt@^4.0.1:
     abbrev "1"
     osenv "^0.1.4"
 
-normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0:
+normalize-package-data@^2.5.0:
   version "2.5.0"
   resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
   integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
@@ -6839,7 +6674,7 @@ normalize-path@^2.1.1:
   dependencies:
     remove-trailing-separator "^1.0.1"
 
-normalize-path@^3.0.0:
+normalize-path@^3.0.0, normalize-path@~3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
   integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
@@ -6891,7 +6726,7 @@ npm-run-path@^4.0.0:
   dependencies:
     path-key "^3.0.0"
 
-"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2:
+npmlog@^4.0.2:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
   integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
@@ -7104,13 +6939,6 @@ os-homedir@^1.0.0:
   resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
   integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
 
-os-locale@^1.4.0:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
-  integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=
-  dependencies:
-    lcid "^1.0.0"
-
 os-locale@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
@@ -7134,7 +6962,7 @@ os-tmpdir@^1.0.0, os-tmpdir@~1.0.2:
   resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
   integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
 
-osenv@0, osenv@^0.1.4:
+osenv@^0.1.4:
   version "0.1.5"
   resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
   integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
@@ -7273,13 +7101,6 @@ parse-asn1@^5.0.0:
     pbkdf2 "^3.0.3"
     safe-buffer "^5.1.1"
 
-parse-json@^2.2.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
-  integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=
-  dependencies:
-    error-ex "^1.2.0"
-
 parse-json@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
@@ -7396,15 +7217,6 @@ path-to-regexp@^1.7.0:
   dependencies:
     isarray "0.0.1"
 
-path-type@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
-  integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=
-  dependencies:
-    graceful-fs "^4.1.2"
-    pify "^2.0.0"
-    pinkie-promise "^2.0.0"
-
 path-type@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
@@ -7438,6 +7250,11 @@ performance-now@^2.1.0:
   resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
   integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
 
+picomatch@^2.0.4:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
+  integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
+
 picomatch@^2.0.5, picomatch@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a"
@@ -8253,23 +8070,6 @@ rc@^1.2.7:
     minimist "^1.2.0"
     strip-json-comments "~2.0.1"
 
-read-pkg-up@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
-  integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=
-  dependencies:
-    find-up "^1.0.0"
-    read-pkg "^1.0.0"
-
-read-pkg@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
-  integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=
-  dependencies:
-    load-json-file "^1.0.0"
-    normalize-package-data "^2.3.2"
-    path-type "^1.0.0"
-
 read-pkg@^5.1.1:
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc"
@@ -8311,19 +8111,18 @@ readdirp@^2.2.1:
     micromatch "^3.1.10"
     readable-stream "^2.0.2"
 
+readdirp@~3.4.0:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada"
+  integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==
+  dependencies:
+    picomatch "^2.2.1"
+
 reconnecting-websocket@^3.0.3:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-3.2.2.tgz#8097514e926e9855e03c39e76efa2e3d1f371bee"
   integrity sha512-SWSfoXiaHVOqXuPWFgGWeUxKnb5HIY7I/Fh5C/hy4wUOgeOh7YIMXEiv5/eHBlNs4tNzCrO5YDR9AH62NWle0Q==
 
-redent@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
-  integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=
-  dependencies:
-    indent-string "^2.1.0"
-    strip-indent "^1.0.1"
-
 regenerate-unicode-properties@^8.1.0:
   version "8.1.0"
   resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
@@ -8426,13 +8225,6 @@ repeat-string@^1.5.2, repeat-string@^1.6.1:
   resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
   integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
 
-repeating@^2.0.0:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
-  integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=
-  dependencies:
-    is-finite "^1.0.0"
-
 request-promise-core@1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346"
@@ -8587,7 +8379,7 @@ right-align@^0.1.1:
   dependencies:
     align-text "^0.1.1"
 
-rimraf@2, rimraf@2.6.3, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3:
+rimraf@2.6.3, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3:
   version "2.6.3"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
   integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
@@ -8668,16 +8460,6 @@ sanitize-html@^1.20.1:
     srcset "^1.0.0"
     xtend "^4.0.1"
 
-sass-graph@^2.2.4:
-  version "2.2.4"
-  resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49"
-  integrity sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=
-  dependencies:
-    glob "^7.0.0"
-    lodash "^4.0.0"
-    scss-tokenizer "^0.2.3"
-    yargs "^7.0.0"
-
 sass-loader@^8.0.2:
   version "8.0.2"
   resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-8.0.2.tgz#debecd8c3ce243c76454f2e8290482150380090d"
@@ -8689,6 +8471,13 @@ sass-loader@^8.0.2:
     schema-utils "^2.6.1"
     semver "^6.3.0"
 
+sass@^1.26.5:
+  version "1.26.5"
+  resolved "https://registry.yarnpkg.com/sass/-/sass-1.26.5.tgz#2d7aecfbbabfa298567c8f06615b6e24d2d68099"
+  integrity sha512-FG2swzaZUiX53YzZSjSakzvGtlds0lcbF+URuU9mxOv7WBh7NhXEVDa4kPKN4hN6fC2TkOTOKqiqp6d53N9X5Q==
+  dependencies:
+    chokidar ">=2.0.0 <4.0.0"
+
 sax@^1.2.4, sax@~1.2.4:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
@@ -8718,14 +8507,6 @@ schema-utils@^2.0.0, schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6
     ajv "^6.10.2"
     ajv-keywords "^3.4.1"
 
-scss-tokenizer@^0.2.3:
-  version "0.2.3"
-  resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
-  integrity sha1-jrBtualyMzOCTT9VMGQRSYR85dE=
-  dependencies:
-    js-base64 "^2.1.8"
-    source-map "^0.4.2"
-
 select-hose@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
@@ -8763,11 +8544,6 @@ semver@^6.1.0, semver@^6.3.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
   integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
 
-semver@~5.3.0:
-  version "5.3.0"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
-  integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8=
-
 send@0.17.1:
   version "0.17.1"
   resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
@@ -9058,13 +8834,6 @@ source-map-url@^0.4.0:
   resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
   integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
 
-source-map@^0.4.2:
-  version "0.4.4"
-  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
-  integrity sha1-66T12pwNyZneaAMti092FzZSA2s=
-  dependencies:
-    amdefine ">=0.0.4"
-
 source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1:
   version "0.5.7"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
@@ -9197,13 +8966,6 @@ static-extend@^0.1.1:
   resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
   integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
 
-stdout-stream@^1.4.0:
-  version "1.4.1"
-  resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de"
-  integrity sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==
-  dependencies:
-    readable-stream "^2.0.1"
-
 stealthy-require@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
@@ -9246,7 +9008,7 @@ strict-uri-encode@^1.0.0:
   resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
   integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
 
-string-width@^1.0.1, string-width@^1.0.2:
+string-width@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
   integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
@@ -9332,13 +9094,6 @@ strip-ansi@^6.0.0:
   dependencies:
     ansi-regex "^5.0.0"
 
-strip-bom@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
-  integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=
-  dependencies:
-    is-utf8 "^0.2.0"
-
 strip-comments@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-1.0.2.tgz#82b9c45e7f05873bee53f37168af930aa368679d"
@@ -9357,13 +9112,6 @@ strip-final-newline@^2.0.0:
   resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
   integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
 
-strip-indent@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2"
-  integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=
-  dependencies:
-    get-stdin "^4.0.1"
-
 strip-indent@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68"
@@ -9468,15 +9216,6 @@ tapable@^1.0.0, tapable@^1.1.3:
   resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
   integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
 
-tar@^2.0.0:
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40"
-  integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==
-  dependencies:
-    block-stream "*"
-    fstream "^1.0.12"
-    inherits "2"
-
 tar@^4:
   version "4.4.10"
   resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1"
@@ -9707,18 +9446,6 @@ tr46@^1.0.1:
   dependencies:
     punycode "^2.1.0"
 
-trim-newlines@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
-  integrity sha1-WIeWa7WCpFA6QetST301ARgVphM=
-
-"true-case-path@^1.0.2":
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.3.tgz#f813b5a8c86b40da59606722b144e3225799f47d"
-  integrity sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==
-  dependencies:
-    glob "^7.1.2"
-
 tryer@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
@@ -10351,17 +10078,12 @@ whatwg-url@^7.0.0:
     tr46 "^1.0.1"
     webidl-conversions "^4.0.2"
 
-which-module@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
-  integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=
-
 which-module@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
   integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
 
-which@1, which@1.3.1, which@^1.2.9:
+which@1.3.1, which@^1.2.9:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
   integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
@@ -10671,13 +10393,6 @@ yargs-parser@^16.1.0:
     camelcase "^5.0.0"
     decamelize "^1.2.0"
 
-yargs-parser@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"
-  integrity sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=
-  dependencies:
-    camelcase "^3.0.0"
-
 yargs-parser@^8.1.0:
   version "8.1.0"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950"
@@ -10780,25 +10495,6 @@ yargs@^15.0.0:
     y18n "^4.0.0"
     yargs-parser "^16.1.0"
 
-yargs@^7.0.0:
-  version "7.1.0"
-  resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"
-  integrity sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=
-  dependencies:
-    camelcase "^3.0.0"
-    cliui "^3.2.0"
-    decamelize "^1.1.1"
-    get-caller-file "^1.0.1"
-    os-locale "^1.4.0"
-    read-pkg-up "^1.0.1"
-    require-directory "^2.1.1"
-    require-main-filename "^1.0.1"
-    set-blocking "^2.0.0"
-    string-width "^1.0.2"
-    which-module "^1.0.0"
-    y18n "^3.2.1"
-    yargs-parser "^5.0.0"
-
 yargs@~1.2.6:
   version "1.2.6"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-1.2.6.tgz#9c7b4a82fd5d595b2bf17ab6dcc43135432fe34b"
-- 
GitLab