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

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

本文作者是蚂蚁集团体验设计师闻冰(社区称呼:空谷),本文的受众同时是设计师与前端。对于设计师来说,这篇文章最大的意义在于可以知道用了 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 体系的详细介绍,后续会作为独立的系列更新,并在完善后同步到官网,敬请期待~

相关文章
|
5月前
Nuxt3 实战 (八):优雅的实现暗黑主题模式
这篇文章介绍了在Nuxt3中实现暗黑模式的过程。首先推荐使用color-mode库来轻易实现暗黑模式切换,并通过pnpm命令安装@nuxtjs/color-mode依赖。然后在nuxt.config.ts配置文件中注入依赖,并根据项目实际情况自定义配置。接着通过toggleDark函数实现切换动画,并在需要的地方加载组件<ColorMode/>,从而达到最终效果。
148 3
Nuxt3 实战 (八):优雅的实现暗黑主题模式
|
API
【Ant Design Pro】使用ant design pro做为你的开发模板(九)开发第一个完整的后台页面(二)
【Ant Design Pro】使用ant design pro做为你的开发模板(九)开发第一个完整的后台页面(二)
844 0
【Ant Design Pro】使用ant design pro做为你的开发模板(九)开发第一个完整的后台页面(二)
|
IDE 安全 程序员
IDE暗黑主题推荐-Dracula
Dracula 为 JetBrains IDE 带来了出色的高对比度黑暗风格,本文向大家推荐了这款插件的优秀之处。如果你还没有尝试过这款插件,不妨让 Dracula 来点缀你的 IDE,体验它的魅力所在吧。
135 2
|
6月前
|
JavaScript
Vuetify 设置主题
Vuetify 设置主题
108 0
Ant Design Pro 修改主题设置
Ant Design Pro 修改主题设置
334 0
|
前端开发 CDN
ant-design实现主题暗黑主题 和 亮色主题的 切换(实现网站黑白皮肤)
最近在使用vite+react + ant-design 来搭建个人站点,看到网上好多网站都实现了黑白皮肤的切换,并且ant-design帮我们实现了三套主题色,一个默认亮白色,暗黑主题和紧凑主题。于是我也想来弄一弄。最后还是实现了,打包后也是ok的。
ant-design实现主题暗黑主题 和 亮色主题的 切换(实现网站黑白皮肤)
|
自然语言处理 前端开发 JavaScript
【Ant Design Pro】使用ant design pro做为你的开发模板(二)新增一个页面与如何添加国际化
【Ant Design Pro】使用ant design pro做为你的开发模板(二)新增一个页面与如何添加国际化
833 0
【Ant Design Pro】使用ant design pro做为你的开发模板(二)新增一个页面与如何添加国际化
|
前端开发 JavaScript
Yii2的主题是干什么的?有什么作用?为什么需要使用主题?
Yii2的主题是干什么的?有什么作用?为什么需要使用主题?
|
前端开发
【Ant Design Pro】使用ant design pro做为你的开发模板(八)开发第一个完整的后台页面
【Ant Design Pro】使用ant design pro做为你的开发模板(八)开发第一个完整的后台页面
667 0
【Ant Design Pro】使用ant design pro做为你的开发模板(八)开发第一个完整的后台页面
|
资源调度
ant-design-vue运行时动态切换主题色(2)
ant-design-vue运行时动态切换主题色(2)
632 0
ant-design-vue运行时动态切换主题色(2)