* fix: fix theme-editor link * Update docs/react/customize-theme.zh-CN.md Co-authored-by: afc163 <afc163@gmail.com> * Update customize-theme.en-US.md Co-authored-by: afc163 <afc163@gmail.com>
11 KiB
order | title |
---|---|
7 | Customize Theme |
Ant Design allows you to customize our design tokens to satisfy UI diversity from business or brand requirements, including primary color, border radius, border color, etc.
In version 5.0, we provide a new way to customize themes. Different from the less and CSS variables of the 4.x version, with CSS-in-JS, the ability of theming has also been enhanced, including but not limited to:
- Switching theme dynamically;
- Multiple themes;
- Customizing theme variables for some component;
- ...
Customize theme with ConfigProvider
In version 5.0 we call the smallest element that affects the theme Design Token. By modifying the Design Token, we can present various themes or components.
Customize Design Token
You can pass theme
to ConfigProvider to customize theme. After migrate to V5, theme of V5 will be applied by default. Here's a simple example:
import React from 'react';
import { ConfigProvider, Button } from 'antd';
const App: React.FC = () => (
<ConfigProvider
theme={{
token: {
colorPrimary: '#00b96b',
},
}}
>
<Button />
</ConfigProvider>
);
export default App;
You will get a theme with primary color
#00b96b
. And we can see the change in Button:
Use Preset Algorithms
Themes with different styles can be quickly generated by modifying algorithm
. Ant Design 5.0 provides three sets of preset algorithms by default, which are default algorithm theme.defaultAlgorithm
, dark algorithm theme.darkAlgorithm
and compact algorithm theme.compactAlgorithm
. You can switch algorithms by modifying the algorithm
property of theme
in ConfigProvider.
import React from 'react';
import { ConfigProvider, Button, theme } from 'antd';
const App: React.FC = () => (
<ConfigProvider
theme={{
algorithm: theme.darkAlgorithm,
}}
>
<Button />
</ConfigProvider>
);
export default App;
Customize Component Token
In addition to Design Token, each component will also have its own Component Token to achieve style customization capabilities for components, and different components will not affect each other. Similarly, other Design Token of components can also be overridden in this way.
import React from 'react';
import { ConfigProvider, Radio, Checkbox } from 'antd';
const App: React.FC = () => (
<ConfigProvider
theme={{
components: {
Radio: {
colorPrimary: '#00b96b',
},
},
}}
>
<Radio>Radio</Radio>
<Checkbox>Checkbox</Checkbox>
</ConfigProvider>
);
export default App;
In this way, we changed the primary color of Radio to
#00b96b
, and Checkbox is not affected.
Other Ways to Use Dynamic Themes
Switch Themes Dynamically
In v5, dynamically switching themes is very simple for users, you can dynamically switch themes at any time through the theme
property of ConfigProvider
without any additional configuration.
Local Theme
By nesting ConfigProvider
you can apply local theme to some parts of your page. Design Tokens that have not been changed in the child theme will inherit the parent theme.
import React from 'react';
import { ConfigProvider, Button } from 'antd';
const App: React.FC = () => (
<ConfigProvider
theme={{
token: {
colorPrimary: '#1677ff',
},
}}
>
<Button />
<ConfigProvider
theme={{
token: {
colorPrimary: '#1890ff',
},
}}
>
<Button />
</ConfigProvider>
</ConfigProvider>
);
export default App;
Consume Design Token
If you want to consume the Design Token under the current theme, we provide useToken
hook to get Design Token.
import React from 'react';
import { Button, theme } from 'antd';
const { useToken } = theme;
const App: React.FC = () => {
const { token } = useToken();
return <Button style={{ backgroundColor: token.colorPrimary }}>Button</Button>;
};
export default App;
Static consume (e.g. less)
When you need token out of React life cycle, you can use static function to get them:
import { theme } from 'antd';
const { defaultAlgorithm, defaultSeed } = theme;
const mapToken = defaultAlgorithm(defaultSeed);
If you want to use in preprocess style framework like less, use less-loader for injection:
{
loader: "less-loader",
options: {
lessOptions: {
modifyVars: mapToken,
},
},
}
Compatible package provide convert function to transform to v4 less variable. Read this for detail.
Advanced
In Design Token, we provide a three-layer structure that is more suitable for the design, and disassemble the Design Token into three parts: Seed Token, Map Token and Alias Token. These three groups of Tokens are not simple groupings, but a three-layer derivation relationship. Map Tokens are derived from Seed Tokens, and Alias Tokens are derived from Map Tokens. In most cases, using Seed Tokens is sufficient for custom themes. But if you need a higher degree of theme customization, you need to understand the life cycle of Design Token in antd.
Life Cycle of Design Token
Seed Token
Seed Token means the origin of all design intent. For example, we can change the theme color by changing colorPrimary
, and the algorithm inside antd will automatically calculate and apply a series of corresponding colors according to the Seed Token:
const theme = {
token: {
colorPrimary: '#1890ff',
},
};
Map Token
Map Token is a gradient variable derived from Seed. It is recommended to implement custom Map Token through theme.algorithm
, which can ensure the gradient relationship between Map Tokens. It can also be overridden by theme.token
to modify the value of some map tokens individually.
const theme = {
token: {
colorPrimaryBg: '#e6f7ff',
},
};
Alias Token
Alias Token is used to control the style of some common components in batches, which is basically a Map Token alias, or a specially processed Map Token.
const theme = {
token: {
colorLink: '#1890ff',
},
};
Algorithm
The basic algorithm is used to expand the Seed Token into a Map Token, such as calculating a gradient color palette from a basic color, or calculating rounded corners of various sizes from a basic rounded corner. Algorithms can be used alone or in any combination, for example, dark and compact algorithms can be combined to get a dark and compact theme.
import { theme } from 'antd';
const { darkAlgorithm, compactAlgorithm } = theme;
const theme = {
algorithm: [darkAlgorithm, compactAlgorithm],
};
Compatible adjustment
Ant Design default using CSS-in-JS with :where
Selector to reduce priority to avoid user additional adjust style cost when updating to v5. If you want to support old browser, you can use @ant-design/cssinjs
to adjust this behavior (Please note keep version align with antd):
import React from 'react';
import { StyleProvider } from '@ant-design/cssinjs';
export default () => (
<StyleProvider hashPriority="high">
<MyApp />
</StyleProvider>
);
It will turn :where
to class selector:
-- :where(.css-bAMboO).ant-btn {
++ .css-bAMboO.ant-btn {
color: #fff;
}
Note: After turning off the :where
downgrade, you may need to manually adjust the priority of some styles.
Server Side Render (SSR)
Use @ant-design/cssinjs
to extract style:
import React from 'react';
import { renderToString } from 'react-dom/server';
import { createCache, extractStyle, StyleProvider } from '@ant-design/cssinjs';
export default () => {
// SSR Render
const cache = createCache();
const html = renderToString(
<StyleProvider cache={cache}>
<MyApp />
</StyleProvider>,
);
// Grab style from cache
const styleText = extractStyle(cache);
// Mix with style
return `
<!DOCTYPE html>
<html>
<head>
${styleText}
</head>
<body>
<div id="root">${html}</div>
</body>
</html>
`;
};
Shadow DOM Usage
Since <style />
tag insertion is different from normal DOM in Shadow DOM scenario, you need to use StyleProvider
of @ant-design/cssinjs
to configure the container
property to set the insertion position:
import React from 'react';
import { createRoot } from 'react-dom/client';
import { StyleProvider } from '@ant-design/cssinjs';
const shadowRoot = someEle.attachShadow({ mode: 'open' });
const container = document.createElement('div');
shadowRoot.appendChild(container);
const root = createRoot(container);
root.render(
<StyleProvider container={shadowRoot}>
<MyApp />
</StyleProvider>,
);
API
Theme
Property | Description | Type | Default |
---|---|---|---|
token | Modify Design Token | AliasToken |
- |
inherit | Inherit theme configured in upper ConfigProvider | boolean | true |
algorithm | Modify the algorithms of theme | (token: SeedToken) => MapToken | ((token: SeedToken) => MapToken)[] |
defaultAlgorithm |
components | Modify Component Token and Alias Token applied to components | OverrideToken | - |
OverrideToken
Property | Description | Type | Default |
---|---|---|---|
Component (可以是任意 antd 组件名,如 Button ) |
用于修改 Component Token 以及覆盖该组件消费的 Alias Token | ComponentToken & AliasToken |
- |
SeedToken
MapToken
继承所有 SeedToken 的 Property
AliasToken
继承所有 SeedToken 和 MapToken 的 Property
How to Debug your Theme
We provide tools to help users debug themes: Theme Editor
You can use this tool to freely modify Design Token to meet your theme expectations.
Theme Presets
FAQ
Why component re-mounted when theme
changed from undefined
to some object or to undefined
?
In ConfigProvider, we pass context through DesignTokenContext
. When theme
is undefined
, a layer of Provider will not be set, so React VirtualDOM structure changes from scratch or from existence to nothing, causing components to be re-mounted. Solution: Replace undefined
with an empty object {}
.