聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活

lxf2023-03-14 12:18:01

本文作者是蚂蚁集团体验设计师闻冰(社区称呼:空谷),本文受众同时是设计师与前端。对于设计师来说,这篇文章最大的意义在于可以知道用了 antd V5 ,后续可以怎么用标准化的方案达成自定义主题的诉求。而对于前端来说,这篇文章能让你知道 V5 的应用上限在哪里。

Ant Design V5 已经发布也有一段时间了,作为 V5 设计研发小组成员,在这几个月中我们也第一时间升级了手上的业务应用到 antd v5,并针对实际的业务场景研究了 antd 动态主题的实践用法。截至目前近 3 个月的时间,我们总共完成了 6 个应用/组件/站点的 antd v5 升级改造,且均已支持亮暗色主题切换。并在一些对主题自定义有较强需求的场景下都有了不错的效果(如下):

亮色主题暗色主题
Kitchen3 插件
@闻冰 / 业务应用聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活
Kitchen Measure
@百音 @闻冰 / 业务应用聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活
Kitchen 官网
@倏昱 / 业务应用聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活
ProComponents
@期贤 / TechUI Pro系组件聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活
ProEditor 编辑器
@兼续 / 业务组件聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活
Ant Design Style 文档
@闻冰 / 文档应用聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活

我个人感受的就是:用了 CSSinJS 之后的 antd 太香了! 正是有了这么多案例的应用验证,我才有信心写下这篇文章,和大家聊聊用 antd v5 可以在主题方面做出什么样的行活与花活。在可以预见的未来,所有动态主题的需求,无论是行活还是花活,我们都能做的轻轻松松~ 聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活

PS:所谓的行活用法,就是无论设计师还是前端同学都可直接体验和使用;而花活用法,就是目前只有用写代码方式才能实现的方案。(不过其中有些方案可能会在未来给到设计师使用)


行活用法 —— V4 的延展与产品化

由于 antd v5 核心采用了 CssinJS 的方案,因此所有的动态主题配置也都变成了代码里的运行时配置,且从单一的动态主色变成了圆角、字体、阴影等几乎所有样式变量。针对前端同学来说,在 Ant Design 官网的文档( )里也详细展示了基础的用法,我在这里就不赘述了。主要和大家聊聊 V5 里的产品化用法。

通过 Ant Design 的主题编辑器,设计系统的创建者可以非常简单地配出来 antd 的整体风格,并进行实时预览。 聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活然后设计师将主题导入到 Kitchen 之后,设计师便可以直接消费主题 Token,同时也可以拖拽获得自定义主题后的 antd 组件。

聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活
聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活

交付设计稿后,基于 C2D2C 的链路,前端同学就能看到设计师使用了哪个 antd 组件,并一键获得 antd 的前端代码。 那如果大家对这部分内容感兴趣,欢迎在这里查看详情: Kitchen v3.2.0 Ant Design V5 动态主题资产来啦~ · 语雀


接下来就全部都是 antd 的花活用法了,由于以下的花活部分目前暂时都只能通过代码实现(部分能力后续可能也会产品化),所以介绍里会涉及到一些代码,但相对来说也还简单。

花活用法 ❶:自定义主题算法

antd 的 token 体系和市面上大部分的 token 体系不同,我们有一个非常重要的因素,它就是——算法。虽然一听算法好像挺高大上的,但它的思想是非常容易理解的:基于基础变量和派生规则来生成一组变量。其中派生规则就是算法。antd 在v4及以前一直有这方面的沉淀,在 v5 中自然把这个能力继承了过来。 聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活 由于算法的存在,我们就可以传入不同的主题算法,进而获得不同的主题风格效果。例如 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 自定义,就可以实现风格感受很不一样的主题,同时界面的视觉梯度仍然可以保持稳定,不会出现常见的暗色模式翻车的问题。

聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活

而且通过传入不同的主题算法,我们甚至可以控制组件在不同应用场景下的展示形态。例如在Kitchen中为了让 智能表格编辑器符合 kitchen 的视觉风格,我们通过传入 kitchen 风的 antd 自定义主题算法,就可以让 ProEditor 在 kitchen 中变成另外一个风格。

聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活

所以单纯一个 v5 的自定义算法,就可以玩出很多花活,而我们后续也会结合 Kitchen Color Studio 色彩生成工具,在 Ant Design 的主题编辑器中集成可供设计师使用的自定义算法功能,让不懂代码的设计师也能轻松生成业务定制的色彩算法。 聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活

花活用法 ❷:局部主题自定义

自定义主题算法是一个全局风格的调整,接来下再来介绍局部主题的使用。在 v4 及以前,想要 antd 的组件有多套主题同屏的实现难度是非常高的,而在 V5 中,得益于 CSSinJS 的动态主题,多套主题模式同屏展示就变得非常简单。 这在我们的 ProEditor 编辑器、TechUI Studio 平台、Ant Design 的主题预览器都有使用到这些效果。

聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活

以 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 样式的情况下做到风格兼容。 聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活

花活用法 ❸:组件级风格自定义

当大家对主题能力切换有了基础的感知之后,我们再来看看组件级别的主题风格定制能力。在 antd v4 及以前,自定义一个 popup 的提示组件是非常难的。 下面则演示了一个需要自定义为主题色的 popup 提示说明(代码示例:codesandbox.io/s/popover-i…)为了达成较好的视觉效果,样式覆写可能会比组件的声明都要多,但仍然难以达到完美状态。 所以 antd v4 才会经常被诟病说很难做自定义。 聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活

那在 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>
  );
}

产出的效果如下(示例代码:codesandbox.io/s/v5-popove…): 聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活

基于 CSSinJS 这样的动态能力,我们可以实现在组件样式组合上非常高的灵活度,进而轻松实现一些原本在 V4 中很难达成的自定义样式。

花活用法 ❹:组件的搭配组合

在 antd v4 中,之前被诟病的另一点就是组件的组合性不理想,比如一个典型场景是暗色模式下的弹窗与表格组合使用。可以看到由于暗色模式下 Modal 的底色与页面基础的 Layout 底色不同,最终呈现的感觉就是 table “陷”进去了一层,看起来就会很奇怪。

聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活

但这种场景在 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 背景色的表格样式。 聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活 同时,利用一些颜色计算库,我们甚至可以非常轻松地调整出比 Modal 的背景色更加突出的效果。这样的操作,在 v4 中几乎是不敢想的。 聊聊 Ant Design V5 的主题(上):CSSinJS 动态主题的花活 而它的代码,只需简单地微调即可。

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 类、组件级 Token、Stylish、主题编辑器 2.0 、antd 应用级 CSSinJS 方案等等…当然,整条产研消费链路中组件库只是其中一环,上下游的协同也非常重要,但在这里就不多展开了。

既然这篇文章的标题起的是(上),那么势必还会有个(下),那在下一篇中将会和大家聊聊在实际应用中,我们应该如何用“工程化”的方式接入 antd v5 的这些特性,并将上面提到的诸多花活统统收到囊中。

关于 V5 Token 体系的详细介绍,后续会作为独立的系列更新,并在完善后同步到官网,敬请期待~