威言威语
我愿像茶,苦涩留心,清香予人。
威言威语
当前位置: 首页 > 设计 > 正文

深色模式的设计思考

随着深色模式成为各类APP和网站的常见功能,越来越多的设计方案涌现。但我个人的设计理念与这些方案有所不同,我个人喜欢采用单按钮切换的方式,默认首次访问时网站会跟随系统环境设置(深色/浅色),用户手动切换后,系统模式变化时网站主题色系仍可同步更新,确保一致的体验。
深色模式的设计思考

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

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

常见的三种实现方案

基础切换款

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

三按钮切换款

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

时间切换款

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

各种方案的优缺点

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

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

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

我的设计思路

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

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

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

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

场景举例

  1. 初始状态
  2. 系统:浅色

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

  3. 用户手动切换网站
  4. 网站被手动切换为 → 深色

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

  5. 修改系统为深色
  6. 系统:深色

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

  7. 再次修改系统为浅色
  8. 系统:浅色

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

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

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

先看演示

实现代码

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

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

JavaScript

const rootElement = document.documentElement;
const darkModeClassName = "dark";
const darkModeStorageKey = "user-color-scheme";
const validColorModeKeys = { dark: true, light: true };
const invertDarkModeObj = { dark: "light", light: "dark" };

const setLocalStorage = (key, value) => {
	try {
		localStorage.setItem(key, value);
	} catch (e) {}
};

const removeLocalStorage = (key) => {
	try {
		localStorage.removeItem(key);
	} catch (e) {}
};

const getLocalStorage = (key) => {
	try {
		return localStorage.getItem(key);
	} catch (e) {
		return null;
	}
};

const getModeFromCSSMediaQuery = () => {
	return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
};

const setColorScheme = (mode) => {
	rootElement.classList.remove(mode, invertDarkModeObj[mode]);
	rootElement.classList.add(mode);
};

const resetRootDarkModeClassAndLocalStorage = () => {
	rootElement.classList.remove(darkModeClassName, invertDarkModeObj[darkModeClassName]);
	removeLocalStorage(darkModeStorageKey);
};

const applyCustomDarkModeSettings = (mode) => {
	// 接受从「开关」处传来的模式,或者从 localStorage 读取
	const currentSetting = mode || 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 toggleCustomDarkMode = () => {
	let currentSetting = getLocalStorage(darkModeStorageKey);
	if (validColorModeKeys[currentSetting]) {
		// 从 localStorage 中读取模式,并取相反的模式
		currentSetting = invertDarkModeObj[currentSetting];
	} else if (currentSetting === null) {
		// localStorage 中没有相关值,或者 localStorage 抛了 Error
		// 从 CSS 中读取当前 prefers-color-scheme 并取相反的模式
		currentSetting = invertDarkModeObj[getModeFromCSSMediaQuery()];
	} else {
		// 不知道出了什么幺蛾子,比如 localStorage 被篡改成非法值
		return; // 直接 return;
	}
	// 将相反的模式写入 localStorage
	setLocalStorage(darkModeStorageKey, currentSetting);

	return currentSetting;
};

// 当页面加载时,将显示模式设置为 localStorage 中自定义的值(如果有的话)
applyCustomDarkModeSettings();

const onSystemSchemeChanged = (event) => {
	// 获取新的系统主题方案
	const newColorScheme = event.matches ? "dark" : "light";
	// 用户主动配置了系统方案,清除用户之前记忆
	resetRootDarkModeClassAndLocalStorage();
	// 使用系统当前方案
	setColorScheme(newColorScheme);
};

const darkModePreference = window.matchMedia("(prefers-color-scheme: dark)");

// recommended method for newer browsers: specify event-type as first argument
darkModePreference.addEventListener("change", onSystemSchemeChanged);

// deprecated method for backward compatibility
darkModePreference.addListener(onSystemSchemeChanged);
CSS

:root {
	--text: #111;
	--background: #eee;
}

.dark {
	--text: #ccc;
	--background: #111;
}

body {
	color: var(--text);
	background: var(--background);
}

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

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

深色模式下的页面滚动条

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


<meta name="color-scheme" content="light dark" />

当然还要添加下面的css


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

您可能还会对这些文章感兴趣!

深色模式的设计思考:目前有 1 条评论

  1. 老麦
    沙发
    老麦Google Chrome 138.0.0.0 Android 10

    我的博客实现方式差不多,也是单按钮,在不同的情况以不同的处理方式来切换 auto dark light

    2025-07-18 01:20 回复

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

gravatar

question razz sad smile redface biggrin eek shock confused cool lol mad rolleyes wink cry