diff --git a/changes/changelog.d/756.feature b/changes/changelog.d/756.feature new file mode 100644 index 000000000..402b0e589 --- /dev/null +++ b/changes/changelog.d/756.feature @@ -0,0 +1 @@ +Dark theme (#756) diff --git a/front/public/index.html b/front/public/index.html index 7b09feaf0..142419ca6 100644 --- a/front/public/index.html +++ b/front/public/index.html @@ -9,7 +9,7 @@ Funkwhale - + diff --git a/front/src/App.vue b/front/src/App.vue index 5711466c5..e401d475e 100644 --- a/front/src/App.vue +++ b/front/src/App.vue @@ -251,6 +251,14 @@ export default { this.$store.dispatch('instance/fetchSettings') this.fetchNodeInfo() }, + '$store.state.ui.theme': { + immediate: true, + handler (newValue, oldValue) { + let oldTheme = oldValue || 'light' + document.body.classList.remove(`theme-${oldTheme}`) + document.body.classList.add(`theme-${newValue}`) + }, + }, '$store.state.auth.authenticated' (newValue) { if (!newValue) { this.disconnect() diff --git a/front/src/components/Footer.vue b/front/src/components/Footer.vue index 2d06b50c7..d758b456b 100644 --- a/front/src/components/Footer.vue +++ b/front/src/components/Footer.vue @@ -33,6 +33,14 @@ Mobile and desktop apps
Keyboard shortcuts
+
+
+ + +
+

Getting help

@@ -76,15 +84,18 @@ export default { parser.href = url return parser.hostname }, + themes () { + return [ + { + name: this.$pgettext('Footer/Settings/Dropdown.Label/Theme name', 'Light'), + key: 'light' + }, + { + name: this.$pgettext('Footer/Settings/Dropdown.Label/Theme name', 'Dark'), + key: 'dark' + } + ] + } } } - diff --git a/front/src/components/notifications/NotificationRow.vue b/front/src/components/notifications/NotificationRow.vue index b8e145647..95f8e3644 100644 --- a/front/src/components/notifications/NotificationRow.vue +++ b/front/src/components/notifications/NotificationRow.vue @@ -126,7 +126,4 @@ export default { .read > span { cursor: pointer; } -.disabled-row { - color: rgba(40, 40, 40, 0.3); -} diff --git a/front/src/components/playlists/Card.vue b/front/src/components/playlists/Card.vue index 60a322ab4..6efc2350e 100644 --- a/front/src/components/playlists/Card.vue +++ b/front/src/components/playlists/Card.vue @@ -59,12 +59,10 @@ export default { diff --git a/front/src/store/index.js b/front/src/store/index.js index 791dbb1e9..126368e23 100644 --- a/front/src/store/index.js +++ b/front/src/store/index.js @@ -40,7 +40,7 @@ export default new Vuex.Store({ }), createPersistedState({ key: 'ui', - paths: ['ui.currentLanguage', 'ui.momentLocale'] + paths: ['ui.currentLanguage', 'ui.momentLocale', 'ui.theme'] }), createPersistedState({ key: 'radios', diff --git a/front/src/store/ui.js b/front/src/store/ui.js index 8a8bc1da0..d13a92d88 100644 --- a/front/src/store/ui.js +++ b/front/src/store/ui.js @@ -10,6 +10,7 @@ export default { maxMessages: 100, messageDisplayDuration: 10000, messages: [], + theme: 'light', notifications: { inbox: 0, pendingReviewEdits: 0, @@ -39,6 +40,9 @@ export default { computeLastDate: (state) => { state.lastDate = new Date() }, + theme: (state, value) => { + state.theme = value + }, addMessage (state, message) { state.messages.push(message) if (state.messages.length > state.maxMessages) { diff --git a/front/src/style/_main.scss b/front/src/style/_main.scss index 8f8ee8b16..bc527415c 100644 --- a/front/src/style/_main.scss +++ b/front/src/style/_main.scss @@ -10,6 +10,10 @@ Import this file into your LESS project to use Semantic UI without build tools */ +// Those semantic-ui-css/*.scss don't exist in the package, but we create them +// via scripts/link-scss-files.sh on postinstall, so we can include theme +// under a class namespace + /* Global */ @import "~semantic-ui-css/components/reset.css"; // we use our custom site css here to avoid loading google font @@ -96,6 +100,7 @@ body { #app > main, #app > .main { flex: 1; } + .instance-chooser { margin-top: 2em; } @@ -126,14 +131,10 @@ body { margin-left: 0; margin-right: 0; border: none; - box-shadow: inset 0px -2px 0px 0px rgba(34, 36, 38, 0.15); .ui.item { border: none; border-bottom-style: none; margin-bottom: 0px; - &.active { - box-shadow: inset 0px -2px 0px 0px #000; - } } @include media(">tablet") { padding: 0 2.5rem; @@ -148,7 +149,6 @@ body { @include media(">widedesktop") { left: $widedesktop-sidebar-width; } - background-color: white; .item { padding-top: 1.5em; padding-bottom: 1.5em; @@ -208,9 +208,6 @@ body { } } -.discrete { - color: rgba(0, 0, 0, 0.87); -} .link { cursor: pointer; } @@ -355,3 +352,8 @@ input + .help { .table td .ui.dropdown { min-width: 150px; } + + + +@import "./themes/_light.scss"; +@import "./themes/_dark.scss"; diff --git a/front/src/style/themes/_dark.scss b/front/src/style/themes/_dark.scss new file mode 100644 index 000000000..f612914ca --- /dev/null +++ b/front/src/style/themes/_dark.scss @@ -0,0 +1,222 @@ +$background-color: rgb(43, 58, 66); +$button-hover-color: rgb(33, 48, 56); +$light-background-color: rgb(51, 71, 82); +$input-background-color: rgb(189, 211, 222); +$loading-background-color: rgba(43, 58, 66, 0.9); +$text-color: rgb(223, 235, 240); +$discrete-text-color: rgba(223, 235, 240, 0.904); +$border-color: rgb(63, 88, 102); +$light-shadow-color: rgba(223, 235, 240, 0.15); +$shadow-color: rgba(63, 102, 97, 0.95); +$box-shadow: 0px 1px 3px 0px rgba(63, 88, 102, 0.95), 0px 0px 0px 1px rgba(63, 88, 102, 0.98); +$link-color: rgb(255, 144, 0); + +.theme-dark { + background-color: $background-color; + .ui.labeled.input { + input, .label { + background-color: $input-background-color; + &::placeholder { + color: $light-background-color; + + } + + } + } + .ui.statistics .statistic { + > .label, > .value { + color: $text-color; + } + } + .ui.link.list.list .active.item, .ui.link.list.list .active.item a:not(.ui) { + color: inherit; + } + .ui.form textarea, .ui.form select, .ui.selection.dropdown, .ui.dropdown.selected, .ui.dropdown .menu .selected.item, .ui.form input:not([type]), .ui.form input[type="date"], .ui.form input[type="datetime-local"], .ui.form input[type="email"], .ui.form input[type="number"], .ui.form input[type="password"], .ui.form input[type="search"], .ui.form input[type="tel"], .ui.form input[type="time"], .ui.form input[type="text"], .ui.form input[type="file"], .ui.form input[type="url"] { + background-color: $input-background-color; + &::placeholder { + color: $light-background-color; + + } + } + .ui.dropdown .menu .item:hover { + background-color: $light-background-color; + color: $text-color; + + } + .main.pusher > .ui.secondary.menu { + background-color: $background-color; + box-shadow: inset 0px -2px 0px 0px $light-background-color; + .ui.item { + color: $text-color; + &.active { + box-shadow: inset 0px -2px 0px 0px $shadow-color; + } + } + } + .ui.modal { + > .header, > .content, > .actions { + background-color: $background-color; + } + > .header { + border-bottom: 1px solid $border-color; + } + + > .actions { + border-top: 1px solid $border-color; + } + } + main, .main, footer, .modal { + + .ui.menu { + background-color: $light-background-color; + .item { + + color: $text-color; + } + } + .ui.secondary.menu .dropdown.item:hover, .ui.secondary.menu .link.item:hover, .ui.secondary.menu a.item:hover { + background: $background-color; + color: $text-color; + } + .header, .ui.form .field > label, .sub.header { + color: $text-color; + } + .ui.attached.header { + background-color: transparent; + } + .ui.toggle.checkbox input:checked ~ .box, .ui.toggle.checkbox input:checked ~ label { + color: $text-color !important; + } + .ui.toggle.checkbox .box::before, .ui.toggle.checkbox label::before { + background-color: $light-background-color; + } + a:not(.ui):not(.discrete) { + color: $link-color; + } + .ui.segment:not(.basic) { + background-color: $light-background-color; + } + .ui.list, .ui.dropdown { + .item, div.item, a.item, .button.item { + background-color: $background-color; + color: $discrete-text-color; + } + .selected.item:not(:hover) { + color: $background-color; + } + } + .ui.divided.items > .item:not(:first-child) { + border-top: 1px solid $border-color; + } + .ui.items { + .extra { + color: $discrete-text-color; + } + } + label, .toggle label { + color: $text-color !important; + } + &, .main.pusher, .ui.vertical.segment { + color: $text-color; + background-color: $background-color; + } + + .discrete { + color: $discrete-text-color; + } + + .ui.table thead th, .ui.table { + color: $text-color; + } + .ui.divider:not(.vertical):not(.horizontal) { + border-top: 1px solid $border-color; + border-bottom: 1px solid $border-color; + } + .ui.cards > .card, .ui.card { + color: $text-color; + background-color: $background-color; + box-shadow: $box-shadow; + .content, .header, .description { + color: $text-color; + } + .extra, .meta { + color: $discrete-text-color; + } + } + .playlist.card { + .attached.button { + background-color: $light-background-color; + } + } + + // buttons + [class='ui button ui button'], [class='ui button'], [class='ui icon button'], [class='ui fluid button'], [class='ui cancel button'] { + background-color: $light-background-color; + color: $text-color; + &:hover { + background-color: $button-hover-color; + + } + } + .ui.buttons > .ui.button:not(.basic):not(.inverted), .ui.buttons:not(.basic):not(.inverted) > .button { + box-shadow: 0px 0px 0px 1px $light-shadow-color inset; + + } + .ui.basic.buttons:not(:hover):not(.green):not(.orange):not(.yellow):not(.red) .button, .ui.basic.button { + box-shadow: 0px 0px 0px 1px $text-color inset; + &:not(:hover):not(.green):not(.orange):not(.yellow):not(.red) { + color: $text-color !important; + } + } + .ui.basic.buttons .button, .ui.basic.button { + &:hover { + color: $text-color !important; + } + + } + .ui.basic.buttons:not(.green):not(.orange):not(.yellow):not(.red) .button:hover, .ui.basic.button:not(.green):not(.orange):not(.yellow):not(.red):hover, .ui.basic.button:not(.green):not(.orange):not(.yellow):not(.red):active, .ui.basic.button:not(.green):not(.orange):not(.yellow):not(.red):focus { + color: $background-color !important; + } + // loading /dimmers + .ui.loading.form::before { + background-color: $loading-background-color; + } + .ui.inverted.dimmer { + background-color: $loading-background-color; + } + // table + .ui.basic.table tbody tr, .ui.table tr td { + border-bottom: 1px solid $border-color; + } + .ui.table thead th { + border-bottom: 1px solid $border-color; + } + .ui.table { + &:not(.basic) { + &, thead th { + background-color: $light-background-color; + } + } + } + } + .ui.link.list.list a.item:hover, .ui.link.list.list .item a:not(.ui):not(.button):hover { + color: $link-color; + } + [data-tooltip]::after { + background-color: $light-background-color; + color: $text-color; + } + .ui.progress > .label { + color: $text-color; + + } + i.grey.icon { + color: $text-color !important; + } + input { + &::selection, &::-moz-selection { + background: $background-color; + color: $text-color; + } + } +} diff --git a/front/src/style/themes/_light.scss b/front/src/style/themes/_light.scss new file mode 100644 index 000000000..a6e1a0cdf --- /dev/null +++ b/front/src/style/themes/_light.scss @@ -0,0 +1,35 @@ + + +.theme-light { + + .main.pusher > .ui.secondary.menu { + box-shadow: inset 0px -2px 0px 0px rgba(34, 36, 38, 0.15); + background-color: white; + .ui.item { + &.active { + box-shadow: inset 0px -2px 0px 0px #000; + } + } + } + + .discrete { + color: rgba(0, 0, 0, 0.87); + } + .playlist.card { + .attached.button { + background-color: rgb(243, 244, 245); + } + } + + .disabled-row { + color: rgba(40, 40, 40, 0.3); + } + footer p { + color: grey; + } + + footer#footer div.item:hover { + color: rgba(0, 0, 0, 0.87); + } + +}