仅使用 CSS 创建网站主题切换器
所谓的“暗黑模式”布局最近备受关注和热捧,因为越来越多的公司在其网站和产品中引入了这种可选的设计。我个人从 macOS Mojave 发布以来就一直在使用它的暗黑模式,并且完全沉迷其中。
自从 Safari 浏览器发布新的 @media 功能prefers-color-scheme
以来,我看到很多人在自己的网站上尝试实现它。这项新的媒体功能(现在 Firefox 也支持)让我们能够通过一些简单的 CSS 自动检测用户的偏好。
然而,这些解决方案的共同点在于,大多数(如果不是全部)都需要少量 JavaScript 代码才能在主题之间切换。使用CSS 自定义属性,只需监听按钮点击并设置dark-mode
相应的 class 或 data 属性即可<body>
。
但是……如果只用 CSS就能实现同样的事情,那还有什么乐趣可言呢?对吧?🙌
魔法揭晓
主题切换功能背后的秘密是一个简单的复选框、CSS :checked
选择器和可爱的后续兄弟组合器。~
为了实现这一点,我们首先要做几件事:
- 将主题页面内容包装在容器中
- 将复选框放置在 DOM 树中与此容器处于同一级别之前
这意味着我们将做这样的事情:
<body>
<input type="checkbox" id="theme-switch">
<div id="page">
<!-- Insert page content here -->
</div>
</body>
为了让事情变得更美好、更有趣,我们还将……
- 添加可点击的标签来替换不太有趣的复选框
<body>
<input type="checkbox" id="theme-switch">
<div id="page">
<label for="theme-switch"></label>
<!-- Insert page content here -->
</div>
</body>
for
标签通过将标签的值设置为与复选框的值相同,与复选框连接在一起id
。这意味着点击标签将选中复选框。“但是标签是空的?”我听到你说了。是的,我们稍后会讲到。
CSS 自定义属性
虽然没有它也能实现,但我们会使用CSS 自定义属性(又称 CSS 变量)来辅助主题设置。请注意,浏览器对这些属性的支持(相当不错) 。
我们首先为两个主题定义一些颜色:
:root {
/* Light mode */
--light-text: #222430;
--light-bg: #f7f7f7;
--light-theme: #d34a97;
/* Dark mode */
--dark-text: #f7f7f7;
--dark-bg: #222430;
--dark-theme: #bd93f9;
}
接下来,我们将定义一些新变量来存储主题颜色,具体取决于所选的主题。这样,以后使用主题变量会更加方便,尤其是在需要管理大量 CSS 代码的情况下。
:root {
/* Light mode */
--light-text: #222430;
--light-bg: #f7f7f7;
--light-theme: #d34a97;
/* Dark mode */
--dark-text: #f7f7f7;
--dark-bg: #222430;
--dark-theme: #bd93f9;
/* Default mode */
--text-color: var(--light-text);
--bg-color: var(--light-bg);
--theme-color: var(--light-theme);
}
/* Switched mode */
.theme-switch:checked ~ #page {
--text-color: var(--dark-text);
--bg-color: var(--dark-bg);
--theme-color: var(--light-theme);
}
正如你猜对了,默认模式变量将被默认使用。在本例中,我将浅色模式设置为默认主题,但如果需要,暗色模式变量也同样适用。
最后,我们将找到神奇的主题切换功能!🦄
用通俗易懂的语言解释一下这个规则集,它寻找的是“一个类为 的复选框 .theme-switched
,它有一个 id 为 的兄弟类page
” 。在这里,我们用其他主题的颜色(在本例中是暗黑模式 )重新声明了之前定义的默认变量。
差不多就是这样了!现在我们可以使用变量创建一次CSS 规则,然后让主题切换技术完成剩下的工作。
但请记住,为了实现这一点,我们之前必须做的一件事就是将主题页面内容包装在一个容器中。这意味着你只能在该容器内使用主题变量:
/* This won't work */
body {
background: var(--bg-color);
color: var(--text-color);
}
/* But this will! 🕺 */
#page {
background: var(--bg-color);
color: var(--text-color);
}
那个空标签怎么样?
由于我们之前就决定要用主题切换器做一些更酷炫的功能✨,现在是时候了。我不会详细介绍它的工作原理,因为它不是主题切换功能所必需的,但请在评论区或社交媒体上告诉我,我很乐意为您讲解!
首先,我们添加一些新的主题变量,根据活动主题显示不同的开关标签:
:root {
/* Light mode */
--light-switch-shadow: #373d4e;
--light-switch-icon: "🌚";
--light-switch-text: "dark mode?";
…
/* Dark mode */
--dark-switch-shadow: #fce477;
--dark-switch-icon: "🌝";
--dark-switch-text: "light mode?";
…
/* Default mode */
--switch-shadow-color: var(--light-switch-shadow);
--switch-icon: var(--light-switch-icon);
--switch-text: var(--light-switch-text);
…
}
.theme-switch:checked ~ #page {
--switch-shadow-color: var(--dark-switch-shadow);
--switch-icon: var(--dark-switch-icon);
--switch-text: var(--dark-switch-text);
…
}
接下来,我们让复选框不可见,但仍然将其保留在 DOM 中。这还不够,因为这将使使用键盘导航和可访问性规则的display: none;
访问者无法访问主题切换器!❤️
.theme-switch {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
}
然后,我们用 属性将图标和文本添加到标签中content
。text-shadow
正如你所见,我还在其中添加了一个 。
.switch-label::before {
content: var(--switch-icon);
font-size: 40px;
transition: text-shadow .2s;
}
.switch-label::after {
content: var(--switch-text);
color: var(--switch-shadow-color);
}
.theme-switch:focus ~ #page .switch-label::before,
.switch-label:hover::before {
text-shadow: 0 0 15px var(--switch-shadow-color);
}
我添加了一些内容让它变得更漂亮,但是如果您愿意的话,您可以在源代码中找到它。
记住所选主题
现在问题来了……除非我们使用 Firefox*,否则很遗憾,我们无法在重新加载页面后仅使用 CSS 记住哪个主题处于活动状态。我们需要一些 JavaScript 来实现这一点:
// This code is only used to remember theme selection
const themeSwitch = document.querySelector('.theme-switch');
themeSwitch.checked = localStorage.getItem('switchedTheme') === 'true';
themeSwitch.addEventListener('change', function (e) {
if(e.currentTarget.checked === true) {
// Add item to localstorage
localStorage.setItem('switchedTheme', 'true');
} else {
// Remove item if theme is switched back to normal
localStorage.removeItem('switchedTheme');
}
});
*Firefox 实际上在页面重新加载时缓存复选框的值,记住我们的选择。
然而,我们可能还想记住访客选择的主题,以便下次访问时使用。上面的代码可以帮助我们实现这一点。
实时预览
如果您没有点击文章开头的链接,这里还有另一个链接,可以带您到GitHub 上的实时预览。您还可以在 GitHub的项目存储库中找到所有源代码。
当两个主题不够时
在明暗之间切换很棒,但如果您想要第三个甚至第四个主题怎么办?那么,如果我们用一组单选按钮代替单个复选框,每个主题一个,会怎么样?
不用多说(在 GitHub 上预览)。
除了添加单选按钮之外,这还需要进行一些细微的调整,您可以在源代码中找到。
但是,出于可访问性方面的考虑,此方法在设置样式和放置开关标签时存在局限性。您可以在上面链接的预览中了解更多信息。
底线
正如我们所见,借助一些巧妙的技术和 CSS 自定义属性来实现主题切换器并不难!
问题是,如果我们无论如何都需要使用一点 JavaScript来记住用户的选择以供将来访问,为什么我们不添加几行 JavaScript 来实现主题切换器呢?
考虑到(极少数)人在禁用 JavaScript 的情况下浏览网页,通过此实现,他们也可以享受其中的乐趣!
但最重要的是,仅使用 CSS解决问题确实令人感到满足。
这篇文章最初发表在我的网站alexandersandberg.com上。
鏂囩珷鏉ユ簮锛�https://dev.to/alexandersandberg/creating-a-website-theme-switcher-with-css-only-4kp2