From 908e8a551e33efab508ebc6120d800603c68deb2 Mon Sep 17 00:00:00 2001 From: klevain Date: Sat, 29 Apr 2023 18:26:37 +0200 Subject: [PATCH] feat: add high-contrast theme to web ui (#5470) --- web/assets/css/transmission-app.scss | 84 +++++++++++++++++++++++++++- web/src/action-manager.js | 1 + web/src/overflow-menu.js | 37 +++++++++++- web/src/prefs.js | 6 ++ web/src/transmission.js | 8 +++ 5 files changed, 132 insertions(+), 4 deletions(-) diff --git a/web/assets/css/transmission-app.scss b/web/assets/css/transmission-app.scss index 28eda9549..ddf8a5dcb 100644 --- a/web/assets/css/transmission-app.scss +++ b/web/assets/css/transmission-app.scss @@ -16,6 +16,7 @@ $grey-800: #292929; $grey-900: #191919; $red-500: #d73a49; $white: #fff; +$black: #000; $dark-mode-white: #c9d1d9; $yellow-300: #ffea7f; $grey-40: #666; @@ -111,6 +112,44 @@ $image-play-circle-idle: '../img/play-circle-idle.svg'; --color-progressbar-paused: #{$grey-500}; --color-progressbar-leech: #{$blue-500}; --color-progressbar-seed-paused: #{$grey-500}; + + .contrast-more { + --color-fg-error: #{$red-500}; + --color-fg-port-closed: #{$red-500}; + --color-fg-port-open: #{$green-500}; + --color-bg-hover: #{$grey-40}; + --color-fg-primary: #{$white}; + --color-bg-primary: #{$black}; + --color-bg-primary-hover: #{$black}; + --color-bg-odd: #{$black}; + --color-bg-even: #{$black}; + --color-bg-menu: #{$black}; + --color-fg-secondary: #{$white}; + --color-fg-on-popup: #{$white}; + --color-fg-disabled: #{$white}; + --color-bg-popup: #{$black}; + --color-bg-warn: #cf6679; + --color-fg-warn: #{$black}; + --color-border: #{$white}; + --color-border-selected: #{$white}; + --color-fg-tertiary: #{$white}; + --color-toolbar-background: #{$black}; + --color-inspector-background: #{$black}; + --color-inspector-tabs: #{$white}; + --color-bg-selected: #{$blue-500}; + --color-bg-tabs: #{$black}; + --color-fg-tabs: #{$white}; + --color-default-border: #{$white}; + --color-progressbar-seed-1: #{$green-700}; + --color-progressbar-seed-2: #{$green-500}; + --color-progressbar-seed-paused: #{$grey-500}; + --color-progressbar-background-2: #{$grey-500}; + --color-progressbar-verify: #{$yellow-300}; + --color-progressbar-magnet: #{$yellow-300}; + --color-progressbar-paused: #{$grey-500}; + --color-progressbar-leech: #{$blue-500}; + --color-progressbar-queued: #{$blue-100}; + } } @media (prefers-color-scheme: light) { --progress: 100%; @@ -147,6 +186,44 @@ $image-play-circle-idle: '../img/play-circle-idle.svg'; --color-progressbar-seed-1: #{$green-300}; --color-progressbar-seed-2: #{$green-200}; --color-progressbar-seed-paused: #{$grey-200}; + + .contrast-more { + --color-fg-error: #{$red-500}; + --color-fg-port-closed: #{$red-500}; + --color-fg-port-open: #{$green-500}; + --color-bg-hover: #{$grey-40}; + --color-fg-primary: #{$black}; + --color-bg-primary: #{$white}; + --color-bg-primary-hover: #{$white}; + --color-bg-odd: #{$white}; + --color-bg-even: #{$white}; + --color-bg-menu: #{$white}; + --color-fg-secondary: #{$black}; + --color-fg-on-popup: #{$black}; + --color-fg-disabled: #{$black}; + --color-bg-popup: #{$white}; + --color-bg-warn: #cf6679; + --color-fg-warn: #{$white}; + --color-border: #{$black}; + --color-border-selected: #{$black}; + --color-fg-tertiary: #{$black}; + --color-toolbar-background: #{$white}; + --color-inspector-background: #{$white}; + --color-inspector-tabs: #{$black}; + --color-bg-selected: #{$blue-300}; + --color-bg-tabs: #{$white}; + --color-fg-tabs: #{$black}; + --color-default-border: #{$black}; + --color-progressbar-seed-1: #{$green-700}; + --color-progressbar-seed-2: #{$green-500}; + --color-progressbar-seed-paused: #{$grey-500}; + --color-progressbar-background-2: #{$grey-500}; + --color-progressbar-verify: #{$yellow-300}; + --color-progressbar-magnet: #{$yellow-300}; + --color-progressbar-paused: #{$grey-500}; + --color-progressbar-leech: #{$blue-500}; + --color-progressbar-queued: #{$blue-100}; + } } } @@ -239,7 +316,10 @@ $toolbar-height: $toolbar-height-number * 1px; &:disabled { cursor: default; - opacity: 0.25; + // opacity: 0.25; + svg { + stroke: var(--color-fg-disabled); + } } } @@ -1116,7 +1196,7 @@ $video-image: '../img/film.svg'; } .torrent-row td { - background: var(--color-default-border); + background: var(--color-bg-odd); color: var(--color-fg-primary); font-size: normal; font-weight: bolder; diff --git a/web/src/action-manager.js b/web/src/action-manager.js index f74e62e54..847c6a2a8 100644 --- a/web/src/action-manager.js +++ b/web/src/action-manager.js @@ -77,6 +77,7 @@ export class ActionManager extends EventTarget { }, 'start-all-torrents': { enabled: false, text: 'Start all' }, 'toggle-compact-rows': { enabled: true, text: 'Compact rows' }, + 'toggle-contrast': { enabled: true, text: 'High contrast UI' }, 'trash-selected-torrents': { enabled: false, text: 'Trash data and remove from list…', diff --git a/web/src/overflow-menu.js b/web/src/overflow-menu.js index 01387ec52..e99d4c22b 100644 --- a/web/src/overflow-menu.js +++ b/web/src/overflow-menu.js @@ -223,7 +223,7 @@ export class OverflowMenu extends EventTarget { div.classList.add('table-row'); options.append(div); - const action = 'toggle-compact-rows'; + let action = 'toggle-compact-rows'; check = document.createElement('input'); check.id = 'display-compact-check'; check.dataset.action = action; @@ -235,9 +235,11 @@ export class OverflowMenu extends EventTarget { label.for = check.id; label.setAttribute('for', check.id); label.textContent = this.action_manager.text(action); - div.append(label); check.checked = this.prefs.display_mode === Prefs.DisplayCompact; + + div.append(label); + check.addEventListener('input', (event_) => { const { checked } = event_.target; this.prefs.display_mode = checked @@ -245,6 +247,37 @@ export class OverflowMenu extends EventTarget { : Prefs.DisplayFull; }); + // contrast + + div = document.createElement('div'); + div.classList.add('table-row'); + options.append(div); + + action = 'toggle-contrast'; + check = document.createElement('input'); + check.id = 'contrast-more-check'; + check.dataset.action = action; + check.type = 'checkbox'; + check.classList.add('switch'); + + label = document.createElement('label'); + label.id = 'contrast-more-label'; + label.for = check.id; + label.setAttribute('for', check.id); + label.textContent = this.action_manager.text(action); + + check.checked = this.prefs.contrast_mode === Prefs.ContrastMore; + + div.append(check); + div.append(label); + + check.addEventListener('input', (event_) => { + const { checked } = event_.target; + this.prefs.contrast_mode = checked + ? Prefs.ContrastMore + : Prefs.ContrastLess; + }); + // fullscreen div = document.createElement('div'); diff --git a/web/src/prefs.js b/web/src/prefs.js index 73d3e50d5..7a565c08d 100644 --- a/web/src/prefs.js +++ b/web/src/prefs.js @@ -94,6 +94,9 @@ Prefs.AltSpeedEnabled = 'alt-speed-enabled'; Prefs.DisplayCompact = 'compact'; Prefs.DisplayFull = 'full'; Prefs.DisplayMode = 'display-mode'; +Prefs.ContrastLess = 'less'; +Prefs.ContrastMore = 'more'; +Prefs.ContrastMode = 'contrast-mode'; Prefs.FilterActive = 'active'; Prefs.FilterAll = 'all'; Prefs.FilterDownloading = 'downloading'; @@ -119,6 +122,9 @@ Prefs.SortMode = 'sort-mode'; Prefs._Defaults = { [Prefs.AltSpeedEnabled]: false, [Prefs.DisplayMode]: Prefs.DisplayFull, + [Prefs.ContrastMode]: window.matchMedia('(prefers-contrast: more)').matches + ? Prefs.ContrastMore + : Prefs.ContrastLess, [Prefs.FilterMode]: Prefs.FilterAll, [Prefs.NotificationsEnabled]: false, [Prefs.RefreshRate]: 5, diff --git a/web/src/transmission.js b/web/src/transmission.js index e3653d4ea..8942928dd 100644 --- a/web/src/transmission.js +++ b/web/src/transmission.js @@ -318,6 +318,14 @@ export class Transmission extends EventTarget { this.refilterAllSoon(); break; } + case Prefs.ContrastMode: { + // Add custom class to the body/html element to get the appropriate contrast color scheme + document.body.classList.remove('contrast-more'); + document.body.classList.remove('contrast-less'); + document.body.classList.add(`contrast-${value}`); + // this.refilterAllSoon(); + break; + } case Prefs.FilterMode: case Prefs.SortDirection: