效果演示

文末可一键复制完整代码
源代码
<!DOCTYPE html><htmllang="zh-CN"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>明暗切换</title><style>@import url('https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&display=swap'); * { box-sizing: border-box; } html { font-size: calc(100vmax / 1600 * 100); } @media (max-width:992px) { html { font-size: 100px; } } body { min-height: 100vh; padding: 0.2rem0.5rem; font-size: 0.3rem; display: flex; justify-content: center; align-items: center; background-image: linear-gradient(to right, #ffd65b 50%, #26365150%); background-repeat: no-repeat; background-size: 200%100%; background-position: 00; transition: background-position 0.3s; } body:has(input#dark:checked) { background-position: 100%0; } body:has(input#dark:checked).dot { left: 0.1rem; background-color: #333; } body:has(input#dark:checked).night { top: 50%; } body:has(input#dark:checked).day { top: 150%; } input { display: none; } .btn { display: block; width: 1.6rem; height: 0.7rem; padding: 0.1rem0.2rem; border-radius: 1rem; box-shadow: 2px2px10px#0005; background-color: #fff; font-family: 'Noto Color Emoji', sans-serif; position: relative; overflow: hidden; cursor: pointer; } .dot { position: absolute; width: 0.55rem; height: 0.55rem; top: 50%; left: calc(100% - 0.55rem - 0.1rem); transform: translateY(-50%); border-radius: 50%; background-color: #ddd; transition: all 0.3s; } .day, .night { position: absolute; transform: translateY(-50%); width: 0.55rem; height: 0.55rem; display: flex; justify-content: center; align-items: center; transition: all 0.3s; } .day { left: 0.1rem; top: 50%; } .night { right: 0.1rem; top: 150%; }</style></head><body><divclass="wrapper"><inputtype="checkbox"id="dark"checked><labelclass="btn"for="dark"><divclass="dot"></div><divclass="day">☀️</div><divclass="night">🌛</div></label></div></body></html>实现思路拆分
明暗切换是怎么画出来的?
明暗切换(light/dark toggle button with css :has())用隐藏 checkbox + body:has(:checked) 切换全页昼夜背景,开关内滑块与 ☀️/🌛 emoji 同步滑动,纯 CSS 无 JS。
说白了就三件事
HTML: input#dark+label.btn,内含.dot滑块与.day/.nightemoji。CSS:body 200% 宽渐变背景, background-position切换黄/蓝;:has(#dark:checked)驱动 dot 位置与 emoji 上下滑入。JavaScript:无。
改尺寸动 $dot-size 等效 rem 值,改过渡动 0.3s,改配色动渐变两色 #ffd65b / #263651。
颜色为啥长这样
日间:暖黄 #ffd65b占半屏夜间:深蓝 #263651占半屏开关:白底圆角 pill + 浅灰/深灰 dot emoji:Noto Color Emoji ☀️ / 🌛 整体对比鲜明、切换一目了然
动起来是啥感觉
默认 checked(夜间),点击切换昼夜。
未 checked:背景偏黄,dot 在右,☀️ 居中可见,🌛 在下方隐藏 checked:背景滑至深蓝,dot 滑左变 #333,🌛 升上,☀️ 下沉隐藏body 背景与开关内动画同步 0.3s pill 阴影 #0005微浮起
怎么一层层画出来
body 背景切换
linear-gradient(to right, #ffd65b 50%, #263651 50%)左右各半。background-size: 200%+background-position0 或 100% 露出对应半色。body:has(input#dark:checked)切 position,无需 class toggle。
开关内部
.dot绝对定位,left在右/左两端切换。.day/.night用top: 50%vs150%做垂直滑入滑出。.btnoverflow: hidden裁剪 emoji 出入动画。
源码获取

夜雨聆风