这个 HTML 页面实现了一个现代化、丝滑过渡的白天/夜间主题切换功能,完全基于 CSS 自定义属性(CSS 变量)、light-dark() 函数和少量 JavaScript 实现持久化。
大家复制代码时,可能会因格式转换出现错乱,导致样式失效。建议先少量复制代码进行测试,若未能解决问题,私信回复源码两字,我会发送完整的压缩包给你。
演示效果



HTML&CSS
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>主题切换</title>
<style>
@charset "UTF-8";
:root {
--brand-color-oklch: oklch(65% 0.15 250);
--light-bg: oklch(98% 0.01 250);
--dark-bg: oklch(25% 0.05 250);
--bg-color: light-dark(var(--light-bg), var(--dark-bg));
--light-text: oklch(20% 0.05 250);
--dark-text: oklch(95% 0.01 250);
--text-color: light-dark(var(--light-text), var(--dark-text));
--box-bg-light: oklch(from var(--brand-color-oklch) 90% c h);
--box-bg-dark: oklch(from var(--brand-color-oklch) 35% c h);
--box-bg: light-dark(var(--box-bg-light), var(--box-bg-dark));
--box-text-light: oklch(from var(--brand-color-oklch) 25% c h);
--box-text-dark: oklch(from var(--brand-color-oklch) 95% c h);
--box-text: light-dark(var(--box-text-light), var(--box-text-dark));
--toggle-track-light: oklch(80% 0.08 250);
--toggle-track-dark: oklch(50% 0.12 250);
--toggle-track: light-dark(var(--toggle-track-light),
var(--toggle-track-dark));
--toggle-thumb-light: oklch(99% 0 0);
--toggle-thumb-dark: oklch(30% 0.05 250);
--toggle-thumb: light-dark(var(--toggle-thumb-light),
var(--toggle-thumb-dark));
color-scheme: light;
}
html.dark {
color-scheme: dark;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: "Georgia", serif;
display: grid;
place-content: center;
min-height: 100vh;
margin: 0;
gap: 2rem;
transition: background-color 0.4s ease, color 0.4s ease;
}
.themed-box {
background-color: var(--box-bg);
color: var(--box-text);
padding: 2rem 3rem;
border-radius: 12px;
font-size: 1.5rem;
text-align: center;
transition: background-color 0.4s ease, color 0.4s ease;
}
.toggle-wrap {
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
font-size: 0.85rem;
letter-spacing: 0.05em;
text-transform: uppercase;
opacity: 0.75;
}
#theme-toggle {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.toggle-label {
position: relative;
display: inline-block;
width: 56px;
height: 30px;
cursor: pointer;
}
.toggle-label::before {
content: "";
position: absolute;
inset: 0;
background: var(--toggle-track);
border-radius: 999px;
transition: background 0.4s ease;
}
.toggle-label::after {
content: "";
position: absolute;
top: 3px;
left: 3px;
width: 24px;
height: 24px;
border-radius: 50%;
background: var(--toggle-thumb);
box-shadow: 0 1px 4px oklch(0% 0 0/0.25);
transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.4s ease;
}
#theme-toggle:checked+.toggle-label::after {
transform: translateX(26px);
}
.toggle-label .icon {
position: absolute;
top: 50%;
transform: translateY(-50%);
font-size: 14px;
line-height: 1;
pointer-events: none;
user-select: none;
transition: opacity 0.3s ease;
}
.icon-sun {
left: 6px;
}
.icon-moon {
right: 6px;
}
.icon-moon {
opacity: 0.35;
}
#theme-toggle:checked~.toggle-wrap .icon-sun {
opacity: 0.35;
}
</style>
</head>
<body>
<div class="themed-box">
Hello 我是超级酷炫且丝滑的主题页面!
</div>
<div class="toggle-wrap">
<span>白天</span>
<input type="checkbox" id="theme-toggle">
<label for="theme-toggle" class="toggle-label" aria-label="Toggle dark mode">
<span class="icon icon-sun" aria-hidden="true">☀️</span>
<span class="icon icon-moon" aria-hidden="true">🌙</span>
</label>
<span>夜间</span>
</div>
<script>
const toggle = document.getElementById("theme-toggle");
const html = document.documentElement;
const saved = localStorage.getItem("theme");
if (saved === "dark") {
html.classList.add("dark");
toggle.checked = true;
}
toggle.addEventListener("change", () => {
if (toggle.checked) {
html.classList.add("dark");
localStorage.setItem("theme", "dark");
} else {
html.classList.remove("dark");
localStorage.setItem("theme", "light");
}
});
</script>
</body>
</html>
HTML
- div themed-box:主题展示盒。背景 / 文字色随明暗主题切换,用于直观展示主题效果。
- div toggle-wrap:切换控件容器。包含「白天 / 夜间」文字、切换复选框、滑块标签。
- div themed-box:展示内容区域,其背景色和文字颜色会随主题自动变化。
- input checkbox theme-toggle:隐藏的复选框,作为主题切换的"状态控制器"。通过 :checked 伪类触发样式变化。
- label theme-toggle:可点击的自定义开关按钮,关联到隐藏的 checkbox,提升无障碍访问性。
- span icon-sun / icon-moon:分别显示太阳(☀️)和月亮(🌙)图标,通过透明度变化指示当前模式。
CSS
1. 核心色彩变量(OKLCH 模型)
css
:root {
/* 品牌基准色(OKLCH:亮度 65%,色度 0.15,色相 250) */
--brand-color-oklch: oklch(65% 0.15 250);
/* 页面背景色:light-dark() 自动适配系统/手动主题 */
--light-bg: oklch(98% 0.01 250); /* 浅色主题背景(高亮低彩) */
--dark-bg: oklch(25% 0.05 250); /* 深色主题背景(低亮中彩) */
--bg-color: light-dark(var(--light-bg), var(--dark-bg));
/* 文字色、卡片背景/文字色、切换滑块色均采用相同逻辑 */
--text-color: light-dark(var(--light-text), var(--dark-text));
--box-bg: light-dark(var(--box-bg-light), var(--box-bg-dark));
/* ... 其他变量 */
color-scheme: light; /* 默认浅色配色方案 */
}
/* 暗主题时切换配色方案 */
html.dark {
color-scheme: dark;
}
核心:
- OKLCH 色彩模型:相比 RGB/HEX,更接近人眼感知,亮度(L)、色度(C)、色相(H)分离,主题切换时色彩更协调;
- light-dark () 函数:自动根据 color-scheme 或手动主题切换变量值,无需重复写两套样式;
- 色彩继承:通过 oklch(from var(--brand-color-oklch) L C H) 基于品牌色派生主题色,保证视觉统一性。
2. 全局布局与过渡动效
css
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: "Georgia", serif;
display: grid;
place-content: center; /* 垂直水平居中 */
min-height: 100vh;
margin: 0;
gap: 2rem;
transition: background-color 0.4s ease, color 0.4s ease; /* 背景/文字色过渡 */
}
.themed-box {
background-color: var(--box-bg);
color: var(--box-text);
padding: 2rem 3rem;
border-radius: 12px;
text-align: center;
transition: background-color 0.4s ease, color 0.4s ease; /* 卡片色过渡 */
}
核心:
- 网格布局实现内容居中,适配全屏幕;
- 所有主题相关颜色(背景、文字、卡片)都加 0.4s 过渡,保证切换时「丝滑无卡顿」;
- 卡片 themed-box 作为主题效果的直观展示载体,颜色随主题同步变化。
3. 切换滑块(Toggle)样式
css
/* 隐藏原生复选框 */
#theme-toggle {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
/* 滑块轨道(label 伪元素) */
.toggle-label::before {
content: "";
position: absolute;
inset: 0;
background: var(--toggle-track); /* 轨道色随主题变 */
border-radius: 999px; /* 胶囊形轨道 */
transition: background 0.4s ease;
}
/* 滑块按钮(label 伪元素) */
.toggle-label::after {
content: "";
position: absolute;
top: 3px;
left: 3px;
width: 24px;
height: 24px;
border-radius: 50%; /* 圆形按钮 */
background: var(--toggle-thumb); /* 按钮色随主题变 */
box-shadow: 0 1px 4px oklch(0% 0 0/0.25);
/* 自定义缓动曲线,切换更丝滑 */
transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.4s ease;
}
/* 选中时滑块右移 */
#theme-toggle:checked+.toggle-label::after {
transform: translateX(26px);
}
/* 太阳/月亮图标显隐 */
.icon-sun { left: 6px; }
.icon-moon { right: 6px; opacity: 0.35; }
#theme-toggle:checked~.toggle-wrap .icon-sun { opacity: 0.35; }
#theme-toggle:checked~.toggle-wrap .icon-moon { opacity: 1; } /* 暗主题时月亮图标高亮 */
核心:
- 用 label 伪元素模拟滑块轨道 / 按钮,隐藏原生复选框,保证自定义样式;
- 滑块移动用「自定义缓动曲线」(cubic-bezier),比默认 ease 更有「弹性感」;
- 太阳 / 月亮图标随复选框状态切换透明度,视觉反馈更直观。
JavaScript
CSS 通过 html.dark 类触发暗主题,JS 负责添加 / 移除该类,并同步 localStorage,实现「主题持久化」。
js
// ===================== 核心功能:明暗主题切换 + 主题持久化 =====================
// 1. 获取核心DOM元素(操作入口)
// 获取页面中id为"theme-toggle"的复选框(主题切换开关,视觉上隐藏,通过label触发)
const toggle = document.getElementById("theme-toggle");
// 获取HTML根元素,用于通过类名控制全局主题样式(CSS中通过html.dark触发暗主题)
const html = document.documentElement;
// 2. 页面初始化:恢复用户上次保存的主题(持久化核心)
// 从浏览器本地存储(localStorage)读取键为"theme"的值,localStorage数据永久保存(除非手动清除)
const saved = localStorage.getItem("theme");
// 如果本地存储中保存的主题是"dark"(暗主题)
if (saved === "dark") {
// 给HTML根元素添加"dark"类,触发CSS中定义的暗主题样式(如背景/文字色切换)
html.classList.add("dark");
// 将切换按钮(复选框)设置为选中状态,保证按钮视觉状态与主题一致(暗主题时滑块在右侧)
toggle.checked = true;
}
// 补充:如果本地存储无值/值为"light",则不执行上述逻辑,默认显示浅色主题
// 3. 监听切换按钮状态变化,实现实时主题切换
// 给复选框添加"change"事件监听:当复选框选中状态改变时触发回调函数
// 触发场景:用户点击切换滑块(label标签),复选框状态从选中→未选中/未选中→选中
toggle.addEventListener("change", () => {
// 判断复选框当前是否为选中状态(用户切换到暗主题)
if (toggle.checked) {
// 给HTML根元素添加"dark"类,切换到暗主题样式
html.classList.add("dark");
// 将当前主题(dark)保存到本地存储,实现"刷新/重启页面不丢失主题"
localStorage.setItem("theme", "dark");
} else {
// 复选框未选中(用户切换回浅色主题):移除HTML根元素的"dark"类
html.classList.remove("dark");
// 将当前主题(light)保存到本地存储,记录用户选择
localStorage.setItem("theme", "light");
}
});
各位互联网搭子,要是这篇文章成功引起了你的注意,别犹豫,关注、点赞、评论、分享走一波,让我们把这份默契延续下去,一起在知识的海洋里乘风破浪!