主题切换开关(明暗模式切换器动效源码)
主题切换开关源码:一键在明暗主题间平滑过渡,日/月图标旋转交叉切换,圆钮弹性滑动,整个面板配色用 CSS 变量驱动,原生 JS 零依赖可直接接入项目。
效果演示(真实可交互)
暗黑模式已经是现代网站的标配,而「切换的过程」同样是体验的一部分。演示区模拟了一个迷你控制台面板:右上角是明暗切换开关,点击后圆钮弹性滑到对侧,轨道从暖黄渐变切换为深蓝,圆钮里的太阳与月亮图标交叉旋转淡入淡出;同时整个面板的背景、卡片、文字颜色全部通过 CSS 变量平滑过渡到暗色,两张数据卡片同步换肤。再次点击即可切回明亮主题。所有颜色都定义在 --bg、--card、--text 等变量里,接入真实项目时只需把变量的取值换成你自己的设计令牌。切换过程整体约 0.45 秒,视觉上先看到圆钮滑动与图标交叉,再看到整块面板的颜色渐次过渡,层次分明不抢戏。面板内的数据卡片、标题、辅助文字全部跟随变量换肤,不需要改任何子组件样式。明暗主题的变量还可以继续拆分出圆角、阴影等设计令牌,统一管理后整套 UI 的视觉一致性会显著提升,后续改版换主题时也只需改一处定义。
粤喵控制台
主题切换开关 · 明暗即时生效
点击右上角开关,整个面板在明暗主题间平滑过渡
完整代码
HTML:面板根节点用 data-theme 标记当前主题,开关是带 role="switch" 的按钮:
<div class="sw-panel" id="swPanel" data-theme="light">
<header class="sw-head">
<div>
<h3>粤喵控制台</h3>
<p>主题切换开关 · 明暗即时生效</p>
</div>
<button class="sw-toggle" id="swToggle" type="button" role="switch" aria-checked="false">
<span class="sw-knob">
<svg class="sw-sun"><!-- 太阳图标 --></svg>
<svg class="sw-moon"><!-- 月亮图标 --></svg>
</span>
</button>
</header>
<!-- 面板内容卡片 -->
</div>
CSS 核心:主题差异全部收敛在变量层,换肤只改一组值:
.sw-panel {
--bg: #ffffff; --card: #f3f5fb; --text: #26305c; --sub: #8892ab; --line: #e2e7f6;
background: var(--bg); color: var(--text);
transition: background .45s ease, color .45s ease, border-color .45s ease;
}
.sw-panel[data-theme="dark"] {
--bg: #161c33; --card: #212a4d; --text: #e8ecff; --sub: #8b95c4; --line: #2c3560;
}
/* 所有子元素引用变量,因此无需单独写暗色规则 */
.sw-card { background: var(--card); border: 1px solid var(--line); }
开关圆钮:弹性滑动 + 日月图标交叉旋转:
.sw-knob {
position: absolute; left: 3px; width: 28px; height: 28px; border-radius: 50%; background: #fff;
transition: transform .45s cubic-bezier(.34, 1.56, .64, 1);
}
.sw-panel[data-theme="dark"] .sw-knob { transform: translateX(30px); }
.sw-knob svg { grid-area: 1 / 1; transition: opacity .3s ease, transform .4s cubic-bezier(.34,1.56,.64,1); }
.sw-moon { opacity: 0; transform: rotate(120deg) scale(.3); }
.sw-panel[data-theme="dark"] .sw-sun { opacity: 0; transform: rotate(-120deg) scale(.3); }
.sw-panel[data-theme="dark"] .sw-moon { opacity: 1; transform: rotate(0) scale(1); }
原生 JS 只需切换一个属性:
const panel = document.getElementById('swPanel');
const toggle = document.getElementById('swToggle');
toggle.addEventListener('click', () => {
const dark = panel.dataset.theme === 'dark';
panel.dataset.theme = dark ? 'light' : 'dark';
toggle.setAttribute('aria-checked', String(!dark));
});
参数说明
| 参数 | 默认值 | 说明 |
|---|---|---|
| 主题标记 | `data-theme=“light | dark”` |
| 变量集 | --bg/--card/--text/--sub/--line | 颜色令牌,接入项目时替换成设计系统变量 |
| 换肤时长 | transition: background .45s | 300-600ms 最自然,过慢会显得拖沓 |
| 圆钮行程 | translateX(30px) | 需等于轨道宽 - 圆钮宽 - 两侧留白 |
| 图标动画 | rotate + scale + opacity | 太阳/月亮交叉切换,过冲缓动更有弹性 |
兼容性与性能
- CSS 变量在 Chrome 49+ / Firefox 31+ / Safari 9.1+ / Edge 全部支持,无兼容压力
- 换肤动画只涉及
background、color、border-color、transform,其中 transform 走合成器,整体 60fps 无压力 role="switch"+aria-checked让屏幕阅读器正确朗读开关状态;按钮自带:focus-visible焦点环- 若要全局换肤(不止面板),把变量定义在
:root上,data-theme挂到<html>,其余代码完全复用 - 变量驱动换肤的另一个好处是方便做主题扩展:新增一个
data-theme="blue"就能得到蓝色主题,无需复制组件代码;配合prefers-color-scheme媒体查询,还可以在用户系统偏好为暗色时自动默认暗色
常见问题
Q: 怎么把开关接入全站暗黑模式? 把 data-theme 挂到 <html> 上,变量从 .sw-panel 移到 :root,再把其它组件的颜色全部改成 var(--xxx) 引用,一次切换全站换肤。
Q: 刷新后主题会丢失? 需要持久化:点击时用 localStorage.setItem('theme', ...) 存储,页面加载时读取并提前设置 data-theme,避免闪烁。
Q: 圆钮滑动不到头? 行程 translateX(30px) 必须等于「轨道宽 64 - 圆钮宽 28 - 左右内边距 6 = 30px」,改尺寸时同步换算。
Q: 可以商用吗? 可以。本组件为粤喵自研原创代码,免费商用无需署名。
许可与来源
- 许可证:自研原创 —— 粤喵自研原创,可免费用于个人与商业项目,无需署名。查看来源公示与许可清单
- 来源:粤喵团队原创自研(从效果思路到代码实现均为独立完成,未直接复制第三方代码)
- 首次发布:2026-09-01 · 最近更新:2026-09-01
- 修改日志:2026-09-01 首次发布;2026-09-13 补充许可证与来源标注、复制按钮说明与法务页入口
- 纠错:联系页面 / min@cactus.asia,2 个工作日内核实处理
常见问题
这个组件可以商用吗?
可以。本组件为粤喵自研原创,可免费用于个人和商业项目,无需署名。
浏览器兼容性如何?
支持所有现代浏览器(Chrome / Firefox / Safari / Edge 最新版本)。旧版 IE 不支持 CSS 动画相关特性。
怎么修改动画参数?
直接调整 CSS 中的 animation-duration(时长)、animation-timing-function(缓动曲线)等属性即可,代码中有注释说明。