logoAnt Design

⌘ K
  • 设计
  • 研发
  • 组件
  • 博客
  • 资源
  • 国内镜像
5.25.4
  • v6 的一些 CSS 琐事
  • 👀 视觉回归测试
  • 为什么禁用日期这么难?
  • 封装 Form.Item 实现数组转对象
  • 行省略计算
  • 📢 v4 维护周期截止
  • antd 里常用的 TypeScript 工具方法
  • 一个构建的幽灵
  • 当 Ant Design 遇上 CSS 变量
  • API 的历史债务
  • 灵动的 Notification
  • 色彩模型与颜色选择器
  • 主题拓展
  • 虚拟表格来了!
  • 快乐工作主题(一)
  • 动态样式去哪儿了?
  • Suspense 引发的样式丢失问题
  • 打包体积优化
  • 你好,GitHub Actions
  • 所见即所得
  • 静态方法之痛
  • SSR 静态样式导出
  • 依赖排查
  • 贡献者开发维护指南
  • 转载-如何提交无法解答的问题
  • 新的 Tooltip 对齐方式
  • 非必要的渲染
  • 如何成长为 Collaborator
  • Modal hook 的有趣 BUG
  • antd 测试库迁移的那些事儿
  • Tree 的勾选传导
  • getContainer 的一些变化
  • 组件级别的 CSS-in-JS
一个例子
Token 之痛
ConfigProvider
主题拓展
总结

主题拓展

2023-09-03
@zombieJ

文章被以下专栏收录:

antd

Ant Design

Juejin logoAnt Design 开源专栏
Juejin logo我有想法,去参与讨论
文档贡献者
  • 色彩模型与颜色选择器虚拟表格来了!

    相关资源

    Ant Design X
    Ant Design Charts
    Ant Design Pro
    Pro Components
    Ant Design Mobile
    Ant Design Mini
    Ant Design Web3
    Ant Design Landing-首页模板集
    Scaffolds-脚手架市场
    Umi-React 应用开发框架
    dumi-组件/文档研发工具
    qiankun-微前端框架
    Ant Motion-设计动效
    国内镜像站点 🇨🇳

    社区

    Awesome Ant Design
    Medium
    Twitter
    yuque logoAnt Design 语雀专栏
    Ant Design 知乎专栏
    体验科技专栏
    seeconf logoSEE Conf-蚂蚁体验科技大会
    加入我们

    帮助

    GitHub
    更新日志
    常见问题
    报告 Bug
    议题
    讨论区
    StackOverflow
    SegmentFault

    Ant XTech logo更多产品

    yuque logo语雀-构建你的数字花园
    AntV logoAntV-数据可视化解决方案
    Egg logoEgg-企业级 Node.js 框架
    Kitchen logoKitchen-Sketch 工具集
    Galacean logoGalacean-互动图形解决方案
    xtech logo蚂蚁体验科技
    主题编辑器
    Made with ❤ by
    蚂蚁集团和 Ant Design 开源社区

    Ant Design v5 提供了 Design Token 模型,支持自定义算法实现主题拓展能力。例如 紧凑主题 本身并不携带颜色样式算法,所以可以通过传入多个算法的方式实现 亮色主题下的紧凑主题 以及 暗色主题下的紧凑主题。

    而今天,我们现在放下算法部分。讲讲如何通过 ConfigProvider 来拓展主题。

    一个例子

    这是我通过 ConfigProvider 来拓展主题的示例,你可以直接在这里查看完整的代码(在线演示):

    Geek Theme

    以下会聊聊在 Ant Design 中如何使用 ConfigProvider 拓展主题。当然这篇文章并不是 CSS 的教程,所以不会去介绍上面的样式实现。如果有兴趣可以直接看看上面的代码地址。

    Token 之痛

    Design Token 提供了非常强大的拓展能力,但是同样它也有限制。例如当 Token 并没有支持某些配置时,开发者就变得无能为力了。更有甚者,某些主题实现不能单纯依赖某种 Token 就会变得十分困难。例如在上面例子中的各种渐变边框色不能简单的通过 border-color 来实现,它需要一些 CSS 小技巧。而如《快乐工作主题》我们提到,将一些具体实现落地到 Design Token 会使得代码质量迅速劣化。因而我们需要一些其他的方式来拓展主题,可以统一的修改某个组件的样式。而 ConfigProvider 就是这样的一个入口。

    ConfigProvider

    在 5.7.0 中,ConfigProvider 支持了所有组件的 className 和 style 配置。因此我们可以很容易进行 Token 之外的拓展:

    tsx
    <ConfigProvider
    button={{ className: 'my-button' }}
    checkbox={{ className: 'my-checkbox' }}
    divider={{ className: 'my-divider' }}
    />

    接着我们就可以去添加我们的样式了:

    less
    .my-button {
    background: red;
    }

    你会发现,这其实奇怪。既然我们可以通过 className 来修改样式,那么为什么还需要 ConfigProvider 呢?我们覆盖 .ant-btn 样式不就行了。

    如果你的项目只由你一个人来维护,这是个不错的主意。但是如果你的项目是一个大型项目,那么你就会发现这样的做法会导致样式冲突。尤其在多人协作的情况下,随意修改样式会出现非预期的结果,而其他人为了覆盖你的样式不得不使用更加复杂的选择器。而 ConfigProvider 则可以很好的解决这个问题,它可以将样式隔离在 ConfigProvider 内部,不会影响到其他组件。

    主题拓展

    上面的示例看起来实现很容易,但是真实场景下你会发现对于层级结构而言不免也有一些不足。比如说 ant- 前缀可以通过 ConfigProvider 的 prefixCls 修改,所以语义化结构的前缀可能从 ant-btn-icon 变成 abc-btn-icon。那么仅通过 my-button 是不足以实现覆盖的:

    less
    .my-button {
    // OPS. It's `abc-btn-icon` now.
    .ant-btn-icon {
    background: red;
    }
    }

    所以我们的拓展主题也同样需要能够消费 prefixCls 的能力。而在 CSS-in-JS 中,混合 prefixCls 是很容易的事情。我们可以通过 ConfigProvider 的 getPrefixCls 方法来获取 prefixCls,然后进行混合:

    tsx
    // This is an example of using `antd-style`, you can use any CSS-in-JS library.
    import React from 'react';
    import { ConfigProvider } from 'antd';
    import { createStyles } from 'antd-style';
    const useButtonStyle = createStyles(({ css }, prefixCls: string) => {
    return {
    btn: css`
    background: red;
    .${prefixCls}-icon {
    color: green;
    }
    `,
    };
    });
    const GeekProvider: React.FC<Readonly<React.PropsWithChildren>> = (props) => {
    const { getPrefixCls } = React.useContext(ConfigProvider.ConfigContext);
    const btnPrefixCls = getPrefixCls('btn');
    const { styles } = useButtonStyle(btnPrefixCls);
    return <ConfigProvider button={{ className: styles.btn }}>{props.children}</ConfigProvider>;
    };
    export default GeekProvider;
    Red Button

    对需要继承 className 的场景,拓展也很容易:

    tsx
    import React from 'react';
    import { ConfigProvider } from 'antd';
    import classNames from 'classnames';
    const GeekProvider: React.FC<Readonly<React.PropsWithChildren>> = (props) => {
    const { button, getPrefixCls } = React.useContext(ConfigProvider.ConfigContext);
    const btnPrefixCls = getPrefixCls('btn');
    const { styles } = useButtonStyle(btnPrefixCls);
    return (
    <ConfigProvider button={{ className: classNames(button?.className, styles.btn) }}>
    {props.children}
    </ConfigProvider>
    );
    };
    export default GeekProvider;

    总结

    通过 ConfigProvider 可以进一步拓展主题,它可以很好的隔离样式,避免样式冲突。赶快动手试试吧!