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 | 7 | 'arrowScroll' |
| 8 | 8 | 'analyticsConsent' |
| 9 | 9 | 'docs' |
| 10 | − 'dark' | |
| 10 | + 'dark' # legacy | |
| 11 | + 'theme' | |
| 11 | 12 | 'layout' |
| 12 | 13 | 'size' |
| 13 | 14 | 'tips' |
| @@ -31,10 +32,16 @@ class app.Settings · 17 unmodified lines | ||
| 31 | 32 | manualUpdate: false |
| 32 | 33 | schema: 1 |
| 33 | 34 | analyticsConsent: false |
| 35 | + theme: 'auto' | |
| 34 | 36 | |
| 35 | 37 | constructor: -> |
| 36 | 38 | @store = new CookiesStore |
| 37 | 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 | 46 | get: (key) -> |
| 40 | 47 | return @cache[key] if @cache.hasOwnProperty(key) |
| @@ -43,7 +50,7 @@ class app.Settings · 2 unmodified lines | ||
| 43 | 50 | set: (key, value) -> |
| 44 | 51 | @store.set(key, value) |
| 45 | 52 | delete @cache[key] |
| 46 | − @toggleDark(value) if key == 'dark' | |
| 53 | + @setTheme(value) if key == 'theme' | |
| 47 | 54 | return |
| 48 | 55 | |
| 49 | 56 | del: (key) -> |
| @@ -114,15 +121,24 @@ class app.Settings · 64 unmodified lines | ||
| 114 | 121 | return |
| 115 | 122 | |
| 116 | 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 | 128 | @toggleLayout(layout, @hasLayout(layout)) for layout in @LAYOUTS |
| 119 | 129 | @initSidebarWidth() |
| 120 | 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 | 135 | classList = document.documentElement.classList |
| 124 | − classList.toggle('_theme-default', !enable) | |
| 125 | − classList.toggle('_theme-dark', enable) | |
| 136 | + classList.remove('_theme-default', '_theme-dark') | |
| 137 | + classList.add('_theme-' + theme) | |
| 138 | + @updateColorMeta() | |
| 139 | + return | |
| 140 | + | |
| 141 | + updateColorMeta: -> | |
| 126 | 142 | color = getComputedStyle(document.documentElement).getPropertyValue('--headerBackground').trim() |
| 127 | 143 | $('meta[name=theme-color]').setAttribute('content', color) |
| 128 | 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 | 8 | app.templates.settingsPage = (settings) -> """ |
| 2 | 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 | 23 | <div class="_settings-fieldset"> |
| 5 | 24 | <h2 class="_settings-legend">General:</h2> |
| 6 | 25 | |
| 7 | 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 | 27 | <label class="_settings-label _setting-max-width"> |
| 12 | 28 | <input type="checkbox" form="settings" name="layout" value="_max-width"#{if settings['_max-width'] then ' checked' else ''}>Enable fixed-width layout |
| 13 | 29 | </label> |
| @@ -11,19 +11,20 @@ class app.views.SettingsPage extends app.View · 10 unmodified lines | ||
| 11 | 11 | |
| 12 | 12 | currentSettings: -> |
| 13 | 13 | settings = {} |
| 14 | − settings.dark = app.settings.get('dark') | |
| 14 | + settings.theme = app.settings.get('theme') | |
| 15 | 15 | settings.smoothScroll = !app.settings.get('fastScroll') |
| 16 | 16 | settings.arrowScroll = app.settings.get('arrowScroll') |
| 17 | 17 | settings.autoInstall = app.settings.get('autoInstall') |
| 18 | 18 | settings.analyticsConsent = app.settings.get('analyticsConsent') |
| 19 | + settings.autoSupported = app.settings.autoSupported | |
| 19 | 20 | settings[layout] = app.settings.hasLayout(layout) for layout in app.settings.LAYOUTS |
| 20 | 21 | settings |
| 21 | 22 | |
| 22 | 23 | getTitle: -> |
| 23 | 24 | 'Preferences' |
| 24 | 25 | |
| 25 | − toggleDark: (enable) -> | |
| 26 | − app.settings.set('dark', !!enable) | |
| 26 | + setTheme: (value) -> | |
| 27 | + app.settings.set('theme', value) | |
| 27 | 28 | return |
| 28 | 29 | |
| 29 | 30 | toggleLayout: (layout, enable) -> |
| @@ -74,8 +75,8 @@ class app.views.SettingsPage extends app.View · 44 unmodified lines | ||
| 74 | 75 | onChange: (event) => |
| 75 | 76 | input = event.target |
| 76 | 77 | switch input.name |
| 77 | − when 'dark' | |
| 78 | − @toggleDark input.checked | |
| 78 | + when 'theme' | |
| 79 | + @setTheme input.value | |
| 79 | 80 | when 'layout' |
| 80 | 81 | @toggleLayout input.value, input.checked |
| 81 | 82 | when 'smoothScroll' |