暗色模式看这一篇就够了

CSS暗色模式

暗色模式(darkmode)绝对是客户端(Client)最出色的设计之一。亮色模式即以白色或浅色为主色调的主题,暗色模式即以黑色或深色为主色调的主题(此处的"主色调"主要是指所占区域最大的背景颜色风格等)。

暗色模式的流行与智能手机的普及密不可分,它不仅可以减少眼睛疲劳,还能节省设备电池寿命。而亮/暗的两套主题也扩展了页面的适用范围,使页面更加灵活、表现效果更好。

本文将从什么样的页面适合亮暗模式 (理念)、如何实现亮暗模式(原理)进行介绍。

Ant Design的亮暗模式对比

什么样的页面适合亮暗模式?

在决定动手为你的应用添加暗色/亮色模式或者是在规划阶段确定要设置亮暗模式之前,你有必要了解清楚"什么样的页面适合亮暗模式"。

是所有的页面都适合亮色/暗色模式吗? 答案是否定的。

我们需要先明确颜色对于风格的影响。白色等浅色调的颜色通常给人一种干净、简洁、现代的感觉,而黑色等深色调的颜色则传达出一种优雅、神秘和专业的氛围。因此选择什么样的颜色风格取决于你想要传达的品牌形象和用户体验。

对于教育类网站 (学校官网)、官方传媒 (新华网、人民网)、国民级生活应用 (支付宝、微信、QQ、微博)等,这类网站通常需要传达出权威、可信赖和专业的形象;购物类网站 (淘宝、京东)需要清楚地呈现商品、激发人们的购买欲;还有偏可爱、清新风格的应用(B站)等使用亮色模式都是非常常见的选择。

新华网

淘宝网

对于科技类网站 (尤其是需要营销的科技类网站)如各类提供AI服务的网站 (DeepSeek、ChatGPT、Kimi)、代码类网站 (GitHub、LeetCode)、部分设计类网站等,暗色模式可以传达出一种现代、前卫和专业的感觉,符合科技行业的形象。暗色模式也给人一种"安全感",因此大部分浏览器的"无痕浏览"等功能也采用了暗色模式。

DeepSeek

GitHub

除了这两种网站,还有彩色、图片占比大的页面,常见于游戏网站视频网站。而即使如此,其采用的颜色与风格仍有亮暗之分,如二次元题材常使用浅的亮色、史诗战争类题材偏向使用暗色、未来题材偏向于暗色背景+亮色色块(尤其是红色、浅绿色)制造出信号波动与科技的效果。

战地风云6

小结一下,使用深色模式可以:

  • 减轻眼睛疲劳;
  • 构造视觉层次;
  • 使界面更神秘;
  • 营造高端感;
  • 提高在夜间提高可读性;
  • 确保夜晚使用环境(比如在晚上使用娱乐的app);

在以下情况下,请避免使用暗黑模式:

  • 该解决方案在户外白天使用;
  • 有很多文字可供阅读;

这些是深色模式的优缺点

总之,颜色风格决定用户感受,因此我们需要从期望的用户感受反推颜色风格,进而决定是否需要亮暗模式或相应的主题。


如何实现亮暗模式?

亮暗模式是主题色为亮色/暗色的两套主题的统称。它既可以非常简单,最简单的是黑白两色加边框;也可以逐渐丰富,选取不同的亮色/暗色构建颜色的层次区分而不再只用一条边框勾勒布局。

不过这些更多是设计的职责了,对于前端程序员来说,掌握合适的实现方式即可(太复杂的颜色交给AI也未尝不可)。

实现之前

在实现之前,我们先介绍CSS的几个特性,它们是实现亮暗模式的基础

color-scheme

color-schemeCSS属性允许元素指示它可以舒适地呈现哪些颜色方案。

操作系统颜色方案的常见选择为"亮色"和"暗色",或"日间模式"和"夜间模式"。当用户选择其中一种颜色方案时,操作系统会对用户界面进行调整,包括表单控件、滚动条和CSS系统颜色的使用值。

css 复制代码
/* color-schema 语法 */
color-scheme: normal;
color-scheme: light;
color-scheme: dark;
color-scheme: light dark;
color-scheme: only light;

/* 全局值 */
color-scheme: inherit;
color-scheme: initial;
color-scheme: revert;
color-scheme: revert-layer;
color-scheme: unset;

注意:还必须使用prefers-color-scheme媒体功能来支持其余元素的配色方案。

prefers-color-scheme

prefers-color-schemeCSS媒体特性用于检测用户是否有将系统的主题色设置为亮色或者暗色。

语法:

  • no-preference:表示系统未得知用户在这方面的选项。在布尔值上下文中,其执行结果为 false
  • light:表示用户已告知系统他们选择使用浅色主题的界面。
  • dark:表示用户已告知系统他们选择使用暗色主题的界面。

"未得知"可理解为:浏览器的宿主系统不支持设置主题色,或者支持主题色并默认为/被设为了未设置/无偏好。 "已告知"为:浏览器的宿主系统支持设置主题色,且被设置为了亮色或者暗色。

这一特性可以让我们根据用户的系统主题色偏好来自动调整网页的主题,从而提供更好的首次加载体验。

如果想使用JavaScript来检测用户的主题色偏好,可以使用 window.matchMedia() 方法:

javascript 复制代码
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
    // 用户偏好暗色主题
} else {
    // 用户偏好亮色主题或无偏好
}
自定义属性(变量)

自定义属性(有时候也被称作 CSS变量 或者 级联变量)是由 CSS 作者定义的,它包含的值可以在整个文档中重复使用。

由自定义属性标记设定值(比如:--main-color: black;),由 var() 函数来获取值(比如:color: var(--main-color);)。

用 var() 函数可以定义多个备用值(fallback value),当给定值未定义时将会用备用值替换。(color: var(--my-var, red, blue, black);

自定义属性会继承。这意味着如果在一个给定的元素上,没有为这个自定义属性设置值,在其父元素上的值会被使用。

当浏览器遇到无效的 var() 时,会使用继承值或初始值代替。

复杂的网站都会有大量的CSS代码,通常也会有许多重复的值。举个例子,同样一个颜色值可能在成千上百个地方被使用到,如果这个值发生了变化,需要全局搜索并且一个一个替换(很麻烦哎~)。自定义属性在某个地方存储一个值,然后在其他许多地方引用它,增强了代码的复用性

另一个好处是语义化 的标识。比如,--main-text-color 会比 #00ff00 更易理解,尤其是这个颜色值在其他上下文中也被使用到。

在实现亮暗模式时,自定义属性可以让我们根据主题设置两套颜色变量并实现切换。

媒体查询

媒体查询(Media query)非常实用,尤其是当你想要根据设备的大致类型(如打印设备与带屏幕的设备)或者特定的特征和设备参数(例如屏幕分辨率和浏览器视口宽度)来修改网站或应用程序时。

媒体查询常被用于以下目的:

  • 有条件的通过@media@import at规则用CSS装饰样式。
  • media= 属性为<style><link><source>和其他HTML元素指定特定的媒体类型(<link rel="stylesheet" src="styles.css" media="print" />)。
  • 使用Window.matchMedia()MediaQueryList.addListener()方法来测试和监控媒体状态。

每条媒体查询语句都由一个可选的媒体类型和任意数量的媒体特性表达式构成。可以使用多种逻辑操作符合并多条媒体查询语句。

媒体查询语句不区分大小写。

当媒体类型(如果指定)与在其上显示文档的设备匹配并且所有媒体功能表达式都计算为 true 时,媒体查询将计算为 true。涉及未知媒体类型的查询始终为 false

媒体类型描述了给定设备的一般类别。

css 复制代码
@media screen, print { ... }

媒体功能描述了给定的user agent的输出设备或环境的特定特征。

css 复制代码
@media (hover: hover) { ... }

@media (max-width: 12450px) { ... }

@media (color) { ... }

@media speech and (aspect-ratio: 11/5) { ... }

有时你可能想创建一个取决于多个条件的媒体查询。这就是逻辑运算符使用的场景:not,and,和 only。此外,你可以将多个媒体查询合并到一个逗号分隔的列表中。这使你可以在不同情况下应用相同的样式。

css 复制代码
@media (min-width: 30em) and (orientation: landscape) { ... }

@media (min-height: 680px), screen and (orientation: portrait) { ... }

@media not all and (monochrome) { ... }
@media not (all and (monochrome)) { ... } /* 这两行代码等价 */

媒体查询 4 级规范对语法进行了一些改进,以使用具有"范围"类型(例如宽度或高度,减少冗余)的功能进行媒体查询。级别 4 添加了用于编写此类的查询范围上下文。

css 复制代码
@media (max-width: 30em) { ... }
@media (width <= 30em) { ... }

@media (min-width: 30em) and (max-width: 50em) { ... }
@media (30em <= width <= 50em ) { ... }

@media (30em <= width <= 50em ) { ... }

@media (not (color)) or (hover) { ... }

filter滤镜

如果你想要最快速、最低成本实现暗色模式,filter属性是一个不错的选择。它可以通过简单的CSS代码将亮色主题转换为暗色主题,适用于非常简单的页面(如个人博客等)。

CSS filter 属性

CSS filter 属性将模糊或颜色偏移等图形效果应用于元素。滤镜通常用于调整图像、背景和边框的渲染。

CSS滤镜函数

css 复制代码
/* <filter-function> 值 */
filter: blur(5px);
filter: brightness(0.4);
filter: contrast(200%);
filter: drop-shadow(16px 16px 20px blue);
filter: grayscale(50%);
filter: hue-rotate(90deg);
filter: invert(75%);
filter: opacity(25%);
filter: saturate(30%);
filter: sepia(60%);

/* URL */
filter: url("filters.svg#filter-id");

/* 多个滤镜 */
filter: contrast(175%) brightness(3%);
filter: drop-shadow(3px 3px red) sepia(100%) drop-shadow(-3px -3px blue);

/* 不使用滤镜 */
filter: none;

/* 全局值 */
filter: inherit;
filter: initial;
filter: revert;
filter: revert-layer;
filter: unset;
使用filter实现暗色模式

通过滤镜的色彩反转(invert)和色相旋转(hue-rotate)功能,可以快速将亮色主题转换为暗色主题。

对于图片和视频等媒体内容,可以通过再次应用相同的滤镜来防止它们被反转("左转三次等于右转")。

css 复制代码
@media (prefers-color-scheme: dark) {
    html {
        filter: invert(1) hue-rotate(180deg);
    }
    img, video { /* 防止图片和视频被反转 */
        filter: invert(1) hue-rotate(180deg);
    }
}

颜色变量与选择器

这是最常用的方式。通过定义两套颜色变量(亮色主题和暗色主题),并使用媒体查询切换不同CSS选择器来切换主题。

css 复制代码
:root { 
  --bg-color : #ffffff ; 
  --text-color : #000000 ; 
  --bg-color-dark : #121212 ; 
  --text-color-dark : #ffffff ;
} 

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

@media (prefers-color-scheme: dark) {
  body {
    background-color: var(--bg-color-dark);
    color: var(--text-color-dark);
  }
}

自由切换

在上一步骤的基础上,我们不再仅依赖于媒体查询,而是通过JavaScript实现自由切换,并用全局级的变量存储主题状态(如 LocalStorage、Redux、后端数据库存储等)。

基本上有亮暗主题的网站都会通过按钮允许用户手动切换而不是仅依赖于系统主题色偏好,上一步中的写法仅为了清晰展示prefers-color-schema的效果。

css 复制代码
:root {
    --bg-color: #ffffff;
    --text-color: #000000;
}

[data-theme="dark"] {
    --bg-color: #121212;
    --text-color: #ffffff;
}

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

接下来通过 JavaScript 或 React 等前端框架结合 LocalStorage 实现主题切换的逻辑。

JavaScript 示例:

javascript 复制代码
// JavaScript 示例

const toggleTheme = () => {
    const currentTheme = document.documentElement.getAttribute("data-theme");
    const newTheme = currentTheme === "dark" ? "light" : "dark";
    document.documentElement.setAttribute("data-theme", newTheme);
    localStorage.setItem("theme", newTheme); // 保存用户选择
};

// 页面加载时应用用户选择的主题
const savedTheme = localStorage.getItem("theme") || (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
document.documentElement.setAttribute("data-theme", savedTheme);

React 示例:

jsx 复制代码
// React 示例

import React, { useState, useEffect } from 'react';

const App = () => {
    const [theme, setTheme] = useState('light');

    useEffect(() => {
        const savedTheme = localStorage.getItem('theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
        setTheme(savedTheme);
        document.documentElement.setAttribute('data-theme', savedTheme);
    }, []);

    const toggleTheme = () => {
        const newTheme = theme === 'dark' ? 'light' : 'dark';
        setTheme(newTheme);
        document.documentElement.setAttribute('data-theme', newTheme);
        localStorage.setItem('theme', newTheme);
    };

    return (
        <div>
            <button onClick={toggleTheme}>
                切换到 {theme === 'dark' ? '亮色' : '暗色'} 模式
            </button>
            <h1>欢迎来到我的网站</h1>
            <p>这是一个支持亮暗模式切换的示例。</p>
        </div>
    );
};

export default App;

还有哪些细节?

设计原则

暗黑模式不是简单地切换颜色。暗黑模式的设计需要遵循一套原则,以确保页面的易读性、对比度和流畅的用户体验。

色彩对比

在暗黑模式下,对比度成为可读性的关键因素。大多数初学的网页设计师经常错误地认为深色模式就是使用黑色做背景,虽然全黑背景看起来很直观,但它会产生过多的对比度并容易导致眼睛疲劳。

深灰色背景搭配颜色较浅的文本可提供更加舒适的体验。在多数深色模式设计中,背景颜色通常使用深灰、深蓝或深绿色。Google Material Design 则建议使用 #121212 作为基本的深灰色背景选择。

最佳实践:使用略微偏黑的背景,并确保文本和背景颜色之间有足够的对比度以保持可读性。

色彩加强

鲜艳的强调色在浅色模式下展示效果良好,但在暗黑模式下可能会显得太亮。在暗黑模式下,为按钮(Button)、图标(Icon)和链接(Link)选择更柔和、饱和度更低的颜色,可确保用户界面保持赏心悦目,而不会让用户感到不知所措。

最佳实践:使用颜色工具在明暗模式下测试调色板,并调整饱和度以获得平衡的外观。

阴影和高度

由于背景较暗,阴影在暗黑模式下不太明显。如果要模拟深度和高度,应该考虑使用较浅的边框或者添加微妙的高光,而不仅仅依赖阴影。

最佳实践:调整阴影不透明度并使用边框高光使元素在暗模式下脱颖而出。

图片处理

由于界面光线昏暗,深色模式设计需要特别注意图片的使用。如果是游戏、影视等类型的网站,鲜艳的照片可能会导致不舒服的对比度,进而影响整体用户体验。

为了解决影像对比度降低的问题,一种选择是在绘图软件中就先将图片稍微压暗或是降彩度。但这种做法对于用户自行上传图片的情况下不适用,也不太可能要求所有后台操作者都熟悉绘图软件。

另一种做法是在深色模式下,给图片加上一层 overlay,我们常用的设置是 #C1C1C1 + 10% 不透明度,这样可以让图片的彩度稍微下降,但不会变成深色的感觉。当然其他用 CSS filter 的方法也可以考虑。

品牌标识

在明暗模式下保持品牌形象至关重要。在切换模式时,某些品牌颜色可能需要调整。确保品牌标识、按钮和重要品牌元素在两种主题中都清晰可见。

最佳实践:彻底测试品牌元素,以确保它们在暗黑模式下仍然可识别并保持其影响力。

注意:这一部分更多是设计理念的介绍,而非实现原理。另外,笔者并非专业设计师,只是更多结合自身对于设计的简单学习以及网上的相关文章斗胆建言,专业的颜色与设计知识需专业文章,本人观点如有谬误敬请指出。

组件库属性

如果你的网站大量使用了组件库(如Ant Design、Material UI等),那么你可以查看组件库的文档,看看它们是否提供了内置的亮暗模式支持。大多数现代组件库都支持通过配置主题来切换亮暗模式。

jsx 复制代码
// Ant Design
import { ConfigProvider } from 'antd';
import { useState } from 'react';
import { Button } from 'antd';
import { Switch } from 'antd';
import { BulbOutlined, BulbFilled } from '@ant-design/icons';
import 'antd/dist/reset.css';
import 'antd/dist/antd.dark.css'; // 引入暗黑主题样式
import 'antd/dist/antd.compact.css'; // 引入紧凑主题样式
import 'antd/dist/antd.variable.min.css'; // 引入可变主题样式
import './App.css';

function App() {
    const [isDarkMode, setIsDarkMode] = useState(false);

    const toggleTheme = () => {
        setIsDarkMode(!isDarkMode);
    };

    return (
        <ConfigProvider theme={{ mode: isDarkMode ? 'dark' : 'light' }}>
            <div className="app">
                <Switch
                    checkedChildren={<BulbFilled />}
                    unCheckedChildren={<BulbOutlined />}
                    checked={isDarkMode}
                    onChange={toggleTheme}
                />
                <Button type="primary">Primary Button</Button>
            </div>
        </ConfigProvider>
    );
}
export default App;

过渡动画

在亮暗模式间进行切换时,由于对比度较大,容易造成刺眼和突兀的不良效果,可以考虑添加过渡动画来提升用户体验,使切换过程更加平滑和自然。

CSS 框架支持

如果你使用的是CSS框架(如Bootstrap、Tailwind CSS等),请查看它们的文档,了解如何启用和配置亮暗模式支持。许多现代CSS框架都提供了内置的类或变量来处理亮暗模式。

html 复制代码
<!-- Tailwind CSS -->
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tailwind Dark Mode Example</title>
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-white text-black dark:bg-gray-900 dark:text-white">
    <div class="container mx-auto p-4">
        <h1 class="text-3xl font-bold mb-4">Hello, Tailwind CSS!</h1>
        <p class="mb-4">This is a simple example of dark mode using Tailwind CSS.</p>
        <button class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 dark:bg-blue-700 dark:hover:bg-blue-800">
            Click Me
        </button>
    </div>
</body>
</html>

兼容性问题

下面对于上文提到的所有方案的兼容性进行简单总结:

方法 兼容性
prefers-color-scheme 支持较好,现代浏览器均支持(Chrome 76+, Firefox 67+, Safari 12.1+)
CSS 自定义属性(变量) 支持较好,现代浏览器均支持(Chrome 49+, Firefox 31+, Safari 9.1+)
媒体查询 支持较好,现代浏览器均支持(Chrome 21+, Firefox 3.5+, Safari 3.1+)
CSS filter 支持较好,现代浏览器均支持(Chrome 18+, Firefox 35+, Safari 6+)
JavaScript 手动切换 兼容性好,所有现代浏览器均支持

总结

本文介绍了亮暗模式的设计理念和实现方法。亮暗模式不仅提升了用户体验,还能增强页面的视觉吸引力。通过合理的设计和技术实现,可以为用户提供更舒适的浏览环境。

除了技术实现外,设计原则同样重要。确保在设计过程中考虑色彩对比、色彩加强、阴影和高度、图片处理以及品牌标识等因素,以打造出既美观又实用的亮暗模式。在国内"轻交互"的现状下,

参考文献:

相关推荐
没有鸡汤吃不下饭2 小时前
Git将某个分支合并到开发(dev)、测试(test)后突然想撤销该分支的功能,怎么处理?
前端·git·github
文心快码BaiduComate3 小时前
Comate分饰多角:全栈开发一个Python学习网站
前端·后端·python
90后的晨仔3 小时前
Vue 插槽(Slots)全面解析与实战指南
前端·vue.js
我是日安3 小时前
从零到一打造 Vue3 响应式系统 Day 20 - Reactive:reactive 极端案例
前端·vue.js
Slice_cy3 小时前
📚 uniapp版本懒加载 + 不定高虚拟列表实现
前端
golang学习记3 小时前
从0死磕全栈之Next.js API 路由实战:不用后端,前端也能写接口!
前端
Nathan202406163 小时前
Kotlin-Sealed与Open的使用
android·前端·面试
MQliferecord3 小时前
前端性能优化实践经验总结
前端
RoyLin3 小时前
SurrealDB - 统一数据基础设施
前端·后端·typescript