用 ConfigProvider 主题 Token 在 antd 中还原 V4 风格 Menu:menu-v4 示例深度拆解
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design
Ant Design 从 v5 起全面切换到 CSS-in-JS 的 Design Token 体系,Menu 组件的视觉细节也随之调整。对于希望沿用 v4 时代"方角、无内边距、左侧竖条指示"导航风格的老项目或设计规范,官方在components/menu/demo/menu-v4.tsx中给出了一个标为 debug 的示例(对应文档 components/menu/demo/menu-v4.md),仅通过ConfigProvider覆盖 8 个组件级 Token,即可在 v5 组件上完整还原 v4 的 Menu 观感。读完本文,你将掌握这套 Token 的每个取值含义、其背后的样式生成原理,以及如何按需微调成自己的"类 v4"导航样式。
V4 风格 Menu 的视觉特征
在动手配置之前,先明确"V4 样式"到底指什么。对比当前版本 Menu 的默认外观(圆角菜单项、左右留白、hover 灰底),V4 风格主要包含以下特征:
- 直角布局:菜单项与子菜单项不设圆角;
- 零横向外边距:菜单项横向紧贴容器,没有
margin-inline留白; - 左侧选中指示条:inline 模式下选中项左侧有一条约 3px 的竖条(V4 经典"墨条");
- 品牌蓝交互色:hover 与选中文字均为
#1890ff(V4 主色); - 浅蓝选中底色:选中项背景为
#e6f7ff(V4 的item-selected-bg),hover 无底色变化; - 紧凑密度:菜单项高度、间距沿用 V4 的紧凑规格。
这些特征全部可以通过 Menu 组件级 Token 精确还原,无需写任何自定义 CSS。
完整示例:直接可运行的还原代码
以下代码完整来自官方 demo(components/menu/demo/menu-v4.tsx),它定义了一份包含普通项、带图标项、二级与三级子菜单、外部链接项的数据,并在ConfigProvider中集中覆盖 Menu Token:
import React, { useState } from 'react'; import { AppstoreOutlined, CalendarOutlined, LinkOutlined, MailOutlined, SettingOutlined, } from '@ant-design/icons'; import { ConfigProvider, Menu, Switch, Typography } from 'antd'; import type { MenuProps } from 'antd'; type MenuItem = Required<MenuProps>['items'][number]; const items: MenuItem[] = [ { key: '1', icon: <MailOutlined />, label: 'Navigation One', }, { key: '2', icon: <CalendarOutlined />, label: 'Navigation Two', }, { key: 'sub1', icon: <AppstoreOutlined />, label: 'Navigation Two', children: [ { key: '3', label: ( <Typography.Text ellipsis> Ant Design, a design language for background applications, is refined by Ant UED Team </Typography.Text> ), }, { key: '4', label: 'Option 4' }, { key: 'sub1-2', label: 'Submenu', children: [ { key: '5', label: 'Option 5' }, { key: '6', label: 'Option 6' }, ], }, ], }, { key: 'sub2', label: 'Navigation Three', icon: <SettingOutlined />, children: [ { label: 'Option 7', key: '7' }, { label: 'Option 8', key: '8' }, { label: 'Option 9', key: '9' }, { label: 'Option 10', key: '10' }, ], }, { key: 'link', icon: <LinkOutlined />, label: ( <a href="https://ant.design" target="_blank" rel="noopener noreferrer"> Ant Design </a> ), }, ]; const App: React.FC = () => { const [mode, setMode] = useState<'vertical' | 'inline'>('inline'); const changeMode = (value: boolean) => { setMode(value ? 'vertical' : 'inline'); }; return ( <> <Switch onChange={changeMode} /> Change Mode <br /> <br /> <ConfigProvider theme={{ components: { Menu: { itemBorderRadius: 0, subMenuItemBorderRadius: 0, itemHoverColor: '#1890ff', itemSelectedColor: '#1890ff', itemSelectedBg: '#e6f7ff', activeBarWidth: 3, itemMarginInline: 0, itemHoverBg: 'transparent', }, }, }} > <Menu style={{ width: 256 }} defaultSelectedKeys={['1']} defaultOpenKeys={['sub1']} mode={mode} items={items} /> </ConfigProvider> </> ); }; export default App;要点解读:
- 示例默认
mode="inline",通过Switch可在inline与vertical之间即时切换,用于观察两种布局下同一套 Token 的效果; defaultSelectedKeys={['1']}与defaultOpenKeys={['sub1']}用于指定初始选中项与展开的子菜单(对应 Menu API 文档 中的同名属性);items使用Required<MenuProps>['items'][number]推导类型,保证每个菜单项的结构合法;- 菜单项 label 中可以直接渲染
Typography.Text、<a>链接等任意 ReactNode,这也是 Menu items 泛型设计的典型用法。
八个 Token 逐一解析:V4 还原的关键配置
下表汇总了示例中 8 个 Token 的作用、示例取值,以及它们在当前仓库源码中的默认值(默认值取自 components/menu/style/index.ts 的prepareComponentToken):
| Token | 作用 | 示例取值 | 源码默认值 | 默认值来源 |
|---|---|---|---|---|
itemBorderRadius | 菜单项圆角 | 0 | borderRadiusLG | style/index.ts |
subMenuItemBorderRadius | 弹出子菜单内菜单项圆角 | 0 | borderRadiusSM | style/index.ts |
itemHoverColor | 菜单项 hover 文字颜色 | #1890ff | colorText | style/index.ts |
itemSelectedColor | 选中项文字颜色 | #1890ff | colorPrimary | style/index.ts |
itemSelectedBg | 选中项背景色 | #e6f7ff | controlItemBgActive | style/index.ts |
activeBarWidth | inline 模式选中指示条宽度 | 3 | 0(不显示指示条) | style/index.ts |
itemMarginInline | 菜单项横向外边距 | 0 | marginXXS | style/index.ts |
itemHoverBg | 菜单项 hover 背景色 | transparent | colorBgTextHover | style/index.ts |
可以发现,V4 还原的核心逻辑是"把 v5 引入的新视觉特征全部归零或替换":
- 圆角归零:
itemBorderRadius与subMenuItemBorderRadius置为0,恢复 V4 的直角菜单项(注意后者只作用于弹出层中的子菜单项,见 style/index.ts); - 交互色替换:
itemHoverColor、itemSelectedColor固定为 V4 主色#1890ff,不随colorPrimary主题变量漂移; - 选中态重塑:
itemSelectedBg使用 V4 经典的浅蓝#e6f7ff,itemHoverBg置为transparent去掉 v5 的 hover 灰底,只保留文字变蓝; - 指示条开启:
activeBarWidth: 3激活 inline 模式左侧竖条,itemMarginInline: 0让竖条与容器边缘对齐,形成 V4 的"贴边墨条"。
源码原理:这些 Token 如何驱动样式生成
activeBarWidth:inline 模式的左侧竖条
在 components/menu/style/theme.ts 中,inline 模式为每个-item生成了一个::after伪元素:
[`${componentCls}-item`]: { position: 'relative', '&::after': { position: 'absolute', insetBlock: 0, insetInlineEnd: 0, borderInlineEnd: `${unit(activeBarWidth)} solid ${itemSelectedColor}`, transform: 'scaleY(0.0001)', opacity: 0, ... }, },也就是说,竖条本质上是一条border-inline-end,宽度正是activeBarWidth,颜色跟随itemSelectedColor。默认未选中时通过scaleY(0.0001)与opacity: 0隐藏;选中时切换为scaleY(1)与opacity: 1,并配合motionDurationMid的过渡实现 V4 时代的"滑出"动效。示例将宽度设为3并令itemSelectedColor为#1890ff,即还原 V4 的经典蓝条。
值得注意的是activeBarWidth的默认值是0(见 style/index.ts),这正是 v5 默认不显示左侧指示条的原因——手动赋值为3是开启该特性的开关。
itemMarginInline:影响菜单项实际宽度
在 components/menu/style/vertical.ts 中,inline/vertical 模式下的菜单项宽度并非简单的100%,而是由itemWidth决定:
[`${componentCls}-item, ${componentCls}-submenu-title`]: { ... marginInline: itemMarginInline, width: itemWidth, },itemWidth的推导逻辑见 style/index.ts:
itemWidth: activeBarWidth ? `calc(100% + ${activeBarBorderWidth}px)` : `calc(100% - ${itemMarginInline * 2}px)`,- 当
activeBarWidth为0(v5 默认)时,菜单项宽度为100%减去两侧itemMarginInline,即默认marginXXS(4px)的左右留白; - 当启用指示条后,宽度变为
calc(100% + activeBarBorderWidth),配合itemMarginInline: 0,竖条能够紧贴容器右缘,实现 V4 的贴边效果。
这解释了为什么示例必须同时设置activeBarWidth: 3与itemMarginInline: 0:二者共同作用才能得到"容器内满宽 + 右缘竖条"的 V4 形态。
圆角与背景:作用于不同层级
itemBorderRadius直接作用于-item与-submenu-title(见 style/index.ts);subMenuItemBorderRadius只作用于弹出层(popup)中的菜单项(style/index.ts),所以示例将两者都设为0以保证所有层级的菜单项都是直角;itemHoverBg仅在非水平模式下、且未选中/未展开的项上生效(theme.ts),置为transparent后 hover 只剩文字变色,与 V4 行为一致;- 选中态背景
itemSelectedBg通过& ${componentCls}-item-selected的backgroundColor生效(theme.ts)。
注意事项与扩展建议
- 作用范围:Token 通过
ConfigProvider注入,会影响该 Provider 子树内所有Menu 实例(包括子菜单弹出层、以及 Layout Sider 等组合场景)。如需全局统一"类 v4"导航,可直接把这段theme.components.Menu配置放到应用根部的ConfigProvider;若只想影响局部,可像示例一样缩小 Provider 包裹范围。 - debug 标记:该示例在 Menu 文档 中被标记为 debug,属于"风格兼容演示"而非推荐默认样式,使用时请确认团队设计规范确实需要 V4 观感。
- 暗色主题:若同时使用
theme="dark"的 Menu,暗色 Token(如darkItemBg、darkItemSelectedBg等,见 style/index.ts)仍会接管颜色,上述亮色配置需另行针对暗色 Token 调整。 - 继续微调:在还原基础上可进一步调整
itemHeight(V4 更紧凑的 40px 高度)、itemPaddingInline、groupTitleColor等 Token(完整清单见 style/index.ts 的ComponentToken定义),即可得到自定义密度与配色;同一文件中的旧版 Token 别名(如colorItemTextSelected、radiusItem)已标记 deprecated,新代码应使用上述标准名称。 - 验证方式:该 demo 同时被快照测试覆盖(见
components/menu/__tests__/__snapshots__/demo.test.tsx.snap中的menu-v4.tsx用例),修改 Token 后可通过npm test运行组件测试确认样式输出无回归。
小结
V4 风格 Menu 的还原不需要任何 hack 或自定义 CSS:activeBarWidth、itemMarginInline、itemBorderRadius、itemSelectedBg等 8 个组件 Token 的组合,就能完整复刻"直角贴边 + 左侧蓝条 + 蓝色交互 + 浅蓝选中"的经典观感。理解 theme.ts 中指示条伪元素与itemWidth的推导逻辑(vertical.ts),还能让你在此基础上自由派生属于自己的菜单风格。
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考