网页的暗黑模式切换
在网页中实现暗黑模式和亮色模式的切换,通过定义CSS变量(Custom Properties)来管理不同的主题颜色,并根据特定的条件来应用这些变量。
实现效果
移动端还存在一个问题,如下图第三张,移动设备上的浏览器设置为暗黑模式(Dark Mode)时,页面的浅色模式样式被浏览器覆盖,如果对于该情况有特殊需求,比如保持不被覆盖,可通过使用媒体查询检测系统暗黑模式再设置相应样式。
使用 CSS 媒体查询
prefers-color-scheme
,你可以检测用户的系统是否启用了暗黑模式,并相应地调整网页的样式。相应代码设置:
/* 这个用于处理浏览器设置为暗色模式的情况 */ @media (prefers-color-scheme: dark) { :root { --text-color: #2ecc71; --bg-color: #34495e; } }
完整代码
-
index.html
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link href="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/minireset.css/0.0.2/minireset.min.css" type="text/css" rel="stylesheet" /> <link rel="stylesheet" href="./index.css" /> <link rel="stylesheet" href="./theme.css" /> <title>暗黑模式切换</title> </head> <body> <div class="container"> <div class="themeTitle selector" id="themeTitle">浅色</div> <div class="themeSwitch"> <label class="ui-switch"> <input type="checkbox" id="themeSwitch" onclick="switchTheme()" /> <div class="slider"> <div class="circle"></div> </div> </label> </div> </div> </body> <script src="./index.js"></script> </html>
-
index.css
/* switch settings 👇 */ .ui-switch { /* switch */ --switch-bg: rgb(135, 150, 165); --switch-width: 48px; --switch-height: 20px; /* circle */ --circle-diameter: 32px; --circle-bg: rgb(0, 56, 146); --circle-inset: calc((var(--circle-diameter) - var(--switch-height)) / 2); } .ui-switch input { display: none; } .slider { -webkit-appearance: none; -moz-appearance: none; appearance: none; width: var(--switch-width); height: var(--switch-height); background: var(--switch-bg); border-radius: 999px; position: relative; cursor: pointer; } .slider .circle { top: calc(var(--circle-inset) * -1); left: 0; width: var(--circle-diameter); height: var(--circle-diameter); position: absolute; background: var(--circle-bg); border-radius: inherit; background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjAiIHdpZHRoPSIyMCIgdmlld0JveD0iMCAwIDIwIDIwIj4KICAgIDxwYXRoIGZpbGw9IiNmZmYiCiAgICAgICAgZD0iTTkuMzA1IDEuNjY3VjMuNzVoMS4zODlWMS42NjdoLTEuMzl6bS00LjcwNyAxLjk1bC0uOTgyLjk4Mkw1LjA5IDYuMDcybC45ODItLjk4Mi0xLjQ3My0xLjQ3M3ptMTAuODAyIDBMMTMuOTI3IDUuMDlsLjk4Mi45ODIgMS40NzMtMS40NzMtLjk4Mi0uOTgyek0xMCA1LjEzOWE0Ljg3MiA0Ljg3MiAwIDAwLTQuODYyIDQuODZBNC44NzIgNC44NzIgMCAwMDEwIDE0Ljg2MiA0Ljg3MiA0Ljg3MiAwIDAwMTQuODYgMTAgNC44NzIgNC44NzIgMCAwMDEwIDUuMTM5em0wIDEuMzg5QTMuNDYyIDMuNDYyIDAgMDExMy40NzEgMTBhMy40NjIgMy40NjIgMCAwMS0zLjQ3MyAzLjQ3MkEzLjQ2MiAzLjQ2MiAwIDAxNi41MjcgMTAgMy40NjIgMy40NjIgMCAwMTEwIDYuNTI4ek0xLjY2NSA5LjMwNXYxLjM5aDIuMDgzdi0xLjM5SDEuNjY2em0xNC41ODMgMHYxLjM5aDIuMDg0di0xLjM5aC0yLjA4NHpNNS4wOSAxMy45MjhMMy42MTYgMTUuNGwuOTgyLjk4MiAxLjQ3My0xLjQ3My0uOTgyLS45ODJ6bTkuODIgMGwtLjk4Mi45ODIgMS40NzMgMS40NzMuOTgyLS45ODItMS40NzMtMS40NzN6TTkuMzA1IDE2LjI1djIuMDgzaDEuMzg5VjE2LjI1aC0xLjM5eiIgLz4KPC9zdmc+"); background-repeat: no-repeat; background-position: center center; -webkit-transition: left 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, -webkit-transform 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; -o-transition: left 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, transform 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; transition: left 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, transform 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, -webkit-transform 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-align: center; -ms-flex-align: center; align-items: center; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12); } .slider .circle::before { content: ""; position: absolute; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.75); border-radius: inherit; -webkit-transition: all 500ms; -o-transition: all 500ms; transition: all 500ms; opacity: 0; } /* actions */ .ui-switch input:checked + .slider .circle { left: calc(100% - var(--circle-diameter)); background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjAiIHdpZHRoPSIyMCIgdmlld0JveD0iMCAwIDIwIDIwIj4KICAgIDxwYXRoIGZpbGw9IiNmZmYiCiAgICAgICAgZD0iTTQuMiAyLjVsLS43IDEuOC0xLjguNyAxLjguNy43IDEuOC42LTEuOEw2LjcgNWwtMS45LS43LS42LTEuOHptMTUgOC4zYTYuNyA2LjcgMCAxMS02LjYtNi42IDUuOCA1LjggMCAwMDYuNiA2LjZ6IiAvPgo8L3N2Zz4="); } .ui-switch input:active + .slider .circle::before { -webkit-transition: 0s; -o-transition: 0s; transition: 0s; opacity: 1; width: 0; height: 0; } body { background-color: var(--bg-color); color: var(--text-color); transition: background-color 0.3s, color 0.3s; } .container { width: 100%; margin: 0 auto; display: flex; flex-direction: column; align-items: center; padding-top: 6rem; } .themeTitle { font-size: 2rem; font-weight: 700; margin-bottom: 1rem; } /* 文字不可选中 */ .selector { -webkit-user-select: none; /* Chrome, Safari, Opera */ -moz-user-select: none; /* Firefox */ -ms-user-select: none; /* Internet Explorer/Edge */ user-select: none; /* 标准浏览器 */ } .themeSwitch { margin: 1rem 0; }
-
index.js
let theme = ""; function switchTheme(model) { // 如果没有传入数据,则是切换主题,传入了数据,则是设置主题 if (model) { theme = model; } else { theme = theme === "light" ? "dark" : "light"; } console.log("switchTheme"); const text = theme === "light" ? "浅色" : "深色"; const checked = theme === "light" ? false : true; document.documentElement.setAttribute("data-theme", theme); document.getElementById("themeTitle").innerText = text; document.getElementById("themeSwitch").checked = checked; localStorage.setItem("theme", theme); } // 页面加载完成 window.onload = function () { // 获取主题设置 const theme = localStorage.getItem("theme") || "light"; switchTheme(theme); };
-
theme.css
/* 暗色主题 */ html[data-theme="dark"] { --text-color: #f2f2f2; --bg-color: #171717; } /* 默认亮色主题 */ :root { --text-color: #000; --bg-color: #f2f2f2; } /* 这个用于处理浏览器设置为暗色模式的情况 */ @media (prefers-color-scheme: dark) { :root { --text-color: #2ecc71; --bg-color: #34495e; } }
评论区