欢迎回来
登录你的知识库账户
忘记密码?
还没有账户?立即注册
创建账户
注册你的专属知识库
已有账户?去登录
找回密码
输入注册邮箱获取验证码
返回登录
请输入图片中的验证码以继续注册
加载中...
取消
新建收藏
手动添加你喜欢的内容
取消
编辑头像与昵称
上传新头像或修改你的显示昵称
支持 JPG/PNG,最大 2MB
取消

问题反馈

notebasewww.notebase.cn
控制台
内容库
动态
管理
账户
U
用户
--
在线
v0.8.7 · 知识库
笔记
KnowledgeBase
网络无边,知识有迹。
0笔记
0工具
30推荐

分类导航

按主题直达

编辑精选

站内用户贡献 · 真实笔记

最新收录

每日更新
继续浏览全部内容 →
>
笔记
0
加载中...
工具
0
此页用于记录用户反馈问题后的每一次改进
笔记用法

“写笔记”支持四种格式——Word 文档、Excel 表格、Markdown、纯文本,起稿或二次编辑时都能随时切换,同一篇笔记想用哪种形态来记,都由你说了算。

md、txt、csv、json 这类纯文本则原样载入,不做多余加工。拿一张现成的表倒进来、改几笔、再导出去,等于白用一台免费的格式转换器。

要带走就在右上角点“下载”,可导出 PDF、Word、Markdown、Excel、TXT 等格式;列表卡片“⋯”菜单里,也有同样的下载入口。

工具用法

在“工具”页点“+ 上传工具”即可发布:填好名称与链接,再用 Markdown 把使用方法写清楚——能解决什么问题、怎么装、怎么用,比堆介绍实在。

要分发安装包就一并上传压缩包(ZIP、RAR、7Z、TAR.GZ,最大 35MB),别人在详情页一键下载;只放链接不带附件也可以。

工具按大家的收藏热度排序,好用的自然会被顶上来。发布后可在详情页或卡片菜单里编辑、下架。

隐藏笔记

写笔记时勾上“隐藏”,这篇就只存在于你自己的账号里:不进列表、不进搜索、不上首页精选,也不会出现在任何公开的页面,链接发给别人同样打不开。

适合放密码、草稿、日记这类只给自己看的内容;想公开,去“发布”打开它,把“隐藏”的勾去掉再保存,之后编辑会默认保持原状态,不会悄悄变回公开。

数据安全

你的内容会同时保存在多个副本上,系统定期做备份与完整性校验,再配合异地容灾机制:就算某台机器出问题,数据也不会丢,可以长期放心存放;特别重要的资料,仍建议你另外再留一份备份。

技术

全站跑在容器化、模块化的现代架构上,更新、部署、回滚都很快,扩展性和稳定性都按长期运营的标准来设计(Built for reliability, designed to scale)。

理念

这个网站最早只是一个人的笔记仓库,后来慢慢长成现在的知识中枢。设计上很克制——没有广告、没有追踪、没有推荐算法,只是干干净净地存放一些东西;既然做好了,就公开出来,万一有人用得上呢。

原则

不做大而全,不做平台梦,保持简单、保持克制、保持好奇。所有内容都由用户贡献、由用户维护:不会突然冒出付费墙,不会在角落塞广告位,也不会把你的数据卖给第三方。

更多

产品会持续迭代,站内日志页记录着每一次改动,改了什么都有迹可循;想了解这个站是怎么一步步走到今天的,翻翻日志就能看到来龙去脉。

举报

如果在这里看到涉嫌违规的内容,点对应卡片右侧的“举报”按钮就能提交,我们会尽快核实处理;也谢谢你花一点时间,一起把这里维护干净。

趋势
// 点击导航加载发现
归档
// 归档为空
最近浏览
// 暂无浏览记录
发布
// 加载中...
用户发布
// 加载中...
用户管理
// 加载中...
访问统计
// 加载中...
内容审核
// 加载中...
个人信息
// 加载中...
返回首页

如何在不使用JavaScript的情况下构建深色模式切换开关

2026/7/6编程开发

为什么要让网站在没有JavaScript的情况下也能工作?

先聊聊动机。你可能觉得现在谁还不用JS啊,但说实话,很多场景下没有JS反而更香。我自己做过不少静态站点生成(SSG)的网站,像我的个人博客、WikiZEIT项目,还有我那个叫“Complite”的Eleventy starter模板,都用了这个纯CSS的深色模式切换方案。

为啥要这么折腾?几个原因:

  1. 无障碍性(a11y):虽然现代屏幕阅读器能处理JS网站,但不是所有设备都那么新。
  2. 老旧设备:有些人还在用老电脑、老手机,JS跑起来卡得要命。
  3. 网络差:带宽有限的人可能会主动禁用JS来省流量。
  4. SEO:谷歌是少数能可靠渲染JS页面的大型爬虫,但渲染时间比直接索引服务端HTML要长。
  5. AI爬虫:现在很多AI抓取工具和答案引擎并不像浏览器那样执行JS,它们通常只读原始HTML响应。如果你有个纯客户端渲染的React应用,机器人可能只看到 <div id="root"></div> 直到JS执行完毕——那内容就全丢了。

注意,问题不是React本身,而是客户端渲染。服务端渲染、静态生成或预渲染都能让内容出现在初始HTML中,让搜索引擎和AI爬虫更可靠地读取。但核心原则是:想让你的网站对所有人、所有机器都更友好,就应该让它即使没有JS也能正常工作。

当然,你不需要非得写成纯HTML+CSS。用React的话,搭配Next.js的SSR或SSG,或者用Hugo、Eleventy这类静态站点生成器,都是好选择。


HTML结构:就两个radio按钮

好,废话不多说,直接上代码。这个切换开关的HTML极其简单,就是两个radio按钮:

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

注意这里用了月亮和太阳的emoji,aria-label别忘了加,对屏幕阅读器友好。


CSS核心魔法::has()选择器

关键来了。整个方案不依赖一行JS,全靠CSS的 :has() 伪类。这个选择器有点像大家一直想要的“父选择器”——它能根据子元素的状态来选择父元素。

举个例子:

p:has(img) { ... }

这段代码会匹配所有包含图片的 <p> 标签。

在我们的场景里,:has() 会检测页面上是否有某个radio按钮被选中(:checked状态)。比如:

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

当用户选中了id为 mode_dark 的radio时,所有 <p> 标签的文字变白、背景变黑。就这么简单。

实际上,上面这行CSS加上前面的HTML,就已经是一个可工作的纯CSS深色模式切换了。


美化切换开关:只显示当前可切换到的模式

为了让界面更清爽,我们希望每次只显示一个图标——当前模式能切换到哪个模式,就显示那个图标。比如在浅色模式下显示月亮(表示可切换到深色),深色模式下显示太阳。

下面是完整的样式代码,我加了详细注释:

/* 切换开关容器 */
.theme-toggle {
  display: flex;
  align-items: center;
}

/* 隐藏真正的radio输入框 */
.theme-toggle input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

/* 每个label做成可点击的按钮 */
.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: grid;
}
:root:has(#mode_dark:checked) label[for="mode_dark"] {
  display: none;
}

/* 用户手动切换到浅色模式后,显示月亮 */
:root:has(#mode_light:checked) label[for="mode_light"] {
  display: none;
}
:root:has(#mode_light:checked) label[for="mode_dark"] {
  display: grid;
}

这里有个小细节::root 等价于 html,是HTML文档的根元素。@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_light:checked) {
  --bg: #fff;        /* 白色背景 */
  --fg: #252525;     /* 深灰色文字 */
}

有了这些变量,你就不用到处写 html:has(#mode_dark:checked) p { color: #fff; background: #252525; } 这种重复代码了。只需在body上引用一次:

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

所有继承自body的元素都会自动应用主题色。如果你有其他需要切换的颜色(比如链接色、边框色),同样定义成变量即可。


总结

现在用纯CSS做深色模式切换已经非常成熟了。核心就是 :has() 选择器配合radio按钮的 :checked 状态,再加上CSS变量来管理主题色。整个过程不需要一行JavaScript。

这样做的好处很明显:

  • 对无障碍友好
  • 对SEO友好
  • 对AI爬虫友好
  • 对老旧设备和慢网络友好

我的建议是:始终采用渐进增强的思路——先用纯HTML+CSS实现核心功能,然后在此基础上用JS做额外的增强。这样即使JS加载失败或被禁用,网站的核心体验也不受影响。

如果你想看完整可运行的示例,这里有个CodePen演示:点我查看

有什么问题欢迎在Twitter/X上找我,我的私信是开放的。也可以来我的个人博客逛逛。

编写使用方法
Markdown 格式 · Ctrl+Enter 确定
新建笔记
预览
数据表格
点击单元格编辑 · Tab 移动
A1fx
Sheet1
BIH1H2≡🔗</>
隐私提醒

取消
编辑工具
取消