History

Merge pull request #1086 from simon04/prefers-color-scheme

Add support for prefers-color-scheme media query

Jed Fox committed 6 years ago

Commit 364be3b2 parents af8576f cc406a85 files · +62 −16Browse files

Unified · Split

5 files
  1. assets/javascripts/app/settings.coffee +22 −6
  2. assets/javascripts/templates/pages/settings_tmpl.coffee +19 −3
  3. assets/javascripts/views/content/settings_page.coffee +6 −5
  4. assets/stylesheets/components/_settings.scss +8 −2
  5. assets/stylesheets/global/_base.scss +7 −0
assets/javascripts/app/settings.coffee+22−6
@@ -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: false32 manualUpdate: false
32 schema: 133 schema: 1
33 analyticsConsent: false34 analyticsConsent: false
35 theme: 'auto'
34 36
35 constructor: ->37 constructor: ->
36 @store = new CookiesStore38 @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 return54 return
48 55
49 del: (key) ->56 del: (key) ->
@@ -114,15 +121,24 @@ class app.Settings · 64 unmodified lines
114 return121 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 @LAYOUTS128 @toggleLayout(layout, @hasLayout(layout)) for layout in @LAYOUTS
119 @initSidebarWidth()129 @initSidebarWidth()
120 return130 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.classList135 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 return144 return
assets/javascripts/templates/pages/settings_tmpl.coffee+19−3
@@ -1,13 +1,29 @@
1themeOption = ({ 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
1app.templates.settingsPage = (settings) -> """8app.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 layout28 <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>
assets/javascripts/views/content/settings_page.coffee+6−5
@@ -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.LAYOUTS20 settings[layout] = app.settings.hasLayout(layout) for layout in app.settings.LAYOUTS
20 settings21 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 return28 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.target76 input = event.target
76 switch input.name77 switch input.name
77 when 'dark'78 when 'theme'
78 @toggleDark input.checked79 @setTheme input.value
79 when 'layout'80 when 'layout'
80 @toggleLayout input.value, input.checked81 @toggleLayout input.value, input.checked
81 when 'smoothScroll'82 when 'smoothScroll'
assets/stylesheets/components/_settings.scss+8−2
@@ -48,15 +48,21 @@ · 47 unmodified lines
48}48}
49 49
50._settings-label {50._settings-label {
51 margin: 0 0 .375rem;51 &:not(._theme-label) {
52 margin: 0 0 .375rem;
53 }
52 54
53 > small {55 > small {
54 display: block;56 display: block;
55 color: var(--textColorLight);57 color: var(--textColorLight);
56 margin-left: 1.75rem;58 margin-left: 1.75rem;
57 }59 }
60 &._theme-label > small {
61 display: inline-block;
62 margin-left: 0.75rem;
63 }
58 64
59 input[type=checkbox] {65 input[type=checkbox], input[type=radio] {
60 vertical-align: top;66 vertical-align: top;
61 margin: .25rem .375rem;67 margin: .25rem .375rem;
62 }68 }
assets/stylesheets/global/_base.scss+7−0
@@ -10,6 +10,13 @@ html { · 9 unmodified lines
10 @import 'global/print';10 @import 'global/print';
11}11}
12 12
13html._theme-default {
14 color-scheme: light only;
15}
16html._theme-dark {
17 color-scheme: dark only;
18}
19
13body {20body {
14 height: 100%;21 height: 100%;
15 margin: 0;22 margin: 0;