diff --git a/assets/javascripts/app/settings.coffee b/assets/javascripts/app/settings.coffee
index 8a4c2b72..eb1c9289 100644
--- a/assets/javascripts/app/settings.coffee
+++ b/assets/javascripts/app/settings.coffee
@@ -7,7 +7,8 @@ class app.Settings
'arrowScroll'
'analyticsConsent'
'docs'
- 'dark'
+ 'dark' # legacy
+ 'theme'
'layout'
'size'
'tips'
@@ -31,10 +32,16 @@ class app.Settings
manualUpdate: false
schema: 1
analyticsConsent: false
+ theme: 'auto'
constructor: ->
@store = new CookiesStore
@cache = {}
+ @autoSupported = window.matchMedia('(prefers-color-scheme)').media != 'not all'
+ if @autoSupported
+ @darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)')
+ @darkModeQuery.addListener => @setTheme(@get('theme'))
+
get: (key) ->
return @cache[key] if @cache.hasOwnProperty(key)
@@ -43,7 +50,7 @@ class app.Settings
set: (key, value) ->
@store.set(key, value)
delete @cache[key]
- @toggleDark(value) if key == 'dark'
+ @setTheme(value) if key == 'theme'
return
del: (key) ->
@@ -114,15 +121,24 @@ class app.Settings
return
initLayout: ->
- @toggleDark(@get('dark') is 1)
+ if @get('dark') is 1
+ @set('theme', 'dark')
+ @del 'dark'
+ @setTheme(@get('theme'))
@toggleLayout(layout, @hasLayout(layout)) for layout in @LAYOUTS
@initSidebarWidth()
return
- toggleDark: (enable) ->
+ setTheme: (theme) ->
+ if theme is 'auto'
+ theme = if @darkModeQuery.matches then 'dark' else 'default'
classList = document.documentElement.classList
- classList.toggle('_theme-default', !enable)
- classList.toggle('_theme-dark', enable)
+ classList.remove('_theme-default', '_theme-dark')
+ classList.add('_theme-' + theme)
+ @updateColorMeta()
+ return
+
+ updateColorMeta: ->
color = getComputedStyle(document.documentElement).getPropertyValue('--headerBackground').trim()
$('meta[name=theme-color]').setAttribute('content', color)
return
diff --git a/assets/javascripts/templates/pages/settings_tmpl.coffee b/assets/javascripts/templates/pages/settings_tmpl.coffee
index d5cb0985..6d227069 100644
--- a/assets/javascripts/templates/pages/settings_tmpl.coffee
+++ b/assets/javascripts/templates/pages/settings_tmpl.coffee
@@ -1,13 +1,29 @@
+themeOption = ({ label, value }, settings) -> """
+
+"""
+
app.templates.settingsPage = (settings) -> """
Preferences
+
+
Theme:
+
+ #{if settings.autoSupported
+ themeOption label: "Automatic Matches system setting", value: "auto", settings
+ else
+ ""}
+ #{themeOption label: "Light", value: "default", settings}
+ #{themeOption label: "Dark", value: "dark", settings}
+
+
+
General:
-
diff --git a/assets/javascripts/views/content/settings_page.coffee b/assets/javascripts/views/content/settings_page.coffee
index 9ca606c6..2db7c6bc 100644
--- a/assets/javascripts/views/content/settings_page.coffee
+++ b/assets/javascripts/views/content/settings_page.coffee
@@ -11,19 +11,20 @@ class app.views.SettingsPage extends app.View
currentSettings: ->
settings = {}
- settings.dark = app.settings.get('dark')
+ settings.theme = app.settings.get('theme')
settings.smoothScroll = !app.settings.get('fastScroll')
settings.arrowScroll = app.settings.get('arrowScroll')
settings.autoInstall = app.settings.get('autoInstall')
settings.analyticsConsent = app.settings.get('analyticsConsent')
+ settings.autoSupported = app.settings.autoSupported
settings[layout] = app.settings.hasLayout(layout) for layout in app.settings.LAYOUTS
settings
getTitle: ->
'Preferences'
- toggleDark: (enable) ->
- app.settings.set('dark', !!enable)
+ setTheme: (value) ->
+ app.settings.set('theme', value)
return
toggleLayout: (layout, enable) ->
@@ -74,8 +75,8 @@ class app.views.SettingsPage extends app.View
onChange: (event) =>
input = event.target
switch input.name
- when 'dark'
- @toggleDark input.checked
+ when 'theme'
+ @setTheme input.value
when 'layout'
@toggleLayout input.value, input.checked
when 'smoothScroll'
diff --git a/assets/stylesheets/components/_settings.scss b/assets/stylesheets/components/_settings.scss
index 86bbf005..807f5500 100644
--- a/assets/stylesheets/components/_settings.scss
+++ b/assets/stylesheets/components/_settings.scss
@@ -48,15 +48,21 @@
}
._settings-label {
- margin: 0 0 .375rem;
+ &:not(._theme-label) {
+ margin: 0 0 .375rem;
+ }
> small {
display: block;
color: var(--textColorLight);
margin-left: 1.75rem;
}
+ &._theme-label > small {
+ display: inline-block;
+ margin-left: 0.75rem;
+ }
- input[type=checkbox] {
+ input[type=checkbox], input[type=radio] {
vertical-align: top;
margin: .25rem .375rem;
}
diff --git a/assets/stylesheets/global/_base.scss b/assets/stylesheets/global/_base.scss
index b81d5a93..ec7be4f2 100644
--- a/assets/stylesheets/global/_base.scss
+++ b/assets/stylesheets/global/_base.scss
@@ -10,6 +10,13 @@ html {
@import 'global/print';
}
+html._theme-default {
+ color-scheme: light only;
+}
+html._theme-dark {
+ color-scheme: dark only;
+}
+
body {
height: 100%;
margin: 0;