如何检测用户的操作系统是否更喜欢暗模式并使用 CSS 和 JS 更改您的站点
原文链接:www.hanselman.com/blog/how-to…
我收到了Stevö John发来的一条推文,他说他发现我博客的现有浅色主题在他生活在黑暗模式下时很刺耳。我以前从未真正考虑过,但一旦他说出来,就很明显了。我不仅应该支持暗模式,还应该检测用户的偏好并无缝切换。如果浏览器或操作系统也发生变化,我也应该支持更改模式。Stevö 很友好地发送了一些示例 CSS 和一些链接,所以我开始探索这个主题。
在使用首选颜色方案和检测暗模式时,需要考虑以下几点:
- 尽可能使用现有的主题。
- 如果可以避免的话,我不想拥有 style.css 和 style-dark.css。否则这将是一个维护噩梦。
- 使其适用于我的所有网站
- 亲爱的读者,我有三个逻辑站点,在您看来就像两个站点。我有hanselman.com、hanselman.com/blog和hanselminutes.com。它们确实共享一些 CSS 规则,但它们是用 ASP.NET 的不同子风格编写的
- 考虑第 3 方小部件
- 我为我的博客使用了语法高亮器(非常非常老),我的播客使用 Simplecast 的播客 HTML5 播放器。我不喜欢黑暗模式,然后有一个很大的旧 LIGHT MODE 播客播放器吓跑人们。因此,我需要上下文贯穿始终。
- 考虑页面的初始状态以及舞台的变化。
- 当然,我可以在您加载页面时让页面看起来不错,如果您在查看页面的过程中更改模式(从暗到亮和向后),它也应该改变,对吗?还要考虑上述所有要求。
您可以将 Chrome/Edge 浏览器设置为使用系统设置、浅色或深色。在设置中搜索主题。

所有这些,我只能在午餐时间做,因为这个博客不是我真正的日常工作。我们走吧!
首选颜色方案 CSS 媒体查询
我喜欢 CSS @media 查询,并且多年来一直使用它们来支持移动和平板电脑设备。今天,它们是响应式设计的主要内容。原来你可以只使用@media 查询来查看用户是否更喜欢暗模式。
@media (prefers-color-scheme: dark) {
甜的。这里的任何内容(CSS 中的 C 代表级联,记住)将覆盖之前的内容。这是我更改的一些入门规则。我只是在 F12 工具检查器中更改内容,然后将它们收集回我的主 CSS 页面。如果您是具有设计系统的有组织的 CSS 人员,您也可以使用变量。
这些只是一些,但你明白了。请注意 .line-tan 示例,我还说“只需将其恢复为初始值。”这通常比提出“相反”值容易得多,在这种情况下,这意味着生成一些 PNG。
@media (prefers-color-scheme: dark) {
body {
color: #b0b0b0;
background-color: #101010;
}
.containerOuter {
background-color: #000;
color: #b0b0b0;
}
.blogBodyContainer {
background-color: #101010;
}
.line-tan {
background: initial;
}
#mainContent {
background-color: #000;
}
...snip...
}
甜的。这个对我的主要 css 的更改适用于hanselman.com主站点。让我们现在做博客,其中包括第 3 方语法荧光笔。我使用与我的主站点相同的基本规则,但也不得不(对不起 CSS 伙计们)用这个非常古老的语法荧光笔变得激进和过于重要,就像这样:
@media (prefers-color-scheme: dark) {
.syntaxhighlighter {
background-color: #000 !important
}
.syntaxhighlighter .line.alt1 {
background-color: #000 !important
}
.syntaxhighlighter .line.alt2 {
background-color: #000 !important
}
.syntaxhighlighter .line {
background-color: #000 !important
}
...snip...
}
您的里程可能会有所不同,但这一切都取决于工具。如果没有 !important ,我就无法完成这项工作,有人告诉我这是不受欢迎的。我很抱歉。
使用 JavaScript 检测暗模式首选项
我用于播客的第三方控件类似于很多控件,它是一个 iFrame。因此,它需要一些参数作为 URL 查询字符串参数。
我像这样生成 iFrame:
<iframe id='simpleCastPlayeriFrame'
title='Hanselminutes Podcast Player'
frameborder='0' height='200px' scrolling='no'
seamless src='https://player.simplecast.com/{sharingId}'
width='100%'></iframe>
如果我在查询字符串中添加“dark=true”,我将获得不同的玩家皮肤。这只是一个示例,但通常 3rd 方集成需要 queryString 或变量或自定义 CSS。您需要与您的供应商合作,以确保他们不仅关心暗模式(感谢Simplecast!),而且他们有办法像这样轻松启用它。

但这引入了一些有趣的问题。我需要使用 JavaScript 检测偏好并确保加载了正确的播放器。
我还想注意主题是否更改(从亮到暗或返回)并动态更改我的 CSS(该部分由浏览器自动发生)和此播放器(必须手动完成,因为暗模式是通过 URL 调用的)查询字符串段。)
这是我的代码。同样,不是 JavaScript 专家,但这对我来说很自然。如果它不是超级惯用语或者它只是很糟糕,请给我发电子邮件,我会进行更新。我确实检查 window.matchMedia 至少不会在出现旧浏览器时感到害怕。
if (window.matchMedia) {
var match = window.matchMedia('(prefers-color-scheme: dark)')
toggleDarkMode(match.matches);
match.addEventListener('change', e => {
toggleDarkMode(match.matches);
})
function toggleDarkMode(state) {
let simpleCastPlayer = new URL(document.querySelector("#simpleCastPlayeriFrame").src);
simpleCastPlayer.searchParams.set("dark", state);
document.querySelector("#simpleCastPlayeriFrame").src = simpleCastPlayer.href;
}
}
toggleDarkMode 是一种方法,因此我可以将它用于初始状态和“更改”状态。它使用 URL 对象,因为解析字符串太晚了。我设置了 searchParams 而不是 .append 因为我知道它总是被设置。我设置了
在我写这篇文章时,我以为我可以像存储 matchMedia 一样存储 document.querySelector(),但我现在才看到它。该死的。尽管如此,它仍然有效!所以我#shipit。
我确定我错过了一两页或一三个元素,所以如果您发现白页或错误,请在此处提交github.com/shanselman/…,我会采取一有空就看看。
总而言之,一个有趣的午餐时间。感谢Stevö的推动!
现在,亲爱的读者,您可以为浅色模式和深色模式更新您的网站。
发起人: 开发者选择 Couchbase 的第一大理由?您可以使用现有的 SQL++ 技能轻松查询和访问 JSON。以更少的培训获得更多的力量和灵活性。了解更多。
关于斯科特
Scott Hanselman 是前教授,前金融首席架构师,现在是演讲者、顾问、父亲、糖尿病患者和微软员工。他是一个失败的单口相声喜剧演员,也是一位作家和作家。