乐于分享
好东西不私藏

明暗切换(可复制源代码)

明暗切换(可复制源代码)

效果演示

文末可一键复制完整代码

源代码

<!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-sizecalc(100vmax / 1600 * 100); } @media (max-width:992px) { html { font-size100px; } } body { min-height100vhpadding0.2rem0.5remfont-size0.3remdisplay: flex; justify-content: center; align-items: center; background-imagelinear-gradient(to right, #ffd65b 50%, #26365150%); background-repeat: no-repeat; background-size200%100%background-position00transition: background-position 0.3s; } body:has(input#dark:checked) { background-position100%0; } body:has(input#dark:checked).dot { left0.1rembackground-color#333; } body:has(input#dark:checked).night { top50%; } body:has(input#dark:checked).day { top150%; } input { display: none; } .btn { display: block; width1.6remheight0.7rempadding0.1rem0.2remborder-radius1rembox-shadow2px2px10px#0005background-color#ffffont-family'Noto Color Emoji', sans-serif; position: relative; overflow: hidden; cursor: pointer; } .dot { position: absolute; width0.55remheight0.55remtop50%leftcalc(100% - 0.55rem - 0.1rem); transformtranslateY(-50%); border-radius50%background-color#dddtransition: all 0.3s; } .day.night { position: absolute; transformtranslateY(-50%); width0.55remheight0.55remdisplay: flex; justify-content: center; align-items: center; transition: all 0.3s; } .day { left0.1remtop50%; } .night { right0.1remtop150%; }</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。

说白了就三件事

  1. HTMLinput#dark + label.btn,内含 .dot 滑块与 .day / .night emoji。
  2. CSS:body 200% 宽渐变背景,background-position 切换黄/蓝;:has(#dark:checked) 驱动 dot 位置与 emoji 上下滑入。
  3. 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 背景切换
  1. linear-gradient(to right, #ffd65b 50%, #263651 50%) 左右各半。
  2. background-size: 200% + background-position 0 或 100% 露出对应半色。
  3. body:has(input#dark:checked) 切 position,无需 class toggle。
开关内部
  1. .dot 绝对定位,left 在右/左两端切换。
  2. .day / .night 用 top: 50% vs 150% 做垂直滑入滑出。
  3. .btnoverflow: hidden 裁剪 emoji 出入动画。

源码获取