随着深色模式成为各类APP和网站的常见功能,越来越多的设计方案涌现。但我个人的设计理念与这些方案有所不同,我个人喜欢采用单按钮切换的方式,默认首次访问时网站会跟随系统环境设置(深色/浅色),用户手动切换后,系统模式变化时网站主题色系仍可同步更新,确保一致的体验。文章目录常见的三种实现方案基础切换款三按钮切换款时间切换款各种方案的优缺点我的设计思路场景举例先看演示实现代码JavaScriptCSS深色模式下的页面滚动条

随着深色模式成为各类APP和网站的常见功能,越来越多的设计方案涌现。尽管市面上已有许多深色模式的实现方案,但我个人的设计理念与这些方案有所不同。

前段时间根据manus分析浅色系的配色,给Weisay Grace主题重新生成的深色模式的配色,不再是原来不同透明度的黑色,而是有了深蓝色元素,深浅色更加统一协调了。

常见的三种实现方案基础切换款

这是一种简单的模式切换,仅包含浅色与深色的切换。当用户选择深色模式后,系统环境的模式不再影响网站,网站始终保持用户的选择。

三按钮切换款

在基础款的基础上,增加了一个“自动”选项,允许网站根据系统环境自动调整模式。如果用户手动选择了固定的颜色,系统环境变化时,网站主题则不再自动变化。

时间切换款

根据时间段自动切换深色模式。例如,在晚上自动切换为深色模式,白天则恢复浅色模式,用户也可以手动进行切换。

各种方案的优缺点

基础款:功能简单,但完全忽略了系统环境的变化。

三按钮切换款:增加了自动模式,但用户需要点击两次才能完成切换,稍显繁琐。

时间切换款:过于“霸道”,直接根据时间切换模式,可能不符合用户的个性化需求。

我的设计思路

我偏向一种单按钮切换的方式,具体如下:

首次访问时,网站默认跟随系统环境设置(深色或浅色);

用户手动切换后,网站会记住用户的选择,不再自动跟随系统的主题;

当系统主题色再次变化时,网站主题色系模式仍可同步更新,确保体验一致。

系统:浅色

网站:默认跟随系统 → 浅色

用户手动切换网站

网站被手动切换为 → 深色

此时网站忽略系统主题,记住用户选择。

修改系统为深色

系统:深色

网站:因用户已手动选择深色 → 保持深色(不随系统改变)

再次修改系统为浅色

系统:浅色

网站:恢复为浅色(因用户未手动切换,重新跟随系统)

这种设计方式通过用户的手动选择临时“覆盖”系统设置,同时在系统主题变化时能重新同步,确保用户体验的一致性。这种方案既能兼顾系统偏好,又能提供个性化的操作体验。

当然,这种方式的缺点在于,如果用户希望网站颜色一直保持固定,而不受系统环境变化的影响,这种设计就不太适用了。

VIEW DEMO

在看下面的代码前,可以先参考 浅谈网页「深色模式」的实现 这篇文章,我想要的深色模式切换功能是在这个的基础上面修改来的。

通过使用了向 html标签 添加 dark/light 类,并通过点击切换按钮来实现深色模式切换的。

JavaScriptconstdocument.documentElement;const;const;const{ dark: true, light: true };const{ dark: "light", light: "dark" };const(key, value) => { try { localStorage.setItem(key, value); } catch (e) {}const(key) => { try { localStorage.removeItem(key); } catch (e) {}const(key) => { try { return localStorage.getItem(key); } catch (e) { return null;const() => { return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";const(mode) => { rootElement.classList.remove(mode, invertDarkModeObj[mode]); rootElement.classList.add(mode);const() => { rootElement.classList.remove(darkModeClassName, invertDarkModeObj[darkModeClassName]); removeLocalStorage(darkModeStorageKey);const(mode) => { // 接受从「开关」处传来的模式,或者从 localStorage 读取 constmode || getLocalStorage(darkModeStorageKey); if (currentSetting === getModeFromCSSMediaQuery()) { // 当用户自定义的显示模式和 prefers-color-scheme 相同时重置、恢复到自动模式 resetRootDarkModeClassAndLocalStorage(); setColorScheme(currentSetting); } else if (validColorModeKeys[currentSetting]) { rootElement.classList.add(currentSetting); rootElement.classList.remove(invertDarkModeObj[currentSetting]); } else { // 首次访问或从未使用过开关、localStorage 中没有存储的值,currentSetting 是 null // 或者 localStorage 被篡改,currentSetting 不是合法值 resetRootDarkModeClassAndLocalStorage(); // 使用系统当前方案 setColorScheme(getModeFromCSSMediaQuery());const() => { letgetLocalStorage(darkModeStorageKey); if (validColorModeKeys[currentSetting]) { // 从 localStorage 中读取模式,并取相反的模式invertDarkModeObj[currentSetting]; } else if (currentSetting === null) { // localStorage 中没有相关值,或者 localStorage 抛了 Error // 从 CSS 中读取当前 prefers-color-scheme 并取相反的模式invertDarkModeObj[getModeFromCSSMediaQuery()]; } else { // 不知道出了什么幺蛾子,比如 localStorage 被篡改成非法值 return; // 直接 return; // 将相反的模式写入 localStorage setLocalStorage(darkModeStorageKey, currentSetting); return currentSetting;// 当页面加载时,将显示模式设置为 localStorage 中自定义的值(如果有的话)applyCustomDarkModeSettings();const(event) => { // 获取新的系统主题方案 constevent.matches ? "dark" : "light"; // 用户主动配置了系统方案,清除用户之前记忆 resetRootDarkModeClassAndLocalStorage(); // 使用系统当前方案 setColorScheme(newColorScheme);constwindow.matchMedia("(prefers-color-scheme: dark)");// recommended method for newer browsers: specify event-type as first argumentdarkModePreference.addEventListener("change", onSystemSchemeChanged);// deprecated method for backward compatibilitydarkModePreference.addListener(onSystemSchemeChanged);:root { --text: #111; --background: #eee;.dark { --text: #ccc; --background: #111;body { color: var(--text); background: var(--background);

其实用 :root 是一个很好的方式,通过定义全局 CSS 变量,实现样式统一管理。修改变量值即可全局生效,其实非常适合主题切换或响应式设计的,但是我就是用不习惯。

因为js会在页面的html标签里面动态加上一个 light 或 dark 的类,那么深色模式也可以用 .dark 开头来定义。

深色模式下的页面滚动条

只需在页面的 中添加下面的标签即可让页面滚动条的样式跟随深色模式变化。

meta /

当然还要添加下面的css

:root { color-scheme: light;.dark { color-scheme: dark;