[RFC] Use design tokens of Ant Design v5 to customize component
See original GitHub issueSummary
As we all know that Ant Design v5 is using CSS-in-JS to generate style now, and a lot of tokens which is used for generating themes are also introduced in v5. We would provide hooks like useToken
and useStyle
for users to customize components with tokens and CSS-in-JS used in Ant Design.
Motivation
For users who want to use Ant Design to do more design, as well as third-party libraries based on Ant Design, directly using Ant Design’s CSS-in-JS solution and being able to use Design Token directly will allow them to follow Ant Design’s design system, getting a consistent experience with Ant Design.
Design Token is a wonderful design, we implemented a flexible theme system with Design Token. But this is not limited to Ant Design itself, users should also benefit from it, and integrate custom tokens to achieve more cool designs and themes.
Therefore Ant Design v5 will provide two complementary capabilities:
- Directly use Ant Design’s CSS-in-JS solution;
- Use and expand Ant Design’s Design Token to gain more flexible theme customization capabilities.
对于想用 Ant Design 做更多设计的用户,以及基于 Ant Design 的三方库来说,直接使用 Ant Design 的 CSS-in-JS 方案,并且能够直接使用 Design Token 能够让他们继续沿用 Ant Design 的设计系统,获得与 Ant Design 一致的体验。
Design Token 是一个绝妙的设计,我们利用 Design Token 实现了灵活的主题系统。但这并不会局限于 Ant Design 本身,用户也应该从中获利,融合自定义的 token 以实现更加炫酷的设计和主题。
因此 Ant Design v5 会提供两种相辅相成的能力:
- 直接使用 Ant Design 的 CSS-in-JS 方案;
- 使用并拓展 Ant Design 的 Design Token,获得更灵活的主题定制能力。
API
There are two hooks planed:
useCustomToken
: used to inject custom tokens other than tokens provided by Ant Design.useStyleRegister
: used to generate styles with tokens and CSS-in-JS.
Basic Example
Use CSS-in-JS and token provided by Ant Design
const MyBtn = () => {
useStyleRegister('MyBtn', (token) => ({
'.my-btn': {
color: token.colorPrimary,
}
}));
return <button className="my-btn">My Button</button>;
}
Use customized token
Customized token can be injected in this way.
// Custom useToken
const useMyToken = () =>
useCustomToken({
seedToken: { colorSpecial: 'red' },
formatToken: token => ({
...token,
colorSpecialActive: 'purple',
}),
});
// Custom useStyleRegister
const useMyStyle = (componentName: string, styleFn: (token) => CSSObject) => {
const token = useMyToken();
return useStyleRegister(componentName, token, styleFn);
}
// And use it in your component
const MyBtn = () => {
useMyStyle('MyBtn', (token) => ({
'.my-btn': {
color: token.colorSpecial,
}
}));
return <button className="my-btn">My Button</button>;
}
Detailed Design
At the beginning, a solution with a higher degree of encapsulation was envisaged. The above APIs were curried, and allow users to divide into four steps when they want to expand the Design Token:
- Write
useMyToken
based onuseCustomToken
; - Register
useMyToken
to theregisterToken
method and return agenStyleHook
method; - Use
genStyleHook
, pass in token-basedstyleFn
, write CSS-in-JS, and returnuseStyle
hook; - Use the
useStyle
hook in component.
The features of this solution:
- High degree of encapsulation, except for one-time configuration code, what users actually need to put into use is the
genStyleHook
method; - The parameter list is concise;
- The configuration process is more complicated and the learning cost is high;
- Requires writing configuration code outside of the React lifecycle.
In order to remain the flexibility of CSS-in-JS and Design Token, the solution described above was finally decided, the registerToken
layer of encapsulation was removed, and the user’s usage steps were simplified as follows:
- Write
useMyToken
based onuseCustomToken
; - Write
useMyStyle
based onuseStyleRegister
; - Use
useMyStyle
in the component.
If you want to directly use the Design Token preset by Ant Design, the first two steps can be omitted. Of course, users can also curry useMyStyle
by themselves to reduce the burden on components.
一开始设想过封装度更高的方案,将上述这些 API 柯里化,让用户想要拓展 Design Token 时分为四步:
- 基于
useCustomToken
编写useMyToken
; - 将
useMyToken
注册到registerToken
方法中,返回一个genStyleHook
方法; - 使用
genStyleHook
,传入基于 token 的styleFn
,编写 CSS-in-JS,返回useStyle
hook; - 在组件中使用
useStyle
hook。
这个方案的特点是:
- 封装度高,除去一次性的配置代码,用户实际上需要大量投入使用的就是
genStyleHook
方法; - 参数列表简洁;
- 配置流程比较复杂,学习成本较高;
- 需要在 React 生命周期外写配置代码。
为了保持 CSS-in-JS 和 Design Token 的灵活度,最终还是决定了上文中所述的方案,去掉了 registerToken
这一层封装,将用户的使用步骤简化为:
- 基于
useCustomToken
编写useMyToken
; - 基于
useStyleRegister
编写useMyStyle
; - 在组件中使用
useMyStyle
.
如果想要直接使用 Ant Design 预设的 Design Token,则前两步可以省略。当然用户也可以自行将 useMyStyle
柯里化,给组件减负。
Drawbacks
No obvious drawbacks yet.
Issue Analytics
- State:
- Created a year ago
- Comments:7 (6 by maintainers)
Top GitHub Comments
专门搞个板块放 rfcs 如何?https://github.com/ant-design/ant-design/discussions
核心要解 v4升v5 的方案吧。我之前 YY 过几种方案:
基础:原有的 V4 CSS Modules
大部分业务都会这么去用
1. V5 CSS Modules
此方案不改动原来的任何样式结构,只是将 v4 的 less变量,改为v5的 token(用css variable 的模式)
2. V5 CSS IN JS 对象模式
这种JS对象的模式应该是现在 antd 内部组件库的使用方式。但我觉得业务里不会想这么去写的。有点别扭
3. V5 CSS IN JS 字符串模式
这是个人最期望的模式。基本上不会太改变原有的写法。
4. V5 CSS in JS(纯消费 Token 的方案)
这个是 期贤 说他在自己实践的方案。只要antd 给 token 就好了,从灵活性上来说是不错的。但旧业务升级新业务,采用这种方式要全部重构,成本会非常高。
简单评估了下,供参考:
最推荐的方案确定以后,antd token 在设计侧的消费模式才能确定下来。(即在 kitchen 里提供给开发 token 的代码模式)