首页
/ Ant Design Menu 主题切换实战:内置 light / dark 双主题与源码级配色机制解析

Ant Design Menu 主题切换实战:内置 light / dark 双主题与源码级配色机制解析

2026-09-07 15:51:23作者:龚格成

导读

Menu 是 Ant Design 中最常被用于搭建导航骨架的组件之一,而它的 theme 属性让开发者可以仅凭一个字符串就完成整棵菜单树从浅色到深色的视觉切换。本篇围绕 components/menu/demo/theme.md 所述内容展开:先给出可直接运行的双主题切换示例,再进入源码确认 theme 参数的默认值、类型定义与 className 生成逻辑,最后拆解 dark 主题背后成套的组件级 Design Token,帮助你既能在业务中熟练切换主题,也能理解深浅色视觉差异的底层来源。

主题能力概述:两套内置主题,默认 light

在 Menu 的官方示例描述中明确说明了这一事实:

Menu 内建了两套主题 lightdark,默认 light。(There are two built-in themes: light and dark. The default value is light.)

这句话对应到 API 上就是 Menu 组件暴露的 theme 属性,其取值范围被严格约束为两个枚举值之一。在 Menu API 文档 中,该属性的定义为:

Property Description Type Default
theme 菜单主题颜色 light | dark light

类型层面,MenuTheme 联合类型定义在 components/menu/MenuContext.tsx

export type MenuTheme = 'light' | 'dark';

它同时通过 components/menu/index.tsx 从组件包的入口重新导出,因此业务代码可以直接 import type { MenuTheme } from 'antd' 使用。这意味着一套菜单只会在两种固定主题之间切换,杜绝了自由字符串带来的样式错乱,也方便类型系统在编译期帮你捕获拼写错误。

可直接运行的完整示例:Switch 切换 dark / light

配套的演示代码 components/menu/demo/theme.tsx 给出了一个完整、可直接复制的实现:用 Ant Design 的 Switch 组件作为主题切换开关,选中态即为 dark,取消选中回到 light,同时配合受控的 selectedKeys 展示菜单选中效果。

import React, { useState } from 'react';
import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@ant-design/icons';
import type { MenuProps, MenuTheme } from 'antd';
import { Menu, Switch } from 'antd';

type MenuItem = Required<MenuProps>['items'][number];

const items: MenuItem[] = [
  {
    key: 'sub1',
    label: 'Navigation One',
    icon: <MailOutlined />,
    children: [
      { key: '1', label: 'Option 1' },
      { key: '2', label: 'Option 2' },
      { key: '3', label: 'Option 3' },
      { key: '4', label: 'Option 4' },
    ],
  },
  {
    key: 'sub2',
    label: 'Navigation Two',
    icon: <AppstoreOutlined />,
    children: [
      { key: '5', label: 'Option 5' },
      { key: '6', label: 'Option 6' },
      {
        key: 'sub3',
        label: 'Submenu',
        children: [
          { key: '7', label: 'Option 7' },
          { key: '8', label: 'Option 8' },
        ],
      },
    ],
  },
  {
    key: 'sub4',
    label: 'Navigation Three',
    icon: <SettingOutlined />,
    children: [
      { key: '9', label: 'Option 9' },
      { key: '10', label: 'Option 10' },
      { key: '11', label: 'Option 11' },
      { key: '12', label: 'Option 12' },
    ],
  },
];

const App: React.FC = () => {
  const [theme, setTheme] = useState<MenuTheme>('dark');
  const [current, setCurrent] = useState('1');

  const changeTheme = (value: boolean) => {
    setTheme(value ? 'dark' : 'light');
  };

  const onClick: MenuProps['onClick'] = (e) => {
    console.log('click ', e);
    setCurrent(e.key);
  };

  return (
    <>
      <Switch
        checked={theme === 'dark'}
        onChange={changeTheme}
        checkedChildren="Dark"
        unCheckedChildren="Light"
      />
      <br />
      <br />
      <Menu
        theme={theme}
        onClick={onClick}
        style={{ width: 256 }}
        defaultOpenKeys={['sub1']}
        selectedKeys={[current]}
        mode="inline"
        items={items}
      />
    </>
  );
};

export default App;

该示例的关键手法可以拆成三点复用到实际业务中:

  1. 用布尔状态桥接双主题Switchchecked 天然对应布尔值,而 MenuTheme 是二选一字符串,因此通过 theme === 'dark'changeTheme 中的三元表达式完成互转,状态单一、逻辑直观。
  2. items 与渲染分离:菜单数据结构完全由 items 描述(含多级子菜单 sub3),组件内部不再写 JSX 子节点,便于主题切换时仅重渲染样式而非重建结构。
  3. 受控选中与受控主题并行selectedKeys={[current]} 让选中态由 React 状态驱动,onClick 中更新 current,这样切换主题后当前高亮项不会丢失。

源码解析:theme 默认值、上下文与 className 的生成

components/menu/menu.tsx 中,MenuProps 显式声明了 theme?: MenuTheme,而组件实现内部通过解构默认值将其兜底为 light(见 menu.tsx 第 141 行):

theme = 'light',

这与文档中「默认 light」的描述完全一致——即便调用方完全不传 theme,组件也会以 light 主题渲染。顺着渲染流程可以看到 theme 的两种下游去向:

去向一:生成根节点与溢出弹出层的主题 class。 组件组合出形如 ${prefixCls}-${theme} 的类名(menu.tsx 第 244 行),在默认 prefixClsant-menu 时即得到 ant-menu-light / ant-menu-dark;当菜单内容溢出、以弹出层形式展示时(横向模式的 overflow 场景),弹出层同样会拼接该主题 class(menu.tsx 第 302-306 行),保证子菜单弹出面板与父级主题一致。

去向二:写入 MenuContext 供后代消费。menu.tsx 第 269-293 行 构造的 MenuContextProps 中,theme 被放入 context。而 components/menu/MenuContext.tsxtheme?: MenuTheme 字段说明:菜单内部的 SubMenu、MenuItem 等子结构并不各自持有主题副本,而是统一从祖先 context 读取,这正是「整棵树一键换肤」的机制基础——你只需在外层 Menu 上传一次 theme,所有层级的展开箭头、悬停态、选中态都会联动变化。

dark 主题的底层视觉来源:成套的 dark 组件 Token

主题切换的视觉效果并不来自硬编码的随机颜色,而是由一组以 dark* 命名的组件级 Token 驱动,定义在 components/menu/style/index.tsMenuToken 中(覆盖背景、文字、悬停、选中、危险项、分组标题、弹出面板等维度的 dark 态取值)。其默认值通过 getDefaultToken 汇总,例如:

  • darkItemBg: '#001529'(深色菜单底色)
  • darkPopupBg: '#001529'(深色弹出子菜单底色)
  • darkSubMenuItemBg: '#000c17'(深色子菜单项的背景,比菜单底色更暗一层,用于体现层级)
  • darkItemSelectedBg: colorPrimary(选中项直接使用主题主色高亮)
  • darkItemColor: colorTextDarkdarkItemHoverColor: colorTextLightSolid 等文字颜色
  • 禁用态 darkItemDisabledColor 通过 new FastColor(colorTextLightSolid).setA(0.25).toRgbString() 在亮色文字基础上降低透明度生成(见 style/index.ts

也就是说,dark 主题的「深色底 + 主色选中块 + 亮色文字」观感,是这些 Token 协同作用的结果。样式生成侧则通过 theme/internalgenStyleHooksmergeToken 等工具将 Token 编译为最终 CSS-in-JS 样式,并依据类名 ant-menu-dark 生效,相关结构可参考 components/menu/style/index.ts 与同目录下的 theme.tsgetThemeStyle)。

若你需要在主题色、圆角、暗色底等维度做企业级定制,正确的姿势是使用 ConfigProvider 的主题 Token 能力,而不是直接覆盖这几个 dark* 字符串——这样既保留内置主题的联动逻辑,又能让视觉与整体设计语言保持一致。

周边联动场景

围绕内置双主题,仓库还提供了多个可对照参考的相邻演示,帮助你覆盖更多布局形态:

小结

Menu 的 light / dark 双主题看似只是一个布尔开关,但背后串联了「类型定义(MenuTheme)→ 组件默认值(light)→ context 传递 → 主题 class 生成 → dark Token 着色」的完整链路。在业务中使用时,你只需记住三点:theme 只接受 'light' | 'dark' 两个值且默认 light;通过受控状态加 Switch 即可实现运行时切换;若对深色观感不满意,请通过 ConfigProvider 主题 Token 定制而非直接覆写样式类。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
594
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
516
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388