Add support for prefers-color-scheme media query
Showing the changes to assets/javascripts (3 of 5 files). Show all
Unified · Split
| @@ -7,7 +7,8 @@ class app.Settings · 6 unmodified lines | |||
| 7 | 'arrowScroll' | 7 | 'arrowScroll' |
| 8 | 'analyticsConsent' | 8 | 'analyticsConsent' |
| 9 | 'docs' | 9 | 'docs' |
| 10 | 'dark' | 10 | 'dark' # legacy |
| 11 | 'theme' | ||
| 11 | 'layout' | 12 | 'layout' |
| 12 | 'size' | 13 | 'size' |
| 13 | 'tips' | 14 | 'tips' |
| @@ -31,10 +32,16 @@ class app.Settings · 17 unmodified lines | |||
| 31 | manualUpdate: false | 32 | manualUpdate: false |
| 32 | schema: 1 | 33 | schema: 1 |
| 33 | analyticsConsent: false | 34 | analyticsConsent: false |
| 35 | theme: 'auto' | ||
| 34 | 36 | ||
| 35 | constructor: -> | 37 | constructor: -> |
| 36 | @store = new CookiesStore | 38 | @store = new CookiesStore |
| 37 | @cache = {} | 39 | @cache = {} |
| 40 | @autoSupported = window.matchMedia('(prefers-color-scheme)').media != 'not all' | ||
| 41 | if @autoSupported | ||
| 42 | @darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)') | ||
| 43 | @darkModeQuery.addListener => @setTheme(@get('theme')) | ||
| 44 | |||
| 38 | 45 | ||
| 39 | get: (key) -> | 46 | get: (key) -> |
| 40 | return @cache[key] if @cache.hasOwnProperty(key) | 47 | return @cache[key] if @cache.hasOwnProperty(key) |
| @@ -43,7 +50,7 @@ class app.Settings · 2 unmodified lines | |||
| 43 | set: (key, value) -> | 50 | set: (key, value) -> |
| 44 | @store.set(key, value) | 51 | @store.set(key, value) |
| 45 | delete @cache[key] | 52 | delete @cache[key] |
| 46 | @toggleDark(value) if key == 'dark' | 53 | @setTheme(value) if key == 'theme' |
| 47 | return | 54 | return |
| 48 | 55 | ||
| 49 | del: (key) -> | 56 | del: (key) -> |
| @@ -114,15 +121,24 @@ class app.Settings · 64 unmodified lines | |||
| 114 | return | 121 | return |
| 115 | 122 | ||
| 116 | initLayout: -> | 123 | initLayout: -> |
| 117 | @toggleDark(@get('dark') is 1) | 124 | if @get('dark') is 1 |
| 125 | @set('theme', 'dark') | ||
| 126 | @del 'dark' | ||
| 127 | @setTheme(@get('theme')) | ||
| 118 | @toggleLayout(layout, @hasLayout(layout)) for layout in @LAYOUTS | 128 | @toggleLayout(layout, @hasLayout(layout)) for layout in @LAYOUTS |
| 119 | @initSidebarWidth() | 129 | @initSidebarWidth() |
| 120 | return | 130 | return |
| 121 | 131 | ||
| 122 | toggleDark: (enable) -> | 132 | setTheme: (theme) -> |
| 133 | if theme is 'auto' | ||
| 134 | theme = if @darkModeQuery.matches then 'dark' else 'default' | ||
| 123 | classList = document.documentElement.classList | 135 | classList = document.documentElement.classList |
| 124 | classList.toggle('_theme-default', !enable) | 136 | classList.remove('_theme-default', '_theme-dark') |
| 125 | classList.toggle('_theme-dark', enable) | 137 | classList.add('_theme-' + theme) |
| 138 | @updateColorMeta() | ||
| 139 | return | ||
| 140 | |||
| 141 | updateColorMeta: -> | ||
| 126 | color = getComputedStyle(document.documentElement).getPropertyValue('--headerBackground').trim() | 142 | color = getComputedStyle(document.documentElement).getPropertyValue('--headerBackground').trim() |
| 127 | $('meta[name=theme-color]').setAttribute('content', color) | 143 | $('meta[name=theme-color]').setAttribute('content', color) |
| 128 | return | 144 | return |
| @@ -1,13 +1,29 @@ | |||
| 1 | themeOption = ({ label, value }, settings) -> """ | ||
| 2 | <label class="_settings-label _theme-label"> | ||
| 3 | <input type="radio" name="theme" value="#{value}"#{if settings.theme == value then ' checked' else ''}> | ||
| 4 | #{label} | ||
| 5 | </label> | ||
| 6 | """ | ||
| 7 | |||
| 1 | app.templates.settingsPage = (settings) -> """ | 8 | app.templates.settingsPage = (settings) -> """ |
| 2 | <h1 class="_lined-heading">Preferences</h1> | 9 | <h1 class="_lined-heading">Preferences</h1> |
| 3 | 10 | ||
| 11 | <div class="_settings-fieldset"> | ||
| 12 | <h2 class="_settings-legend">Theme:</h2> | ||
| 13 | <div class="_settings-inputs"> | ||
| 14 | #{if settings.autoSupported | ||
| 15 | themeOption label: "Automatic <small>Matches system setting</small>", value: "auto", settings | ||
| 16 | else | ||
| 17 | ""} | ||
| 18 | #{themeOption label: "Light", value: "default", settings} | ||
| 19 | #{themeOption label: "Dark", value: "dark", settings} | ||
| 20 | </div> | ||
| 21 | </div> | ||
| 22 | |||
| 4 | <div class="_settings-fieldset"> | 23 | <div class="_settings-fieldset"> |
| 5 | <h2 class="_settings-legend">General:</h2> | 24 | <h2 class="_settings-legend">General:</h2> |
| 6 | 25 | ||
| 7 | <div class="_settings-inputs"> | 26 | <div class="_settings-inputs"> |
| 8 | <label class="_settings-label"> | ||
| 9 | <input type="checkbox" form="settings" name="dark" value="1"#{if settings.dark then ' checked' else ''}>Enable dark theme | ||
| 10 | </label> | ||
| 11 | <label class="_settings-label _setting-max-width"> | 27 | <label class="_settings-label _setting-max-width"> |
| 12 | <input type="checkbox" form="settings" name="layout" value="_max-width"#{if settings['_max-width'] then ' checked' else ''}>Enable fixed-width layout | 28 | <input type="checkbox" form="settings" name="layout" value="_max-width"#{if settings['_max-width'] then ' checked' else ''}>Enable fixed-width layout |
| 13 | </label> | 29 | </label> |
| @@ -11,19 +11,20 @@ class app.views.SettingsPage extends app.View · 10 unmodified lines | |||
| 11 | 11 | ||
| 12 | currentSettings: -> | 12 | currentSettings: -> |
| 13 | settings = {} | 13 | settings = {} |
| 14 | settings.dark = app.settings.get('dark') | 14 | settings.theme = app.settings.get('theme') |
| 15 | settings.smoothScroll = !app.settings.get('fastScroll') | 15 | settings.smoothScroll = !app.settings.get('fastScroll') |
| 16 | settings.arrowScroll = app.settings.get('arrowScroll') | 16 | settings.arrowScroll = app.settings.get('arrowScroll') |
| 17 | settings.autoInstall = app.settings.get('autoInstall') | 17 | settings.autoInstall = app.settings.get('autoInstall') |
| 18 | settings.analyticsConsent = app.settings.get('analyticsConsent') | 18 | settings.analyticsConsent = app.settings.get('analyticsConsent') |
| 19 | settings.autoSupported = app.settings.autoSupported | ||
| 19 | settings[layout] = app.settings.hasLayout(layout) for layout in app.settings.LAYOUTS | 20 | settings[layout] = app.settings.hasLayout(layout) for layout in app.settings.LAYOUTS |
| 20 | settings | 21 | settings |
| 21 | 22 | ||
| 22 | getTitle: -> | 23 | getTitle: -> |
| 23 | 'Preferences' | 24 | 'Preferences' |
| 24 | 25 | ||
| 25 | toggleDark: (enable) -> | 26 | setTheme: (value) -> |
| 26 | app.settings.set('dark', !!enable) | 27 | app.settings.set('theme', value) |
| 27 | return | 28 | return |
| 28 | 29 | ||
| 29 | toggleLayout: (layout, enable) -> | 30 | toggleLayout: (layout, enable) -> |
| @@ -74,8 +75,8 @@ class app.views.SettingsPage extends app.View · 44 unmodified lines | |||
| 74 | onChange: (event) => | 75 | onChange: (event) => |
| 75 | input = event.target | 76 | input = event.target |
| 76 | switch input.name | 77 | switch input.name |
| 77 | when 'dark' | 78 | when 'theme' |
| 78 | @toggleDark input.checked | 79 | @setTheme input.value |
| 79 | when 'layout' | 80 | when 'layout' |
| 80 | @toggleLayout input.value, input.checked | 81 | @toggleLayout input.value, input.checked |
| 81 | when 'smoothScroll' | 82 | when 'smoothScroll' |