本文作者是蚂蚁集团体验设计师闻冰(社区称呼:空谷),本文的受众同时是设计师与前端。对于设计师来说,这篇文章最大的意义在于可以知道用了 antd V5 ,后续可以怎么用标准化的方案达成自定义主题的诉求。而对于前端来说,这篇文章能让你知道 V5 的应用上限在哪里。
Ant Design V5 已经发布也有一段时间了,作为 V5 设计研发小组成员,在这几个月中我们也第一时间升级了手上的业务应用到 antd v5,并针对实际的业务场景研究了 antd 动态主题的实践用法。截至目前近 3 个月的时间,我们总共完成了 6 个应用/组件/站点的 antd v5 升级改造,且均已支持亮暗色主题切换。并在一些对主题自定义有较强需求的场景下都有了不错的效果(如下):
亮色主题 |
暗色主题 |
|
Kitchen3 插件 @闻冰 / 业务应用 |
||
Kitchen Measure @百音 @闻冰 / 业务应用 |
||
Kitchen 官网 @倏昱 / 业务应用 |
||
ProComponents @期贤 / TechUI Pro系组件 |
||
ProEditor 编辑器 @兼续 / 业务组件 |
||
Ant Design Style 文档 @闻冰 / 文档应用 |
我个人感受的就是:用了 CSSinJS 之后的 antd 太香了!正是有了这么多案例的应用验证,我才有信心写下这篇文章,和大家聊聊用 antd v5 可以在主题方面做出什么样的行活与花活。在可以预见的未来,所有动态主题的需求,无论是行活还是花活,我们都能做的轻轻松松~
PS:所谓的行活用法,就是无论设计师还是前端同学都可直接体验和使用;而花活用法,就是目前只有用写代码的方式才能实现的方案。(不过其中有些方案可能会在未来给到设计师使用)
行活用法 —— V4 的延展与产品化
由于 antd v5 核心采用了 CssinJS 的方案,因此所有的动态主题配置也都变成了代码里的运行时配置,且从单一的动态主色变成了圆角、字体、阴影等几乎所有样式变量。针对前端同学来说,在 Ant Design 官网的文档( https://ant-design.antgroup.com/docs/react/customize-theme-cn )里也详细展示了基础的用法,我在这里就不赘述了。主要和大家聊聊 V5 里的产品化用法。
通过 Ant Design 的主题编辑器,设计系统的创建者可以非常简单地配出来 antd 的整体风格,并进行实时预览。
主题编辑&预览
然后设计师将主题导入到 Kitchen 之后,设计师便可以直接消费主题 Token,同时也可以拖拽获得自定义主题后的 antd 组件。
消费自定义主题后的 antd 组件 |
消费自定义主题的 token 变量 |
交付设计稿后,基于 C2D2C 的链路,前端同学就能看到设计师使用了哪个 antd 组件,并一键获得 antd 的前端代码。那如果大家对这部分内容感兴趣,欢迎在这里查看详情:https://www.yuque.com/kitchen/changelog/3.2.0
接下来就全部都是 antd 的花活用法了,由于以下的花活部分目前暂时都只能通过代码实现(部分能力后续可能也会产品化),所以介绍里会涉及到一些代码,但相对来说也还简单。
花活用法 ❶:自定义主题算法
antd 的 token 体系和市面上大部分的 token 体系不同,我们有一个非常重要的因素,它就是——算法。虽然一听算法好像挺高大上的,但它的思想是非常容易理解的:基于基础变量和派生规则来生成一组变量。其中派生规则就是算法。antd 在v4及以前一直有这方面的沉淀,在 v5 中自然把这个能力继承了过来。由于算法的存在,我们就可以传入不同的主题算法,进而获得不同的主题风格效果。例如 TechUI Studio 中,我们通过集成自定义暗色算法,就可以轻松获得独特的暗色风格的主题。
import { theme } from 'antd'; import type { MappingAlgorithm } from 'antd/es/config-provider/context'; // 定义 studio 暗色模式算法 export const studioDarkAlgorithm: MappingAlgorithm = (seedToken, mapToken) => { // 使用 antd 默认的暗色算法生成基础token,这样其他不需要定制的部分则保持原样 const baseToken = theme.darkAlgorithm(seedToken, mapToken); return { ...baseToken, colorBgLayout: '#20252b', // Layout 背景色 colorBgContainer: '#282c34', // 组件容器背景色 colorBgElevated: '#32363e', // 悬浮容器背景色 }; }; // 在应用中集成 const Container =()=>{ return ( <ConfigProvider theme={{ algorithm: studioDarkAlgorithm }}> ... </ConfigProvider> ) }
最终的效果如下图所示。可以看到我们通过集成主题算法,通过非常少的 token 自定义,就可以实现风格感受很不一样的主题,同时界面的视觉梯度仍然可以保持稳定,不会出现常见的暗色模式翻车的问题。
antd 默认亮色主题 |
antd 默认的暗色主题 |
自定义算法后的暗色主题 |
而且通过传入不同的主题算法,我们甚至可以控制组件在不同应用场景下的展示形态。例如在Kitchen中为了让 智能表格编辑器符合 kitchen 的视觉风格,我们通过传入 kitchen 风的 antd 自定义主题算法,就可以让 ProEditor 在 kitchen 中变成另外一个风格。
在TechUI Studio 平台的暗色模式 |
集成到 Kitchen 智能表格中的暗色模式 |
所以单纯一个 v5 的自定义算法,就可以玩出很多花活,而我们后续也会结合 Kitchen Color Studio 色彩生成工具,在 Ant Design 的主题编辑器中集成可供设计师使用的自定义算法功能,让不懂代码的设计师也能轻松生成业务定制的色彩算法。
花活用法 ❷:局部主题自定义
自定义主题算法是一个全局风格的调整,接来下再来介绍局部主题的使用。在 v4 及以前,想要 antd 的组件有多套主题同屏的实现难度是非常高的,而在 V5 中,得益于 CSSinJS 的动态主题,多套主题模式同屏展示就变得非常简单。这在我们的 ProEditor 编辑器、TechUI Studio 平台、Ant Design 的主题预览器都有使用到这些效果。
ProEditor:编辑器框架为暗色,编辑中的 ProTable 组件是亮色 |
Studio 平台:顶部导航栏为暗色、编辑器为亮色 |
主题预览器:预览器框架为亮色,显示 Demo 同时存在亮色与暗色 |
以 Studio 平台的场景为例,这种局部主题设定的核心代码如下:
import { ConfigProvider ,theme } from 'antd'; export default () => { return ( <div> {/* 暗色模式只需套一个 CP 并设定算法,里面的 Toolbar 就是暗色的了 */ } <ConfigProvider theme={{ algorithm: theme.darkAlgorithm }}> <Toolbar /> </ConfigProvider> {/* 其余部分默认是亮色 */ } <ProTableEditor style={{ height: 'calc(100vh - 40px)' }} /> </div> ); };
这样局部主题的定制可以极大程度地提升样式定制的灵活度。像暗色主题的一个泛化是『深色主题』,比较典型的就是类似海兔这样的深色背景头图场景。基于上面写的用法示例,理论上只需做一个「深色主题」算法,就可以实现在不魔改 antd 样式的情况下做到风格兼容。
花活用法 ❸:组件级风格自定义
当大家对主题能力切换有了基础的感知之后,我们再来看看组件级别的主题风格定制能力。在 antd v4 及以前,自定义一个 popup 的提示组件是非常难的。下面则演示了一个需要自定义为主题色的 popup 提示说明(代码示例:https://codesandbox.io/s/popover-inverse-c2l3w6)为了达成较好的视觉效果,样式覆写可能会比组件的声明都要多,但仍然难以达到完美状态。所以 antd v4 才会经常被诟病说很难做自定义。
那在 V5 中,使用 ConfigProvider 可以非常简单地完成样式的自定义,且非常符合直觉。
import { theme, Popover, Checkbox, Button, ConfigProvider } from 'antd'; export default function App() { const { token } = theme.useToken(); return ( <ConfigProvider theme={{ // component 字段可以聚合调整每个组件的 token components: { // 将 Popover 的文本颜色设为白色 Popover: { colorText: token.colorTextLightSolid }, // 将 Checkbox 的文本颜色设为白色,主色设为更强一级的颜色 Checkbox: { colorPrimary: token['blue-7'], colorText: token.colorTextLightSolid }, // 将 Button 的颜色设为更强一级的颜色 Button: { colorPrimary: token['blue-7'] } } }} > ... 业务组件代码 </ConfigProvider> ); }
产出的效果如下(示例代码:https://codesandbox.io/s/v5-popover-inverse-ftcweh?file=/src/App.tsx):
基于 CSSinJS 这样的动态能力,我们可以实现在组件样式组合上非常高的灵活度,进而轻松实现一些原本在 V4 中很难达成的自定义样式。
花活用法 ❹:组件的搭配组合
在 antd v4 中,之前被诟病的另一点就是组件的组合性不理想,比如一个典型场景是暗色模式下的弹窗与表格组合使用。可以看到由于暗色模式下 Modal 的底色与页面基础的 Layout 底色不同,最终呈现的感觉就是 table “陷”进去了一层,看起来就会很奇怪。
普通容器中的表格 |
在弹窗中的表格 |
但这种场景在 antd 层面往往无能为力,因为组件本身并不限制业务应用如何使用组件。最后的结果就是应用中的体验细节有瑕疵。如果修正这些瑕疵,在 V4 中可能需要做很多 hack 才能实现,ROI 划不来。
而在 V5 中则可以非常轻松地达到预期的效果。当然,也是通过 ConfigProvider 的嵌套特性达成。
import React from 'react'; import { Modal, ConfigProvider, theme } from 'antd'; import Table from './Table'; const App: React.FC = () => { const { token } = theme.useToken(); return ( <div style={{ background: token.colorBgLayout, padding: 24, height: '100vh' }} > <Modal open={true} width={800} title={'在Modal中的表格'}> <ConfigProvider theme={{ token: { colorBgContainer: token.colorBgElevated } }} > <Table /> </ConfigProvider> </Modal> <Table /> </div> ); }; export default App;
只需在 Modal 中的 table 外层嵌套一个 ConfigProvider 作为夹心层,然后将 token 的 colorBgContainer
参数设为 colorBgElevated
,我们就得到了响应 Modal 背景色的表格样式。
同时,利用一些颜色计算库,我们甚至可以非常轻松地调整出比 Modal 的背景色更加突出的效果。这样的操作,在 v4 中几乎是不敢想的。
demo 地址:https://codesandbox.io/s/ji-ben-yong-fa-antd-5-1-6-forked-bp2zqj?file=/demo.tsx
而它的代码,只需简单地微调即可。
import React from 'react'; import { Modal, ConfigProvider, theme } from 'antd'; import Table from './Table'; import { lighten } from 'polished'; const App: React.FC = () => { const { token } = theme.useToken(); return ( <div style={{ background: token.colorBgLayout, padding: 24, height: '100vh' }} > <Modal open={true} width={800} title={'在Modal中的表格'}> <ConfigProvider theme={{ // 把 colorBgElevated 的颜色提亮 4% 作为容器基础色 token: { colorBgContainer: lighten(0.04, token.colorBgElevated) } }} > <Table /> </ConfigProvider> </Modal> <Table /> </div> ); }; export default App;
花活总结:v5 的起点将会是其他组件库的天花板
在这么几个月的探索使用中,我愈发感觉 V5 的 CSSinJS 动态能力,搭配我们独一无二的 Token 体系,放眼全球都是极其领先的存在。尝过这些能力的甜头之后,我甚至很笃定地认为未来就是 CSSinJS 的天下。
借用云谦老师的话:「选择很重要,有些方案的起点可能就是另一些方案的天花板」。而上文所提到的行活与花活,也只是 V5 的起点,灵活性课题在 CY23 还会进一步延展下去:语义化组件 DOM 类(https://github.com/ant-design/ant-design/discussions/40221)、组件级 Token(https://github.com/ant-design/ant-design/issues/38975)、Stylish、主题编辑器 2.0 、antd 应用级 CSSinJS 方案等等…当然,整条产研消费链路中组件库只是其中一环,上下游的协同也非常重要,但在这里就不多展开了。
既然这篇文章的标题起的是(上),那么势必还会有个(下),那在下一篇中将会和大家聊聊在实际应用中,我们应该如何用“工程化”的方式接入 antd v5 的这些特性,并将上面提到的诸多花活统统收到囊中。
关于 V5 Token 体系的详细介绍,后续会作为独立的系列更新,并在完善后同步到官网,敬请期待~