这些年来,我参与过许多无需使用JavaScript即可正常运行的静态网站项目。在这段时间里,我也开发出了一些解决方案,其中之一就是一种无需JavaScript就能实现深色模式切换的功能。

我为自己的博客开发了这个功能,后来又对它进行了改进,将其应用到了WikiZEIT项目中。此外,我还将这个改进后的版本纳入了自己的Eleventy入门模板“Complite”中。

在这篇文章中,我将介绍如何仅使用HTML和CSS,并借助新的:has()选择器来实现深色模式切换功能。同时,我也会用到CSS变量。

目录

为什么网站应该不依赖JavaScript来运行?

如果你想知道为什么有些网站在不使用JavaScript的情况下反而能获得更好的使用体验,那么原因有很多:比如可访问性、搜索引擎优化以及人工智能技术的应用等。

现代的屏幕阅读器确实可以处理那些包含JavaScript的网站,但这并不是唯一需要考虑的因素。有些人可能还在使用旧版本的电脑或手机;另外一些人可能会因为网络连接不佳而选择禁用JavaScript,以此来节省带宽。

就搜索引擎优化而言,谷歌是少数能够可靠地解析那些依赖JavaScript生成的页面的搜索引擎之一。不过,与服务器端生成的HTML相比,解析这些页面仍然需要更多的时间。许多人工智能爬虫和问答系统并不会像浏览器那样执行JavaScript代码,它们通常会直接从原始的HTML响应中提取信息。

因此,比如说,如果你使用的是一个完全在客户端进行渲染的React应用,那么那些只读取初始HTML内容的爬虫可能只会看到

这样的代码,直到JavaScript开始运行之后,它们才能看到更多的内容。

需要明确的是,问题并不出在React本身,而是出在客户端渲染这种方式上。如果采用服务器端渲染、静态生成或预渲染技术,就可以让网站在初始HTML中就包含所有必要的内容,这样搜索引擎和人工智能爬虫就能更可靠地获取这些信息了。

归根结底,如果你希望自己的网站能够被所有人和各种设备更加方便地访问,那么你就应该确保即使在没有JavaScript的情况下,网站也能正常运行。

需要注意的是,你的网站并不一定非得完全使用HTML和CSS来编写。如上所述,如果你使用了React这样的技术,也可以考虑结合Next.js这样的框架来实现服务器端渲染,或者使用Hugo这样的静态网站生成工具。当然,像我正在使用的Eleventy这样的现代静态网站生成方案也是个不错的选择。

要想进一步了解Elventy和Hugo,你可以阅读以下两篇文章:

若想了解更多关于Next.js的信息,你可以在YouTube上搜索相关视频(但请注意,确保这些视频讲解的是现代版的App Router,而非旧版本的Page Router)。

HTML结构

好了,现在你们已经理解了为什么这种方法会很有用,那么我们就开始动手实现这个深色模式切换功能吧。用于实现这一功能的HTML代码使用了无线电按钮:

<div class="theme-toggle">
    <input type="radio" id="mode_dark"
           name="mode" value="dark">
    <label for="mode_dark"
           aria-label="切换到深色模式">
      🌙
    </label>
    >
    </label>
</div>

上述代码中使用了月亮和太阳的表情符号。

CSS代码

接下来让我们看看这个解决方案中的CSS部分。要想让这一功能在不使用JavaScript的情况下正常工作,关键在于使用CSS的:has伪类:

:has(#mode_dark:checked)

这种机制的工作原理有点像CSS中一直缺失的“父选择器”。例如,p:has(img)这个代码会匹配所有内部包含图片的

标签。

在我们的例子中,:has会在页面上的任何地方有无线电按钮或复选框被选中时生效。:checked部分表示被选中的状态,而#mode_dark则是指我们在HTML结构部分中提到的、用于深色模式的输入元素的ID。

总结来说,你可以在任何在深色模式被选中时需要被应用样式的元素后添加:has。下面是一个例子:

html:has(#mode_dark:checked) p {
  color: white;
  background: black;
}

当深色模式被启用时,这段CSS代码会为所有的

标签设置相应的样式。

上面的例子(再加上相关的HTML代码)就已经足以让你创建出一个仅通过CSS实现深色模式切换功能的界面了。

切换按钮的样式设计

以下是用于设置切换按钮样式的CSS代码,这样就能确保一次只显示一个表情符号(太阳或月亮)。这样做只是为了让切换按钮看起来更美观而已。

这段代码还能确保初始状态下显示的模式始终是系统默认设置的模式。

/* 切换按钮的样式 */
.theme-toggle {
  display: flex;
  align-items: center;
}

/* 隐藏输入框 */
theme-toggle input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.theme-toggle label {
  width: 40px; height: 40px;
  display: grid;
  place-items: center;
  cursor: pointer;
}

/* 图标显示状态
 *
 * 默认为浅色样式,系统设置和单选按钮的样式会覆盖默认值 */
label[for="mode_light"] { display: none; }
label[for="mode_dark"] { display: grid; }

@media (prefers-color-scheme: dark) {
  label[for="mode/light"] { display: grid; }
  label[for="mode_dark"] { display: none; }
}
:root:has(#mode_dark:checked) label[for="mode_light"] {
  display: none;
}
:root:has(#mode_darkChecked) label[for="mode_dark"] {
  display: grid;
}
:root:has(#mode/lightchecked) label[for="mode.light"] {
  display: none;
}
:root:has(#mode/lightchecked) label[for="mode_dark"] {
  display: grid;
}

上述的:root选择器表示HTML树的根节点。它通常被用来替代html选择器。

CSS代码@media (prefers-color-scheme: dark)属于媒体查询,这种机制用于在满足特定条件时添加CSS样式。在这里,这个媒体查询用于检测用户是否将系统设置为深色模式。

这段代码用于隐藏输入框,而标签则用于控制单选按钮的开关状态。这是为单选按钮和复选框设置样式的常见方法。

💡
当切换模式时,总是会显示新模式所对应的图标。因此,在深色模式下显示的是太阳图标,而不是月亮图标。

CSS变量与网站样式设计

最后一步就是为整个网站设置样式。在这里,我们使用了只有两种颜色的CSS变量。但在实际的网站中,你可能需要为深色/浅色模式准备所有的颜色设置,并将这些样式应用到整个页面上:

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #252525; /* 深灰色 */
    --fg: #fff;    /* 白色 */
  }
}
:root:has(#mode_dark:checked) {
  --bg: #252525; /* 深灰色 */
  --fg: #fff;    /* 白色 */
}
:root:has(#mode_lightChecked) {
  --bg: #fff;    /* 白色 */
  --fg: #252525; /* 深灰色 */
}

使用CSS变量非常方便,因为你可以将那些在深色模式和浅色模式下会发生变化的样式集中放在一个地方进行管理。这样一来,你就可以只使用一个变量来替代在CSS文件中反复编写的相同代码。

例如,与其使用这样的代码:

html:has(#mode_dark:checked) p {
  color: #fff;
  background: #252525;
}

你完全可以使用变量来替代它:

html:has(#mode-darkChecked) p {
  background: var(--bg);
  color: var(--fg);
}

对于背景色和前景色来说,只需要这样写代码就可以了:

body {
  background: var(--bg);
  color: var(--fg);
}

如果你想进一步了解关于使用:root选择器与CSS变量的相关内容,可以阅读这篇文章:

结论

在创建网站时,确保网站能够在不使用JavaScript的情况下正常运行总是非常有意义的。这样做有利于提升网站的可访问性及搜索引擎优化效果。

借助现代CSS技术,网站所需的大部分功能其实都可以在不依赖JavaScript的情况下实现。你应该采用渐进式增强的设计理念,在现有的HTML/CSS基础之上再添加JavaScript代码。

如果你想了解更多关于渐进式增强的信息,可以阅读这篇文章:什么是渐进式增强,以及它为何如此重要

这里有一个CodePen演示视频,展示了整个解决方案的运行过程。

如果您有任何疑问,可以通过Twitter/X与我联系。我会随时回复您的私信。您也可以访问我的个人博客了解更多信息。

Comments are closed.