mirror of
https://github.com/ant-design/ant-design.git
synced 2024-12-23 23:18:36 +08:00
475 lines
14 KiB
Markdown
475 lines
14 KiB
Markdown
---
|
||
group:
|
||
title: Advanced
|
||
order: 1
|
||
order: 0
|
||
title: 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:
|
||
|
||
1. Switching theme dynamically;
|
||
2. Multiple themes;
|
||
3. Customizing theme variables for some components;
|
||
4. ...
|
||
|
||
## Basic Usage
|
||
|
||
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. You can pass `theme` to `ConfigProvider` to customize theme. After migrate to V5, theme of V5 will be applied by default.
|
||
|
||
<!-- prettier-ignore -->
|
||
:::warning
|
||
`ConfigProvider` will not take effect on static methods such as `message.xxx`, `Modal.xxx`, `notification.xxx`, because in these methods, antd will dynamically create new ones through `ReactDOM.render` React entities. Its context is not the same as the context of the current code, so context information cannot be obtained.
|
||
|
||
<!-- prettier-ignore -->
|
||
When you need context information (such as the content configured by ConfigProvider), you can use the `Modal.useModal` method to return the modal entity and the contextHolder node. Just insert it where you need to get the context, or you can use [App Component](/components/app) to simplify the problem of usingModal and other methods that need to manually implant the contextHolder.
|
||
:::
|
||
|
||
### Customize Design Token
|
||
|
||
By modifying `token` property of `theme`, we can modify Design Token globally. Some tokens will affect other tokens. We call these tokens Seed Token.
|
||
|
||
```sandpack
|
||
const sandpackConfig = {
|
||
autorun: true,
|
||
};
|
||
|
||
import { Button, ConfigProvider, Space } from 'antd';
|
||
import React from 'react';
|
||
|
||
const App: React.FC = () => (
|
||
<ConfigProvider
|
||
theme={{
|
||
token: {
|
||
// Seed Token
|
||
colorPrimary: '#00b96b',
|
||
borderRadius: 2,
|
||
|
||
// Alias Token
|
||
colorBgContainer: '#f6ffed',
|
||
},
|
||
}}
|
||
>
|
||
<Space>
|
||
<Button type="primary">Primary</Button>
|
||
<Button>Default</Button>
|
||
</Space>
|
||
</ConfigProvider>
|
||
);
|
||
|
||
export default App;
|
||
```
|
||
|
||
### 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:
|
||
|
||
- default algorithm `theme.defaultAlgorithm`
|
||
- dark algorithm `theme.darkAlgorithm`
|
||
- compact algorithm `theme.compactAlgorithm`
|
||
|
||
You can switch algorithms by modifying the `algorithm` property of `theme` in ConfigProvider.
|
||
|
||
```sandpack
|
||
const sandpackConfig = {
|
||
dark: true,
|
||
};
|
||
|
||
import React from 'react';
|
||
import { Button, ConfigProvider, Input, Space, theme } from 'antd';
|
||
|
||
const App: React.FC = () => (
|
||
<ConfigProvider
|
||
theme={{
|
||
// 1. Use dark algorithm
|
||
algorithm: theme.darkAlgorithm,
|
||
|
||
// 2. Combine dark algorithm and compact algorithm
|
||
// algorithm: [theme.darkAlgorithm, theme.compactAlgorithm],
|
||
}}
|
||
>
|
||
<Space>
|
||
<Input placeholder="Please Input" />
|
||
<Button type="primary">Submit</Button>
|
||
</Space>
|
||
</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.
|
||
|
||
<!-- prettier-ignore -->
|
||
:::info{title=Algorithm of Component Token}
|
||
By default, all component tokens can only override global token and will not be derived based on Seed Token.
|
||
|
||
<!-- prettier-ignore -->
|
||
In version `>= 5.8.0`, component tokens support the `algorithm` property, which can be used to enable algorithm or pass in other algorithms.
|
||
:::
|
||
|
||
```sandpack
|
||
import React from 'react';
|
||
import { ConfigProvider, Button, Space, Input, Divider } from 'antd';
|
||
|
||
const App: React.FC = () => (
|
||
<>
|
||
<ConfigProvider
|
||
theme={{
|
||
components: {
|
||
Button: {
|
||
colorPrimary: '#00b96b',
|
||
algorithm: true, // Enable algorithm
|
||
},
|
||
Input: {
|
||
colorPrimary: '#eb2f96',
|
||
algorithm: true, // Enable algorithm
|
||
}
|
||
},
|
||
}}
|
||
>
|
||
<Space>
|
||
<div style={{ fontSize: 14 }}>Enable algorithm: </div>
|
||
<Input placeholder="Please Input" />
|
||
<Button type="primary">Submit</Button>
|
||
</Space>
|
||
</ConfigProvider>
|
||
<Divider />
|
||
<ConfigProvider
|
||
theme={{
|
||
components: {
|
||
Button: {
|
||
colorPrimary: '#00b96b',
|
||
},
|
||
Input: {
|
||
colorPrimary: '#eb2f96',
|
||
}
|
||
},
|
||
}}
|
||
>
|
||
<Space>
|
||
<div style={{ fontSize: 14 }}>Disable algorithm: </div>
|
||
<Input placeholder="Please Input" />
|
||
<Button type="primary">Submit</Button>
|
||
</Space>
|
||
</ConfigProvider>
|
||
</>
|
||
);
|
||
|
||
export default App;
|
||
```
|
||
|
||
### Disable Motion
|
||
|
||
antd has built-in interaction animations to make enterprise-level pages more detailed. In some extreme scenarios, it may affect the performance of page interaction. If you need to turn off the animation, try setting `motion` of `token` to `false`:
|
||
|
||
```sandpack
|
||
import React from 'react';
|
||
import { Checkbox, Col, ConfigProvider, Flex, Radio, Row, Switch } from 'antd';
|
||
|
||
const App: React.FC = () => {
|
||
const [checked, setChecked] = React.useState<boolean>(false);
|
||
const timerRef = React.useRef<ReturnType<typeof setInterval>>();
|
||
React.useEffect(() => {
|
||
timerRef.current = setInterval(() => {
|
||
setChecked((prev) => !prev);
|
||
}, 500);
|
||
return () => {
|
||
if (timerRef.current) {
|
||
clearInterval(timerRef.current);
|
||
}
|
||
};
|
||
}, []);
|
||
|
||
const nodes = (
|
||
<Flex gap="small">
|
||
<Checkbox checked={checked}>Checkbox</Checkbox>
|
||
<Radio checked={checked}>Radio</Radio>
|
||
<Switch checked={checked} />
|
||
</Flex>
|
||
);
|
||
|
||
return (
|
||
<Row gutter={[24, 24]}>
|
||
<Col span={24}>{nodes}</Col>
|
||
<Col span={24}>
|
||
<ConfigProvider theme={{ token: { motion: false } }}>{nodes}</ConfigProvider>
|
||
</Col>
|
||
</Row>
|
||
);
|
||
};
|
||
|
||
export default App;
|
||
```
|
||
|
||
## Advanced
|
||
|
||
### 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.
|
||
|
||
```sandpack
|
||
import { Button, ConfigProvider, Space, Input, ColorPicker, Divider } from 'antd';
|
||
import React from 'react';
|
||
|
||
const App: React.FC = () => {
|
||
const [primary, setPrimary] = React.useState('#1677ff');
|
||
|
||
return (
|
||
<>
|
||
<ColorPicker showText value={primary} onChange={(color) => setPrimary(color.toHexString())} />
|
||
<Divider />
|
||
<ConfigProvider
|
||
theme={{
|
||
token: {
|
||
colorPrimary: primary,
|
||
},
|
||
}}
|
||
>
|
||
<Space>
|
||
<Input placeholder="Please Input" />
|
||
<Button type="primary">Submit</Button>
|
||
</Space>
|
||
</ConfigProvider>
|
||
</>
|
||
);
|
||
}
|
||
|
||
export default App;
|
||
```
|
||
|
||
### Nested 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.
|
||
|
||
```sandpack
|
||
import React from 'react';
|
||
import { Button, ConfigProvider, Space } from 'antd';
|
||
|
||
const App: React.FC = () => (
|
||
<ConfigProvider
|
||
theme={{
|
||
token: {
|
||
colorPrimary: '#1677ff',
|
||
},
|
||
}}
|
||
>
|
||
<Space>
|
||
<Button type="primary">Theme 1</Button>
|
||
<ConfigProvider
|
||
theme={{
|
||
token: {
|
||
colorPrimary: '#00b96b',
|
||
},
|
||
}}
|
||
>
|
||
<Button type="primary">Theme 2</Button>
|
||
</ConfigProvider>
|
||
</Space>
|
||
</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.
|
||
|
||
```sandpack
|
||
import React from 'react';
|
||
import { Button, theme } from 'antd';
|
||
|
||
const { useToken } = theme;
|
||
|
||
const App: React.FC = () => {
|
||
const { token } = useToken();
|
||
|
||
return (
|
||
<div
|
||
style={{
|
||
backgroundColor: token.colorPrimaryBg,
|
||
padding: token.padding,
|
||
borderRadius: token.borderRadius,
|
||
color: token.colorPrimaryText,
|
||
fontSize: token.fontSize,
|
||
}}
|
||
>
|
||
Consume Design Token
|
||
</div>
|
||
);
|
||
};
|
||
|
||
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:
|
||
|
||
```jsx
|
||
import { theme } from 'antd';
|
||
|
||
const { getDesignToken } = theme;
|
||
|
||
const globalToken = getDesignToken();
|
||
```
|
||
|
||
Same as ConfigProvider, `getDesignToken` could also accept a config object as `theme`:
|
||
|
||
```tsx
|
||
import type { ThemeConfig } from 'antd';
|
||
import { theme } from 'antd';
|
||
import { createRoot } from 'react-dom/client';
|
||
|
||
const { getDesignToken, useToken } = theme;
|
||
|
||
const config: ThemeConfig = {
|
||
token: {
|
||
colorPrimary: '#1890ff',
|
||
},
|
||
};
|
||
|
||
// By static function
|
||
const globalToken = getDesignToken(config);
|
||
|
||
// By hook
|
||
const App = () => {
|
||
const { token } = useToken();
|
||
return null;
|
||
};
|
||
|
||
// Example for rendering
|
||
createRoot(document.getElementById('#app')).render(
|
||
<ConfigProvider theme={config}>
|
||
<App />
|
||
</ConfigProvider>,
|
||
);
|
||
```
|
||
|
||
If you want to use in preprocess style framework like less, use less-loader for injection:
|
||
|
||
```jsx
|
||
{
|
||
loader: "less-loader",
|
||
options: {
|
||
lessOptions: {
|
||
modifyVars: mapToken,
|
||
},
|
||
},
|
||
}
|
||
```
|
||
|
||
Compatible package provide convert function to transform to v4 less variable. Read [this](/docs/react/migration-v5) for detail.
|
||
|
||
### Theme editor
|
||
|
||
We provide tools to help users debug themes: [Theme Editor](/theme-editor)
|
||
|
||
You can use this tool to freely modify Design Token to meet your theme expectations.
|
||
|
||
## Design Token
|
||
|
||
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 of Design Token
|
||
|
||
![token](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*uF3kTrY4InUAAAAAAAAAAAAAARQnAQ)
|
||
|
||
### 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:
|
||
|
||
```tsx
|
||
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.
|
||
|
||
```tsx
|
||
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.
|
||
|
||
```tsx
|
||
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.
|
||
|
||
```tsx
|
||
import { theme } from 'antd';
|
||
|
||
const { darkAlgorithm, compactAlgorithm } = theme;
|
||
|
||
const theme = {
|
||
algorithm: [darkAlgorithm, compactAlgorithm],
|
||
};
|
||
```
|
||
|
||
## 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 | `ComponentsConfig` | - |
|
||
| cssVar | Toggle CSS Variables, refer [CSS Variables](/docs/react/css-variables#api) | `boolean \| { prefix?: string; key?: string }` | false |
|
||
| hashed | Component class Hash value, refer [CSS Variables](/docs/react/css-variables#disable-hash) | boolean | true |
|
||
|
||
### ComponentsConfig
|
||
|
||
| Property | Description | Type | Default |
|
||
| --- | --- | --- | --- |
|
||
| `Component` (Can be any antd Component name like `Button`) | Modify Component Token or override Component used Alias Token | `ComponentToken & AliasToken & { algorithm: boolean \| (token: SeedToken) => MapToken` \| `((token: SeedToken) => MapToken)[]}` | - |
|
||
|
||
> `algorithm` of component is `false` by default, which means tokens of component will only override global token. When it is set with `true`, the algorithm will be the same as global. You can also pass algorithm or Array of algorithm, and it will override algorithm of global.
|
||
|
||
### SeedToken
|
||
|
||
<TokenTable type="seed"></TokenTable>
|
||
|
||
### MapToken
|
||
|
||
> Inherit all SeedToken properties
|
||
|
||
<TokenTable type="map"></TokenTable>
|
||
|
||
### AliasToken
|
||
|
||
> Inherit all SeedToken and MapToken properties
|
||
|
||
<TokenTable type="alias"></TokenTable>
|
||
|
||
## 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 `{}`.
|