by Oskar Hane
由Oskar Hane
如何在JavaScript中检测用户的首选配色方案 (How to detect a user’s preferred color scheme in JavaScript)
In recent versions of macOS (Mojave) and Windows 10, users have been able to enable a system level dark mode. This works well and is easy to detect for native applications.
在最新版本的macOS(Mojave)和Windows 10中,用户已经能够启用系统级暗模式。 这很好用,对于本机应用程序很容易检测。
Websites have been the odd apps where it’s up to the website publisher to decide what color scheme the users should use. Some websites do offer theme support. For the users to switch, they have to find the configuration for it and manually update the settings for each individual website.
网站是奇怪的应用程序,由网站发布者来决定用户应使用哪种配色方案。 一些网站确实提供主题支持。 为了让用户切换,他们必须找到它的配置并手动更新每个单独网站的设置。
Would it be possible to have this detection done automatically and have websites present a theme that respects the user’s preference?
是否可以自动完成此检测并让网站呈现尊重用户偏好的主题?
CSS媒体查询“ prefers-color-scheme'
草稿 (CSS media query ‘prefers-color-scheme'
draft)
There is a CSS media queries draft level 5 where prefers-color-scheme is specified. It is meant to detect if the user has requested the system to use a light or dark color theme.
有一个CSS媒体查询草稿级别5,其中指定了preferreds-color-scheme 。 它旨在检测用户是否已请求系统使用浅色或深色主题。
This sounds like something we can work with! We need to stay up to date with any changes to the draft, though, as it might change at any time since it’s just a… draft. The prefers-color-scheme
query can have three different values: light
, dark
, and no-preference
.
听起来我们可以合作! 但是,我们需要及时了解草案的任何更改,因为它只是一个草案,因此随时可能更改。 prefers-color-scheme
查询可以具有三个不同的值: light
, dark
和no-preference
。
截至2019年3月的Web浏览器支持 (Web browser support as of March 2019)
The current browser support is very limited, and it’s not available in any of the stable releases of any vendor. We can only enjoy this in Safari Technology Preview of version 12.1 and in Firefox 67.0a1. What’s great is that there are binaries that do support it, so we can work with it and try it out in web browsers. For current browser support, check out https://caniuse.com/#search=prefers-color-scheme.
当前的浏览器支持非常有限,并且任何供应商的任何稳定版本中均不提供。 我们只能在12.1版的Safari技术预览版和Firefox 67.0a1中享受此功能 。 很棒的是,有二进制文件支持它,因此我们可以使用它并在网络浏览器中进行尝试。 对于当前的浏览器支持,请访问https://caniuse.com/#search=prefers-color-scheme 。
为什么仅CSS检测还不够 (Why CSS only detection isn’t enough)
The common approach I’ve seen so far is to use a CSS only approach and override CSS rules for certain classes when a media query is matched.Something like this:
到目前为止,我见过的常见方法是使用仅CSS的方法,并在匹配媒体查询时为某些类覆盖CSS规则。
/* global.css */
.themed { display: block; width: 10em; height: 10em; background: black; color: white;}
@media (prefers-color-scheme: light) { .themed { background: white; color: black; }}
As this works fine for many use cases, there are styling techniques that do not use CSS in a way like this. If styled-components is used for theming, for example, then a JS object is replaced when the theme is changed.
由于这在许多用例中都可以正常使用,因此有些样式化技术不以这种方式使用CSS。 例如,如果使用样式组件进行主题设置,则在更改主题时将替换JS对象。
Having access to the preferred scheme is also useful for analytics and more predictable CSS overrides as well as more fine-grained control over which elements should be themed and not.
可以使用首选方案对于分析和更可预测CSS覆盖以及更细粒度的控制哪些元素应该主题而不是主题也很有用。
初始JS方法 (Initial JS approach)
I’ve learned in the past that you can do media query detection by setting the CSS content
of an element to a value if a media query is matched. This is definitely a hack, but it works!
过去我了解到,如果匹配了媒体查询,则可以通过将元素CSS content
设置为值来进行媒体查询检测。 这绝对是一个hack,但是行得通!
Something like this:
像这样:
So when a user loads the CSS and the media query matches one of the above color schemes, the content
property value of the html
element is set to either ‘light’ or ‘dark’.
因此,当用户加载CSS且媒体查询与上述配色方案之一匹配时, html
元素的content
属性值将设置为“亮”或“暗”。
The question then is, how do we read the content
value of the html
element?
然后的问题是,我们如何读取html
元素的content
值?
We can use window.getComputedStyle, like this:
我们可以这样使用window.getComputedStyle :
And this works fine! This approach is fine for a one-time read, but it’s not reactive and automatically updates when the user changes their system color scheme. To be updated, a page reload is needed (or have the above read done at an interval).
而且这很好! 这种方法适合一次性阅读 ,但是它不是被动的,当用户更改系统配色方案时会自动更新。 要进行更新,需要重新加载页面(或每隔一段时间读取一次)。
React式JS方法 (Reactive JS approach)
How can we know when the user changes the system color scheme? Are there any events we can listen to?
我们如何知道用户何时更改系统配色方案? 有什么事我们可以听吗?
Yes there are!
是的,有!
There is window.matchMedia available in modern web browsers.
What’s great with matchMedia
is that we can attach a listener to it that will be called anytime the match changes.
matchMedia
在于,我们可以将侦听器附加到该侦听器,只要匹配更改,该侦听器就会被调用。
The listener will be called with an object containing the information if the media query started matching or if it stopped matching. With this info, we can skip the CSS altogether and just work with JS.
如果媒体查询开始匹配或停止匹配,则将使用包含信息的对象来调用侦听器。 有了这些信息,我们可以完全跳过CSS,而只使用JS。
This approach works really well in the supported web browsers and just opts out if window.matchMedia
isn't supported.
这种方法在受支持的Web浏览器中效果很好,并且仅在不支持window.matchMedia
选择退出。
React钩 (React hook)
Since we are using React in neo4j-browser, I wrote this as a custom React hook to make it easy to re-use in all of our apps and fit into the React system.
由于我们在neo4j-browser中使用React, 因此我将其编写为自定义React钩子,以使其易于在所有应用程序中重复使用并适合React系统。
It’s a bit more code than in the first short proof-of-concept. We have better error detection and we also remove the event listeners when the hook un-mounts.
比第一个简短的概念验证中的代码要多。 我们有更好的错误检测功能,并且在挂接卸载时也删除了事件侦听器。
In our use case, the users can choose to override the autodetected scheme with something else (we offer an outlined theme for example, often used when doing presentations).
在我们的用例中,用户可以选择用其他方法替代自动检测的方案(例如,我们提供概述的主题,通常在进行演示时使用)。
And then use it like this in the application layer:
然后在应用程序层中像这样使用它:
The last part depends on how theming is made in your application. In the example above, the theme data object is passed into a context provider that makes this object available throughout the whole React application.
最后一部分取决于在应用程序中如何进行主题化。 在上面的示例中,主题数据对象被传递到上下文提供程序中,该上下文提供程序使该对象可在整个React应用程序中使用。
最终结果 (End result)
Here’s a gif with the end result, and as you can see, it’s instant.
这是带有最终结果的gif,并且您可以看到,它是即时的。
最后的想法 (Final thoughts)
This was a fun experiment made during a so-called “Lab Day” we have in the UX team at Neo4j. The early stages of the spec and (therefore) the lack of browser support does not justify this to make it into any product yet. But support might come sooner than later.
这是我们在Neo4j的UX团队所谓的“实验室日”期间进行的有趣实验。 规范的早期阶段以及(因此)缺乏浏览器支持并不能证明它可以用于任何产品。 但是支持可能迟早会到来。
And besides, we do ship some Electron-based products and there is an electron.systemPreferences.isDarkMode()
available there...
除此之外,我们确实提供了一些基于Electron的产品,并且在那里提供了electron.systemPreferences.isDarkMode()
...
关于作者 (About the author)
Oskar Hane is a team lead / senior engineer at Neo4j.He works on multiple of Neo4j:s end-user applications and code libraries and have authored two tech books.
Oskar Hane是Neo4j的团队负责人/高级工程师。他从事多个Neo4j:s的最终用户应用程序和代码库的工作,并撰写了两本技术书籍。
Follow Oskar on twitter: @oskarhane
在推特上关注奥斯卡: @oskarhane