mirror of
https://gitee.com/ant-design-vue/ant-design-vue.git
synced 2024-11-29 18:48:32 +08:00
docs(customize-theme): update docs (#6540)
* fix introduce doc * fix getting-started doc * add migration-v4 doc * fix docs * Update migration-v4.zh-CN.md * Update migration-v4.zh-CN.md * Update migration-v4.en-US.md * Update migration-v4.zh-CN.md * Update getting-started.en-US.md * Update getting-started.zh-CN.md * Update introduce.en-US.md * Update introduce.zh-CN.md * update customize-theme doc & fix migration-v4 error * update customize-theme doc * fix migration-v4 error * remove SSR & shadowDom * Update customize-theme.zh-CN.md * Update customize-theme.en-US.md --------- Co-authored-by: tangjinzhou <415800467@qq.com>
This commit is contained in:
parent
fd8af2322c
commit
266482deca
@ -58,14 +58,7 @@ export function createMarkdownToVueRenderFn(
|
||||
};
|
||||
const newContent = data.vueCode
|
||||
? await genComponentCode(md, data, pageData)
|
||||
: `
|
||||
<template><article class="markdown">${html}</article></template>
|
||||
|
||||
<script>
|
||||
export default { pageData: ${JSON.stringify(pageData)} }
|
||||
</script>
|
||||
${fetchCode(content, 'style')}
|
||||
`;
|
||||
: await genDocCode(content, pageData);
|
||||
|
||||
debug(`[render] ${file} in ${Date.now() - start}ms.`);
|
||||
const result = {
|
||||
@ -137,6 +130,28 @@ ${jsSourceCode}
|
||||
return newContent;
|
||||
}
|
||||
|
||||
async function genDocCode(content: string, pageData: PageData) {
|
||||
return `
|
||||
<template><article class="markdown">${pageData.html}</article></template>
|
||||
|
||||
<script>
|
||||
import ColorChunk from '@/components/ColorChunk';
|
||||
import TokenTable from '@/components/TokenTable';
|
||||
import ComponentTokenTable from '@/components/ComponentTokenTable';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ColorChunk,
|
||||
TokenTable,
|
||||
ComponentTokenTable
|
||||
},
|
||||
pageData: ${JSON.stringify(pageData)}
|
||||
}
|
||||
</script>
|
||||
${fetchCode(content, 'style')}
|
||||
`;
|
||||
}
|
||||
|
||||
const inferTitle = (frontmatter: any, content: string) => {
|
||||
if (frontmatter.home) {
|
||||
return 'Home';
|
||||
|
56
site/src/components/ColorChunk/index.tsx
Normal file
56
site/src/components/ColorChunk/index.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import { defineComponent, toRefs, computed } from 'vue';
|
||||
import type { CSSProperties, PropType } from 'vue';
|
||||
import { TinyColor, type ColorInput } from '@ctrl/tinycolor';
|
||||
import useSiteToken from '../../hooks/useSiteToken';
|
||||
|
||||
const ColorChunk = defineComponent({
|
||||
props: {
|
||||
color: {
|
||||
type: String as PropType<ColorInput>,
|
||||
default: '#000',
|
||||
},
|
||||
},
|
||||
setup(props, { attrs, slots }) {
|
||||
const SiteToken = useSiteToken();
|
||||
|
||||
const token = computed(() => SiteToken.value.token);
|
||||
|
||||
const { color } = toRefs(props);
|
||||
|
||||
const dotColor = computed(() => {
|
||||
const _color = new TinyColor(color.value).toHex8String();
|
||||
return _color.endsWith('ff') ? _color.slice(0, -2) : _color;
|
||||
});
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<span
|
||||
{...attrs}
|
||||
style={{
|
||||
padding: '0.2em 0.4em',
|
||||
fontSize: '0.9em',
|
||||
background: token.value.siteMarkdownCodeBg,
|
||||
borderRadius: `${token.value.borderRadius}px`,
|
||||
fontFamily: 'monospace',
|
||||
...(attrs.style as CSSProperties),
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: '6px',
|
||||
height: '6px',
|
||||
borderRadius: `${token.value.borderRadiusSM}px`,
|
||||
marginRight: '4px',
|
||||
border: `1px solid ${token.value.colorSplit}`,
|
||||
backgroundColor: dotColor.value,
|
||||
}}
|
||||
/>
|
||||
{slots.default ? slots.default() : dotColor.value}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default ColorChunk;
|
205
site/src/components/ComponentTokenTable/index.tsx
Normal file
205
site/src/components/ComponentTokenTable/index.tsx
Normal file
@ -0,0 +1,205 @@
|
||||
import { defineComponent, toRefs, computed } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import { ConfigProvider, Table } from 'ant-design-vue';
|
||||
import { getDesignToken } from '../antdv-token-previewer';
|
||||
import tokenMeta from 'ant-design-vue/es/version/token-meta.json';
|
||||
import tokenData from 'ant-design-vue/es/version/token.json';
|
||||
import { useLocale } from '../../i18n';
|
||||
import useSiteToken from '../../hooks/useSiteToken';
|
||||
import { useColumns } from '../TokenTable';
|
||||
import ColorChunk from '../ColorChunk';
|
||||
|
||||
const defaultToken = getDesignToken();
|
||||
|
||||
const locales = {
|
||||
cn: {
|
||||
token: 'Token 名称',
|
||||
description: '描述',
|
||||
type: '类型',
|
||||
value: '默认值',
|
||||
},
|
||||
en: {
|
||||
token: 'Token Name',
|
||||
description: 'Description',
|
||||
type: 'Type',
|
||||
value: 'Default Value',
|
||||
},
|
||||
};
|
||||
|
||||
interface SubTokenTableProps {
|
||||
defaultOpen?: boolean;
|
||||
title: string;
|
||||
tokens: string[];
|
||||
}
|
||||
|
||||
const SubTokenTable = defineComponent({
|
||||
props: {
|
||||
defaultOpen: {
|
||||
type: Boolean as PropType<SubTokenTableProps['defaultOpen']>,
|
||||
},
|
||||
title: {
|
||||
type: String as PropType<SubTokenTableProps['title']>,
|
||||
},
|
||||
tokens: {
|
||||
type: Array as PropType<SubTokenTableProps['tokens']>,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { defaultOpen, title, tokens } = toRefs(props);
|
||||
const [, lang] = useLocale(locales);
|
||||
const siteToken = useSiteToken();
|
||||
const token = computed(() => siteToken.value.token);
|
||||
const columns = useColumns();
|
||||
|
||||
return () => {
|
||||
if (!tokens.value.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = tokens.value
|
||||
.sort((token1, token2) => {
|
||||
const hasColor1 = token1.toLowerCase().includes('color');
|
||||
const hasColor2 = token2.toLowerCase().includes('color');
|
||||
|
||||
if (hasColor1 && !hasColor2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!hasColor1 && hasColor2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return token1 < token2 ? -1 : 1;
|
||||
})
|
||||
.map(name => {
|
||||
const meta = tokenMeta[name];
|
||||
|
||||
if (!meta) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
desc: lang.value === 'cn' ? meta.desc : meta.descEn,
|
||||
type: meta.type,
|
||||
value: (defaultToken as any)[name],
|
||||
};
|
||||
})
|
||||
.filter(info => info);
|
||||
|
||||
return (
|
||||
// Reuse `.markdown` style
|
||||
<details class="markdown" open={defaultOpen.value || process.env.NODE_ENV !== 'production'}>
|
||||
<summary>
|
||||
<h3 style={{ display: 'inline' }}>{title.value}</h3>
|
||||
</summary>
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
token: {
|
||||
borderRadius: 0,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Table
|
||||
size="middle"
|
||||
columns={columns}
|
||||
bordered
|
||||
dataSource={data}
|
||||
style={{ marginBottom: token.value.margin }}
|
||||
pagination={false}
|
||||
rowKey={record => record.name}
|
||||
v-slots={{
|
||||
bodyCell: ({ text, record, column }) => {
|
||||
if (column.key === 'type') {
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
margin: '0 1px',
|
||||
padding: '0.2em 0.4em',
|
||||
fontSize: '0.9em',
|
||||
background: `${token.value.siteMarkdownCodeBg}`,
|
||||
border: `1px solid ${token.value.colorSplit}`,
|
||||
borderRadius: '3px',
|
||||
fontFamily: 'monospace',
|
||||
}}
|
||||
>
|
||||
{record.type}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (column.key === 'value') {
|
||||
const isColor =
|
||||
typeof record.value === 'string' &&
|
||||
(record.value.startsWith('#') || record.value.startsWith('rgb'));
|
||||
if (isColor) {
|
||||
return <ColorChunk color={record.value}>{record.value}</ColorChunk>;
|
||||
}
|
||||
return (
|
||||
<span>
|
||||
{typeof record.value !== 'string'
|
||||
? JSON.stringify(record.value)
|
||||
: record.value}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return <span>{text} </span>;
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</ConfigProvider>
|
||||
</details>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export interface ComponentTokenTableProps {
|
||||
component: string;
|
||||
}
|
||||
|
||||
const ComponentTokenTable = defineComponent({
|
||||
props: {
|
||||
component: {
|
||||
type: String as PropType<ComponentTokenTableProps['component']>,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { component } = toRefs(props);
|
||||
|
||||
const mergedTokens = computed(() => {
|
||||
const globalTokenSet = new Set<string>();
|
||||
let componentTokens: Record<string, string> = {};
|
||||
|
||||
component.value.split(',').forEach(comp => {
|
||||
const { global: globalTokens = [], component: singleComponentTokens = [] } =
|
||||
tokenData[comp] || {};
|
||||
|
||||
globalTokens.forEach((token: string) => {
|
||||
globalTokenSet.add(token);
|
||||
});
|
||||
|
||||
componentTokens = {
|
||||
...componentTokens,
|
||||
...singleComponentTokens,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
mergedGlobalTokens: Array.from(globalTokenSet),
|
||||
mergedComponentTokens: componentTokens,
|
||||
};
|
||||
});
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<>
|
||||
{/* Component Token 先不展示 */}
|
||||
{/* <SubTokenTable title="Component Token" tokens={mergedComponentTokens} defaultOpen /> */}
|
||||
<SubTokenTable title="Global Token" tokens={mergedTokens.value.mergedGlobalTokens} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default ComponentTokenTable;
|
143
site/src/components/TokenTable/index.tsx
Normal file
143
site/src/components/TokenTable/index.tsx
Normal file
@ -0,0 +1,143 @@
|
||||
import { defineComponent, toRefs, computed } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import type { TableProps } from 'ant-design-vue';
|
||||
import { Table } from 'ant-design-vue';
|
||||
import { getDesignToken } from '../antdv-token-previewer';
|
||||
import tokenMeta from 'ant-design-vue/es/version/token-meta.json';
|
||||
import { useLocale } from '../../i18n';
|
||||
import useSiteToken from '../../hooks/useSiteToken';
|
||||
import ColorChunk from '../ColorChunk';
|
||||
|
||||
type TokenTableProps = {
|
||||
type: 'seed' | 'map' | 'alias';
|
||||
lang: 'zh' | 'en';
|
||||
};
|
||||
|
||||
type TokenData = {
|
||||
name: string;
|
||||
desc: string;
|
||||
type: string;
|
||||
value: any;
|
||||
};
|
||||
|
||||
const defaultToken = getDesignToken();
|
||||
|
||||
const locales = {
|
||||
cn: {
|
||||
token: 'Token 名称',
|
||||
description: '描述',
|
||||
type: '类型',
|
||||
value: '默认值',
|
||||
},
|
||||
en: {
|
||||
token: 'Token Name',
|
||||
description: 'Description',
|
||||
type: 'Type',
|
||||
value: 'Default Value',
|
||||
},
|
||||
};
|
||||
|
||||
export function useColumns(): Exclude<TableProps<TokenData>['columns'], undefined> {
|
||||
const [locale] = useLocale(locales);
|
||||
|
||||
return [
|
||||
{
|
||||
title: locale.value.token,
|
||||
key: 'name',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: locale.value.description,
|
||||
key: 'desc',
|
||||
dataIndex: 'desc',
|
||||
},
|
||||
{
|
||||
title: locale.value.type,
|
||||
key: 'type',
|
||||
dataIndex: 'type',
|
||||
},
|
||||
{
|
||||
title: locale.value.value,
|
||||
key: 'value',
|
||||
dataIndex: 'value',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const TokenTable = defineComponent({
|
||||
props: {
|
||||
type: {
|
||||
type: String as PropType<TokenTableProps['type']>,
|
||||
},
|
||||
lang: {
|
||||
type: String as PropType<TokenTableProps['lang']>,
|
||||
},
|
||||
},
|
||||
setup(props, { attrs }) {
|
||||
const { type } = toRefs(props);
|
||||
const SiteToken = useSiteToken();
|
||||
const token = computed(() => SiteToken.value.token);
|
||||
|
||||
const [, lang] = useLocale(locales);
|
||||
const columns = useColumns();
|
||||
|
||||
const data = computed<TokenData[]>(() => {
|
||||
return Object.entries(tokenMeta)
|
||||
.filter(([, meta]: any) => meta.source === type.value)
|
||||
.map(([token, meta]: any) => ({
|
||||
name: token,
|
||||
desc: lang.value === 'cn' ? meta.desc : meta.descEn,
|
||||
type: meta.type,
|
||||
value: (defaultToken as any)[token],
|
||||
}));
|
||||
});
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<Table
|
||||
dataSource={data.value}
|
||||
columns={columns}
|
||||
pagination={false}
|
||||
bordered
|
||||
{...attrs}
|
||||
v-slots={{
|
||||
bodyCell: ({ text, record, column }) => {
|
||||
if (column.key === 'type') {
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
margin: '0 1px',
|
||||
padding: '0.2em 0.4em',
|
||||
fontSize: '0.9em',
|
||||
background: `${token.value.siteMarkdownCodeBg}`,
|
||||
border: `1px solid ${token.value.colorSplit}`,
|
||||
borderRadius: '3px',
|
||||
fontFamily: 'monospace',
|
||||
}}
|
||||
>
|
||||
{record.type}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (column.key === 'value') {
|
||||
const isColor =
|
||||
typeof record.value === 'string' &&
|
||||
(record.value.startsWith('#') || record.value.startsWith('rgb'));
|
||||
if (isColor) {
|
||||
return <ColorChunk color={record.value}>{record.value}</ColorChunk>;
|
||||
}
|
||||
return (
|
||||
<span>
|
||||
{typeof record.value !== 'string' ? JSON.stringify(record.value) : record.value}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return <span>{text} </span>;
|
||||
},
|
||||
}}
|
||||
></Table>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
export default TokenTable;
|
@ -105,17 +105,6 @@ const routes = [
|
||||
meta: { enTitle: 'Customize Theme', title: '定制主题', category: 'docs' },
|
||||
component: () => import('../vueDocs/customize-theme.en-US.md'),
|
||||
},
|
||||
|
||||
{
|
||||
path: 'vue/customize-theme-variable-cn',
|
||||
meta: { enTitle: 'Dynamic Theme (Experimental)', title: '动态主题', category: 'docs' },
|
||||
component: () => import('../vueDocs/customize-theme-variable.zh-CN.md'),
|
||||
},
|
||||
{
|
||||
path: 'vue/customize-theme-variable',
|
||||
meta: { enTitle: 'Dynamic Theme (Experimental)', title: '动态主题', category: 'docs' },
|
||||
component: () => import('../vueDocs/customize-theme-variable.en-US.md'),
|
||||
},
|
||||
{
|
||||
path: 'vue/replace-date-cn',
|
||||
meta: { enTitle: 'Custom Date Library', title: '自定义时间库', category: 'docs' },
|
||||
|
@ -1,79 +0,0 @@
|
||||
---
|
||||
order: 7.1
|
||||
title: Dynamic Theme (Experimental)
|
||||
---
|
||||
|
||||
Except [less customize theme](/docs/react/customize-theme), We also provide CSS Variable version to enable dynamic theme. You can check on [ConfigProvider](/components/config-provider/#components-config-provider-demo-theme) demo.
|
||||
|
||||
## Caveats
|
||||
|
||||
- This function requires at least `ant-design-vue@3.1.0-beta.0`.
|
||||
|
||||
## How to use
|
||||
|
||||
### Import antd.variable.min.css
|
||||
|
||||
Replace your import style file with CSS Variable version:
|
||||
|
||||
```diff
|
||||
-- import 'ant-design-vue/dist/antd.min.css';
|
||||
++ import 'ant-design-vue/dist/antd.variable.min.css';
|
||||
```
|
||||
|
||||
Note: You need remove `babel-plugin-import` for the dynamic theme.
|
||||
|
||||
### Static config
|
||||
|
||||
Call ConfigProvider static function to modify theme color:
|
||||
|
||||
```ts
|
||||
import { ConfigProvider } from 'ant-design-vue';
|
||||
|
||||
ConfigProvider.config({
|
||||
theme: {
|
||||
primaryColor: '#25b864',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Conflict resolve
|
||||
|
||||
CSS Variable use `--ant` prefix by default. When exist multiple antd style file in your project, you can modify prefix to fix it.
|
||||
|
||||
### Adjust
|
||||
|
||||
Modify `prefixCls` on the root of ConfigProvider:
|
||||
|
||||
```html
|
||||
<template>
|
||||
<a-config-provider prefix-cls="custom">
|
||||
<my-app />
|
||||
</a-config-provider>
|
||||
</template>
|
||||
```
|
||||
|
||||
Also need call the static function to modify `prefixCls`:
|
||||
|
||||
```ts
|
||||
import { ConfigProvider } from 'ant-design-vue';
|
||||
ConfigProvider.config({
|
||||
prefixCls: 'custom',
|
||||
theme: {
|
||||
primaryColor: '#25b864',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Compile less
|
||||
|
||||
Since prefix modified. Origin `antd.variable.css` should also be replaced:
|
||||
|
||||
```bash
|
||||
lessc --js --modify-var="ant-prefix=custom" ant-design-vue/dist/antd.variable.less modified.css
|
||||
```
|
||||
|
||||
### Related changes
|
||||
|
||||
In order to implement CSS Variable and maintain original usage compatibility, we added `@root-entry-name: xxx;` entry injection to the `dist/antd.xxx.less` file to support less dynamic loading of the corresponding less file. Under normal circumstances, you do not need to pay attention to this change. However, if your project directly references the less file in the `lib|es` directory. You need to configure `@root-entry-name: default;` (or `@root-entry-name: variable;`) at the entry of less so that less can find the correct entry.
|
||||
|
||||
In addition, we migrated `@import'motion'` and `@import'reset'` in `lib|es/style/minxins/index.less` to `lib|es/style/themes/xxx.less` In, because these two files rely on theme-related variables. If you use the relevant internal method, please adjust it yourself. Of course, we still recommend using the `antd.less` files in the `dist` directory directly instead of calling internal files, because they are often affected by refactoring.
|
@ -1,79 +0,0 @@
|
||||
---
|
||||
order: 7.1
|
||||
title: 动态主题(实验性)
|
||||
---
|
||||
|
||||
除了 [less 定制主题](/docs/vue/customize-theme) 外,我们还提供了 CSS Variable 版本以支持动态切换主题能力。你可以在 [ConfigProvider](/components/config-provider/#components-config-provider-demo-theme) 进行体验。
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 该功能在 `ant-design-vue@3.1.0-beta.0` 版本起支持。
|
||||
|
||||
## 如何使用
|
||||
|
||||
### 引入 antd.variable.min.css
|
||||
|
||||
替换当前项目引入样式文件为 CSS Variable 版本:
|
||||
|
||||
```diff
|
||||
-- import 'ant-design-vue/dist/antd.min.css';
|
||||
++ import 'ant-design-vue/dist/antd.variable.min.css';
|
||||
```
|
||||
|
||||
注:如果你使用了 `babel-plugin-import`,需要将其去除。
|
||||
|
||||
### 静态方法配置
|
||||
|
||||
调用 ConfigProvider 配置方法设置主题色:
|
||||
|
||||
```ts
|
||||
import { ConfigProvider } from 'ant-design-vue';
|
||||
|
||||
ConfigProvider.config({
|
||||
theme: {
|
||||
primaryColor: '#25b864',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 冲突解决
|
||||
|
||||
默认情况下,CSS Variable 会以 `--ant` 作为前缀。当你的项目中引用多份 css 文件时,可以通过修改前缀的方式避免冲突。
|
||||
|
||||
### 代码调整
|
||||
|
||||
通过 ConfigProvider 在顶层修改 `prefixCls`:
|
||||
|
||||
```html
|
||||
<template>
|
||||
<a-config-provider prefix-cls="custom">
|
||||
<my-app />
|
||||
</a-config-provider>
|
||||
</template>
|
||||
```
|
||||
|
||||
通过静态方法设置主题色以及对应 `prefixCls`:
|
||||
|
||||
```ts
|
||||
import { ConfigProvider } from 'ant-design-vue';
|
||||
ConfigProvider.config({
|
||||
prefixCls: 'custom',
|
||||
theme: {
|
||||
primaryColor: '#25b864',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 编译 less
|
||||
|
||||
由于前缀变更,你需要重新生成一份对应的 css 文件。
|
||||
|
||||
```bash
|
||||
lessc --js --modify-var="ant-prefix=custom" ant-design-vue/dist/antd.variable.less modified.css
|
||||
```
|
||||
|
||||
### 相关变更
|
||||
|
||||
为了实现 CSS Variable 并保持原始用法兼容性,我们于 `dist/antd.xxx.less` 文件中添加了 `@root-entry-name: xxx;` 入口注入以支持 less 动态加载对应的 less 文件。一般情况下,你不需要关注该变化。但是,如果你的项目中直接引用了 `lib|es` 目录下的 less 文件。你需要在 less 入口处配置 `@root-entry-name: default;` (或者 `@root-entry-name: variable;`)以使 less 可以找到正确的入口。
|
||||
|
||||
此外,我们将 `lib|es/style/minxins/index.less` 中的 `@import 'motion'` 和 `@import 'reset'` 迁移至了 `lib|es/style/themes/xxx.less` 中,因为这两个文件依赖了主题相关变量。如果你使用了相关内部方法,请自行调整。当然,我们还是建议直接使用 `dist` 目录下的 `antd.less` 文件而非调用内部文件,因为它们经常会受重构影响。
|
@ -1,204 +1,260 @@
|
||||
# Customize Theme
|
||||
|
||||
The structure and styles of ant-design-vue are exactly the same as those of Antd. You can refer to the Antd React customization mode for configuration.
|
||||
Ant Design Vue allows you to customize our design tokens to satisfy UI diversity from business or brand requirements, including primary color, border radius, border color, etc.
|
||||
|
||||
Ant Design allows you to customize some basic design aspects in order to meet the needs of UI diversity from business and brand, including primary color, border radius, border color, etc.
|
||||
In version 4.0, we provide a new way to customize themes. Different from the less and CSS variables of the 3.x version, with CSS-in-JS, the ability of theming has also been enhanced, including but not limited to:
|
||||
|
||||
![](https://zos.alipayobjects.com/rmsportal/zTFoszBtDODhXfLAazfSpYbSLSEeytoG.png)
|
||||
1. Switching theme dynamically;
|
||||
2. Multiple themes;
|
||||
3. Customizing theme variables for some component;
|
||||
4. ...
|
||||
|
||||
## Ant Design Vue Less variables
|
||||
## Customize theme with `ConfigProvider`
|
||||
|
||||
We are using [Less](http://lesscss.org/) as the development language for styling. A set of less variables are defined for each design aspect that can be customized to your needs.
|
||||
In version 4.0 we call the smallest element that affects the theme **Design Token**. By modifying the Design Token, we can present various themes or components.
|
||||
|
||||
There are some major variables below, all less variables could be found in [Default Variables](https://github.com/vueComponent/ant-design-vue/blob/main/components/style/themes/default.less).
|
||||
### Customize Design Token
|
||||
|
||||
```less
|
||||
@primary-color: #1890ff; // primary color for all components
|
||||
@link-color: #1890ff; // link color
|
||||
@success-color: #52c41a; // success state color
|
||||
@warning-color: #faad14; // warning state color
|
||||
@error-color: #f5222d; // error state color
|
||||
@font-size-base: 14px; // major text font size
|
||||
@heading-color: rgba(0, 0, 0, 0.85); // heading text color
|
||||
@text-color: rgba(0, 0, 0, 0.85); // major text color
|
||||
@text-color-secondary: rgba(0, 0, 0, 0.45); // secondary text color
|
||||
@disabled-color: rgba(0, 0, 0, 0.25); // disable state color
|
||||
@border-radius-base: 2px; // major border radius
|
||||
@border-color-base: #d9d9d9; // major border color
|
||||
@box-shadow-base: 0 2px 8px rgba(0, 0, 0, 0.15); // major shadow for layers
|
||||
You can pass `theme` to ConfigProvider to customize theme. After migrate to V4, theme of V4 will be applied by default. Here's a simple example:
|
||||
|
||||
```html
|
||||
<template>
|
||||
<a-config-provider
|
||||
:theme="{
|
||||
token: {
|
||||
colorPrimary: '#00b96b',
|
||||
},
|
||||
}"
|
||||
>
|
||||
<a-button />
|
||||
</a-config-provider>
|
||||
</template>
|
||||
```
|
||||
|
||||
Please report an issue if the existing list of variables is not enough for you.
|
||||
You will get a theme with primary color <ColorChunk color="#00b96b"></ColorChunk>. And we can see the change in Button:
|
||||
|
||||
## How to do it
|
||||
![themed button](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*CbF_RJfKEiwAAAAAAAAAAAAAARQnAQ)
|
||||
|
||||
We will use [modifyVars](http://lesscss.org/usage/#using-less-in-the-browser-modify-variables) provided by less.js to override the default values of the variables. We now introduce some popular way to do it depends on different workflow.
|
||||
### Use Preset Algorithms
|
||||
|
||||
### Customize in webpack
|
||||
Themes with different styles can be quickly generated by modifying `algorithm`. Ant Design Vue 4.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.
|
||||
|
||||
We take a typical `webpack.config.js` file as example to customize it's [less-loader](https://github.com/webpack-contrib/less-loader) options.
|
||||
```html
|
||||
<template>
|
||||
<a-config-provider
|
||||
:theme="{
|
||||
algorithm: theme.darkAlgorithm,
|
||||
}"
|
||||
>
|
||||
<a-button />
|
||||
</a-config-provider>
|
||||
</template>
|
||||
|
||||
```diff
|
||||
// webpack.config.js
|
||||
module.exports = {
|
||||
rules: [{
|
||||
test: /\.less$/,
|
||||
use: [{
|
||||
loader: 'style-loader',
|
||||
}, {
|
||||
loader: 'css-loader', // translates CSS into CommonJS
|
||||
}, {
|
||||
loader: 'less-loader', // compiles Less to CSS
|
||||
+ options: {
|
||||
+ lessOptions: {
|
||||
+ modifyVars: {
|
||||
+ 'primary-color': '#1DA57A',
|
||||
+ 'link-color': '#1DA57A',
|
||||
+ 'border-radius-base': '2px',
|
||||
+ },
|
||||
+ javascriptEnabled: true,
|
||||
+ }
|
||||
+ },
|
||||
}],
|
||||
// ...other rules
|
||||
}],
|
||||
// ...other config
|
||||
<script setup>
|
||||
import { theme } from 'ant-design-vue';
|
||||
</script>
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
```html
|
||||
<template>
|
||||
<a-config-provider
|
||||
:theme="{
|
||||
components: {
|
||||
Radio: {
|
||||
colorPrimary: '#00b96b',
|
||||
},
|
||||
},
|
||||
}"
|
||||
>
|
||||
<a-radio>Radio</a-radio>
|
||||
<a-checkbox>Checkbox</a-checkbox>
|
||||
</a-config-provider>
|
||||
</template>
|
||||
```
|
||||
|
||||
In this way, we changed the primary color of Radio to <ColorChunk color="#00b96b"></ColorChunk>, and Checkbox is not affected.
|
||||
|
||||
![component token](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*EMY0QrHFDjsAAAAAAAAAAAAAARQnAQ)
|
||||
|
||||
> Notice: `ConfigProvider` will not take effect on static methods such as `message.xxx`, `Modal.xxx`, `notification.xxx`, because in these methods, ant-design-vue will dynamically create new ones through `render` Vue entities. Its context is not the same as the context of the current code, so context information cannot be obtained. 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.
|
||||
|
||||
## Other Ways to Use Dynamic Themes
|
||||
|
||||
### Switch Themes Dynamically
|
||||
|
||||
In v4, 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.
|
||||
|
||||
```html
|
||||
<template>
|
||||
<a-config-provider
|
||||
:theme="{
|
||||
token: {
|
||||
colorPrimary: '#1677ff',
|
||||
},
|
||||
}"
|
||||
>
|
||||
<a-button />
|
||||
<a-config-provider
|
||||
:theme="{
|
||||
token: {
|
||||
colorPrimary: '#1890ff',
|
||||
},
|
||||
}"
|
||||
>
|
||||
<a-button />
|
||||
</a-config-provider>
|
||||
</a-config-provider>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Consume Design Token
|
||||
|
||||
If you want to consume the Design Token under the current theme, we provide `useToken` hook to get Design Token.
|
||||
|
||||
```html
|
||||
<template>
|
||||
<a-button :style="{ backgroundColor: token.colorPrimary }">Button</a-button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { theme } from 'ant-design-vue';
|
||||
const { useToken } = theme;
|
||||
const { token } = useToken();
|
||||
</script>
|
||||
```
|
||||
|
||||
### Static consume (e.g. less)
|
||||
|
||||
When you need token out of Vue life cycle, you can use static function to get them:
|
||||
|
||||
```jsx
|
||||
import { theme } from 'ant-design-vue';
|
||||
|
||||
const { defaultAlgorithm, defaultSeed } = theme;
|
||||
|
||||
const mapToken = defaultAlgorithm(defaultSeed);
|
||||
```
|
||||
|
||||
If you want to use in preprocess style framework like less, use less-loader for injection:
|
||||
|
||||
```jsx
|
||||
{
|
||||
loader: "less-loader",
|
||||
options: {
|
||||
lessOptions: {
|
||||
modifyVars: mapToken,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Note that do not exclude antd package in node_modules when using less-loader.
|
||||
Compatible package provide convert function to transform to v3 less variable. Read [this](/docs/vue/migration-v4) for detail.
|
||||
|
||||
### Customize in vue cli 2
|
||||
## Advanced
|
||||
|
||||
Modify the `build/utils.js` file
|
||||
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.
|
||||
|
||||
```diff
|
||||
// build/utils.js
|
||||
- less: generateLoaders('less'),
|
||||
+ less: generateLoaders('less', {
|
||||
+ modifyVars: {
|
||||
+ 'primary-color': '#1DA57A',
|
||||
+ 'link-color': '#1DA57A',
|
||||
+ 'border-radius-base': '2px',
|
||||
+ },
|
||||
+ javascriptEnabled: true,
|
||||
+ }),
|
||||
### Life Cycle of Design Token
|
||||
|
||||
```
|
||||
![token](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*uF3kTrY4InUAAAAAAAAAAAAAARQnAQ)
|
||||
|
||||
### Customize in vue cli 3
|
||||
### Seed Token
|
||||
|
||||
Create a new file `vue.config.js` in the project directory.
|
||||
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:
|
||||
|
||||
```js
|
||||
// vue.config.js for less-loader@6.0.0
|
||||
module.exports = {
|
||||
css: {
|
||||
loaderOptions: {
|
||||
less: {
|
||||
lessOptions: {
|
||||
modifyVars: {
|
||||
'primary-color': '#1DA57A',
|
||||
'link-color': '#1DA57A',
|
||||
'border-radius-base': '2px',
|
||||
},
|
||||
javascriptEnabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
```jsx
|
||||
const theme = {
|
||||
token: {
|
||||
colorPrimary: '#1890ff',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Customize in less file
|
||||
### Map Token
|
||||
|
||||
Another approach to customize theme is creating a `less` file within variables to override `antd.less`.
|
||||
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.
|
||||
|
||||
```css
|
||||
@import '~ant-design-vue/dist/antd.less'; // Import Ant Design Vue styles by less entry
|
||||
@import 'your-theme-file.less'; // variables to override above
|
||||
```
|
||||
|
||||
Note: This way will load the styles of all components, regardless of your demand, which cause `style` option of `babel-plugin-import` not working.
|
||||
|
||||
### Dynamic theme
|
||||
|
||||
Runtime update theme color please [ref this doc](/docs/react/customize-theme-variable).
|
||||
|
||||
## How to avoid modifying global styles?
|
||||
|
||||
Currently ant-design-vue is designed as a whole experience and modify global styles (eg `body` etc). If you need to integrate ant-design-vue as a part of an existing website, it's likely you want to prevent ant-design-vue to override global styles.
|
||||
|
||||
While there's no canonical way to do it, you can take one of the following paths :
|
||||
|
||||
### Configure webpack to load an alternale less file and scope global styles
|
||||
|
||||
It's possible to configure webpack to load an alternate less file:
|
||||
|
||||
```js
|
||||
new webpack.NormalModuleReplacementPlugin( /node_modules\/ant-design-vue\/lib\/style\/index\.less/, path.resolve(rootDir, 'src/myStylesReplacement.less') )
|
||||
|
||||
#antd { @import '~ant-design-vue/lib/style/core/index.less'; @import '~ant-design-vue/lib/style/themes/default.less'; }
|
||||
```
|
||||
|
||||
Where the src/myStylesReplacement.less file loads the same files as the index.less file, but loads them within the scope of a top-level selector : the result is that all of the "global" styles are being applied with the #antd scope.
|
||||
|
||||
### Use a postcss processor to scope all styles
|
||||
|
||||
See an example of usage with gulp and [postcss-prefixwrap](https://github.com/dbtedman/postcss-prefixwrap) : https://gist.github.com/sbusch/a90eafaf5a5b61c6d6172da6ff76ddaa
|
||||
|
||||
## Not working?
|
||||
|
||||
You must import styles as less format. A common mistake would be importing multiple copied of styles that some of them are css format to override the less styles.
|
||||
|
||||
- If you import styles by specifying the `style` option of [babel-plugin-import](https://github.com/ant-design/babel-plugin-import), change it from `'css'` to `true`, which will import the `less` version of antd.
|
||||
- If you import styles from `'ant-design-vue/dist/antd.css'`, change it to `ant-design-vue/dist/antd.less`.
|
||||
|
||||
## Use dark theme
|
||||
|
||||
Method 1: Import [antd.dark.less](https://unpkg.com/browse/ant-design-vue@2.0.0/dist/antd.dark.less) in the style file:
|
||||
|
||||
```less
|
||||
@import '~ant-design-vue/dist/antd.dark.less'; // Introduce the official dark less style entry file
|
||||
```
|
||||
|
||||
If the project does not use Less, you can import [antd.dark.css](https://unpkg.com/browse/ant-design-vue@2.0.0/dist/antd.dark.css) in the CSS file:
|
||||
|
||||
```css
|
||||
@import '~ant-design-vue/dist/antd.dark.css';
|
||||
```
|
||||
|
||||
> Note that you don't need to import `ant-design-vue/dist/antd.less` or `ant-design-vue/dist/antd.css` anymore, please remove it, and remove babel-plugin-import `style` config too. You can't enable two or more theme at the same time by this method.
|
||||
|
||||
Method 3: using [less-loader](https://github.com/webpack-contrib/less-loader) in `webpack.config.js` to introduce as needed:
|
||||
|
||||
```diff
|
||||
const { getThemeVariables } = require('ant-design-vue/dist/theme');
|
||||
|
||||
// webpack.config.js
|
||||
module.exports = {
|
||||
rules: [{
|
||||
test: /\.less$/,
|
||||
use: [{
|
||||
loader: 'style-loader',
|
||||
}, {
|
||||
loader: 'css-loader', // translates CSS into CommonJS
|
||||
}, {
|
||||
loader: 'less-loader', // compiles Less to CSS
|
||||
+ options: {
|
||||
+ lessOptions: { // If you are using less-loader@5 please spread the lessOptions to options directly
|
||||
+ modifyVars: getThemeVariables({
|
||||
+ dark: true, // Enable dark mode
|
||||
+ }),
|
||||
+ javascriptEnabled: true,
|
||||
+ },
|
||||
+ },
|
||||
}],
|
||||
}],
|
||||
```jsx
|
||||
const theme = {
|
||||
token: {
|
||||
colorPrimaryBg: '#e6f7ff',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Related Articles
|
||||
### Alias Token
|
||||
|
||||
- [How to Customize Ant Design with React & Webpack… the Missing Guide](https://medium.com/@GeoffMiller/how-to-customize-ant-design-with-react-webpack-the-missing-guide-c6430f2db10f)
|
||||
- [Theming Ant Design with Sass and Webpack](https://gist.github.com/Kruemelkatze/057f01b8e15216ae707dc7e6c9061ef7)
|
||||
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.
|
||||
|
||||
```jsx
|
||||
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.
|
||||
|
||||
```jsx
|
||||
import { theme } from 'ant-design-vue';
|
||||
|
||||
const { darkAlgorithm, compactAlgorithm } = theme;
|
||||
|
||||
const theme = {
|
||||
algorithm: [darkAlgorithm, compactAlgorithm],
|
||||
};
|
||||
```
|
||||
|
||||
### Legacy Browser Compatible
|
||||
|
||||
Please ref to [CSS Compatible](/docs/vue/compatible-style).
|
||||
|
||||
## 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` (Can be any antd Component name like `Button`) | Modify Component Token or override Component used Alias Token | `ComponentToken & AliasToken` | - |
|
||||
|
||||
### 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>
|
||||
|
||||
## How to Debug your Theme
|
||||
|
||||
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.
|
||||
|
@ -1,182 +1,260 @@
|
||||
# 定制主题
|
||||
|
||||
ant-design-vue 的组件结构及样式和 Antd React 完全一致,你可以参考 Antd React 的定制方式进行配置。
|
||||
Ant Design Vue 设计规范和技术上支持灵活的样式定制,以满足业务和品牌上多样化的视觉需求,包括但不限于全局样式(主色、圆角、边框)和指定组件的视觉定制。
|
||||
|
||||
Ant Design 设计规范上支持一定程度的样式定制,以满足业务和品牌上多样化的视觉需求,包括但不限于主色、圆角、边框和部分组件的视觉定制。
|
||||
在 4.0 版本的 Ant Design Vue 中,我们提供了一套全新的定制主题方案。不同于 3.x 版本的 less 和 CSS 变量,有了 CSS-in-JS 的加持后,动态主题的能力也得到了加强,包括但不限于:
|
||||
|
||||
![](https://zos.alipayobjects.com/rmsportal/zTFoszBtDODhXfLAazfSpYbSLSEeytoG.png)
|
||||
1. 支持动态切换主题;
|
||||
2. 支持同时存在多个主题;
|
||||
3. 支持针对某个/某些组件修改主题变量;
|
||||
4. ...
|
||||
|
||||
## Ant Design Vue 的样式变量
|
||||
## 在 ConfigProvider 中配置主题
|
||||
|
||||
antd 的样式使用了 [Less](http://lesscss.org/) 作为开发语言,并定义了一系列全局/组件的样式变量,你可以根据需求进行相应调整。
|
||||
在 4.0 版本中我们把影响主题的最小元素称为 **Design Token**。通过修改 Design Token,我们可以呈现出各种各样的主题或者组件。
|
||||
|
||||
以下是一些最常用的通用变量,所有样式变量可以在 [这里](https://github.com/vueComponent/ant-design-vue/blob/main/components/style/themes/default.less) 找到。
|
||||
### 修改主题变量
|
||||
|
||||
```less
|
||||
@primary-color: #1890ff; // 全局主色
|
||||
@link-color: #1890ff; // 链接色
|
||||
@success-color: #52c41a; // 成功色
|
||||
@warning-color: #faad14; // 警告色
|
||||
@error-color: #f5222d; // 错误色
|
||||
@font-size-base: 14px; // 主字号
|
||||
@heading-color: rgba(0, 0, 0, 0.85); // 标题色
|
||||
@text-color: rgba(0, 0, 0, 0.65); // 主文本色
|
||||
@text-color-secondary: rgba(0, 0, 0, 0.45); // 次文本色
|
||||
@disabled-color: rgba(0, 0, 0, 0.25); // 失效色
|
||||
@border-radius-base: 2px; // 组件/浮层圆角
|
||||
@border-color-base: #d9d9d9; // 边框色
|
||||
@box-shadow-base: 0 2px 8px rgba(0, 0, 0, 0.15); // 浮层阴影
|
||||
通过在 ConfigProvider 中传入 `theme`,可以配置主题。在升级 v4 后,将默认使用 v4 的主题,以下是将配置主题示例:
|
||||
|
||||
```html
|
||||
<template>
|
||||
<a-config-provider
|
||||
:theme="{
|
||||
token: {
|
||||
colorPrimary: '#00b96b',
|
||||
},
|
||||
}"
|
||||
>
|
||||
<a-button />
|
||||
</a-config-provider>
|
||||
</template>
|
||||
```
|
||||
|
||||
如果以上变量不能满足你的定制需求,可以给我们提 issue。
|
||||
这将会得到一个以 <ColorChunk color="#00b96b"></ColorChunk> 为主色的主题,以 Button 组件为例可以看到相应的变化:
|
||||
|
||||
## 定制方式
|
||||
![themed button](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*CbF_RJfKEiwAAAAAAAAAAAAAARQnAQ)
|
||||
|
||||
我们使用 [modifyVars](http://lesscss.org/usage/#using-less-in-the-browser-modify-variables) 的方式来进行覆盖变量。下面将针对不同的场景提供一些常用的定制方式。
|
||||
### 使用预设算法
|
||||
|
||||
### 在 webpack 中定制主题
|
||||
通过修改算法可以快速生成风格迥异的主题,4.0 版本中默认提供三套预设算法,分别是默认算法 `theme.defaultAlgorithm`、暗色算法 `theme.darkAlgorithm` 和紧凑算法 `theme.compactAlgorithm`。你可以通过修改 ConfigProvider 中 `theme` 属性的 `algorithm` 属性来切换算法。
|
||||
|
||||
我们以 webpack@4 为例进行说明,以下是一个 `webpack.config.js` 的典型例子,对 [less-loader](https://github.com/webpack-contrib/less-loader) 的 options 属性进行相应配置。
|
||||
```html
|
||||
<template>
|
||||
<a-config-provider
|
||||
:theme="{
|
||||
algorithm: theme.darkAlgorithm,
|
||||
}"
|
||||
>
|
||||
<a-button />
|
||||
</a-config-provider>
|
||||
</template>
|
||||
|
||||
```diff
|
||||
// webpack.config.js
|
||||
module.exports = {
|
||||
rules: [{
|
||||
test: /\.less$/,
|
||||
use: [{
|
||||
loader: 'style-loader',
|
||||
}, {
|
||||
loader: 'css-loader', // translates CSS into CommonJS
|
||||
}, {
|
||||
loader: 'less-loader', // compiles Less to CSS
|
||||
+ options: {
|
||||
+ lessOptions: {
|
||||
+ modifyVars: {
|
||||
+ 'primary-color': '#1DA57A',
|
||||
+ 'link-color': '#1DA57A',
|
||||
+ 'border-radius-base': '2px',
|
||||
+ },
|
||||
+ javascriptEnabled: true,
|
||||
+ }
|
||||
+ },
|
||||
}],
|
||||
// ...other rules
|
||||
}],
|
||||
// ...other config
|
||||
<script setup>
|
||||
import { theme } from 'ant-design-vue';
|
||||
</script>
|
||||
```
|
||||
|
||||
### 修改组件变量 (Component Token)
|
||||
|
||||
除了整体的 Design Token,各个组件也会开放自己的 Component Token 来实现针对组件的样式定制能力,不同的组件之间不会相互影响。同样地,也可以通过这种方式来覆盖组件的其他 Design Token。
|
||||
|
||||
```html
|
||||
<template>
|
||||
<a-config-provider
|
||||
:theme="{
|
||||
components: {
|
||||
Radio: {
|
||||
colorPrimary: '#00b96b',
|
||||
},
|
||||
},
|
||||
}"
|
||||
>
|
||||
<a-radio>Radio</a-radio>
|
||||
<a-checkbox>Checkbox</a-checkbox>
|
||||
</a-config-provider>
|
||||
</template>
|
||||
```
|
||||
|
||||
通过这种方式,我们可以仅将 Radio 组件的主色改为 <ColorChunk color="#00b96b"></ColorChunk> 而不会影响其他组件。
|
||||
|
||||
![component token](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*EMY0QrHFDjsAAAAAAAAAAAAAARQnAQ)
|
||||
|
||||
> 注意:`ConfigProvider` 对 `message.xxx`、`Modal.xxx`、`notification.xxx` ant-design-vue 会通过 `render` 动态创建新的 vue 实体。其 context 与当前代码所在 context 并不相同,因而无法获取 context 信息。当你需要 context 信息(例如 ConfigProvider 配置的内容)时,可以通过 `Modal.useModal` 方法会返回 modal 实体以及 contextHolder 节点。将其插入到你需要获取 context 位置即可。
|
||||
|
||||
## 动态主题的其他使用方式
|
||||
|
||||
### 动态切换
|
||||
|
||||
在 v4 中,动态切换主题对用户来说是非常简单的,你可以在任何时候通过 `ConfigProvider` 的 `theme` 属性来动态切换主题,而不需要任何额外配置。
|
||||
|
||||
### 局部主题
|
||||
|
||||
可以嵌套使用 `ConfigProvider` 来实现局部主题的更换。在子主题中未被改变的 Design Token 将会继承父主题。
|
||||
|
||||
```html
|
||||
<template>
|
||||
<a-config-provider
|
||||
:theme="{
|
||||
token: {
|
||||
colorPrimary: '#1677ff',
|
||||
},
|
||||
}"
|
||||
>
|
||||
<a-button />
|
||||
<a-config-provider
|
||||
:theme="{
|
||||
token: {
|
||||
colorPrimary: '#1890ff',
|
||||
},
|
||||
}"
|
||||
>
|
||||
<a-button />
|
||||
</a-config-provider>
|
||||
</a-config-provider>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 使用 Design Token
|
||||
|
||||
如果你希望使用当前主题下的 Design Token,我们提供了 `useToken` 这个 hook 来获取 Design Token。
|
||||
|
||||
```html
|
||||
<template>
|
||||
<a-button :style="{ backgroundColor: token.colorPrimary }">Button</a-button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { theme } from 'ant-design-vue';
|
||||
const { useToken } = theme;
|
||||
const { token } = useToken();
|
||||
</script>
|
||||
```
|
||||
|
||||
### 静态消费(如 less)
|
||||
|
||||
当你需要非 Vue 生命周期消费 Token 变量时,可以通过静态方法将其导出:
|
||||
|
||||
```jsx
|
||||
import { theme } from 'ant-design-vue';
|
||||
|
||||
const { defaultAlgorithm, defaultSeed } = theme;
|
||||
|
||||
const mapToken = defaultAlgorithm(defaultSeed);
|
||||
```
|
||||
|
||||
如果需要将其应用到静态样式编译框架,如 less 可以通过 less-loader 注入:
|
||||
|
||||
```jsx
|
||||
{
|
||||
loader: "less-loader",
|
||||
options: {
|
||||
lessOptions: {
|
||||
modifyVars: mapToken,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
注意 less-loader 的处理范围不要过滤掉 `node_modules` 下的 antd 包。
|
||||
兼容包提供了变量转换方法用于转成 v3 的 less 变量,如需使用[点击此处](/docs/vue/migration-v4)查看详情。
|
||||
|
||||
### 在 vue cli 2 中定制主题
|
||||
## 进阶使用
|
||||
|
||||
修改`build/utils.js`文件
|
||||
在 Design Token 中我们提供了一套更加贴合设计的三层结构,将 Design Token 拆解为 Seed Token、Map Token 和 Alias Token 三部分。这三组 Token 并不是简单的分组,而是一个三层的派生关系,由 Seed Token 派生 Map Token,再由 Map Token 派生 Alias Token。在大部分情况下,使用 Seed Token 就可以满足定制主题的需要。但如果您需要更高程度的主题定制,您需要了解 antd 中 Design Token 的生命周期。
|
||||
|
||||
```diff
|
||||
// build/utils.js
|
||||
- less: generateLoaders('less'),
|
||||
+ less: generateLoaders('less', {
|
||||
+ modifyVars: {
|
||||
+ 'primary-color': '#1DA57A',
|
||||
+ 'link-color': '#1DA57A',
|
||||
+ 'border-radius-base': '2px',
|
||||
+ },
|
||||
+ javascriptEnabled: true,
|
||||
+ }),
|
||||
### 演变过程
|
||||
|
||||
```
|
||||
![token](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*uF3kTrY4InUAAAAAAAAAAAAAARQnAQ)
|
||||
|
||||
### 在 vue cli 3 中定制主题
|
||||
### 基础变量(Seed Token)
|
||||
|
||||
项目根目录下新建文件`vue.config.js`
|
||||
Seed Token 意味着所有设计意图的起源。比如我们可以通过改变 `colorPrimary` 来改变主题色,antd 内部的算法会自动的根据 Seed Token 计算出对应的一系列颜色并应用:
|
||||
|
||||
```js
|
||||
// vue.config.js for less-loader@6.0.0
|
||||
module.exports = {
|
||||
css: {
|
||||
loaderOptions: {
|
||||
less: {
|
||||
lessOptions: {
|
||||
modifyVars: {
|
||||
'primary-color': '#1DA57A',
|
||||
'link-color': '#1DA57A',
|
||||
'border-radius-base': '2px',
|
||||
},
|
||||
javascriptEnabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
```jsx
|
||||
const theme = {
|
||||
token: {
|
||||
colorPrimary: '#1890ff',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### 配置 less 变量文件
|
||||
### 梯度变量(Map Token)
|
||||
|
||||
另外一种方式是建立一个单独的 `less` 变量文件,引入这个文件覆盖 `antd.less` 里的变量。
|
||||
Map Token 是基于 Seed 派生的梯度变量。定制 Map Token 推荐通过 `theme.algorithm` 来实现,这样可以保证 Map Token 之间的梯度关系。也可以通过 `theme.token` 覆盖,用于单独修改一些 map token 的值。
|
||||
|
||||
```css
|
||||
@import '~ant-design-vue/dist/antd.less'; // 引入官方提供的 less 样式入口文件
|
||||
@import 'your-theme-file.less'; // 用于覆盖上面定义的变量
|
||||
```
|
||||
|
||||
注意,这种方式已经载入了所有组件的样式,不需要也无法和按需加载插件 `babel-plugin-import` 的 `style` 属性一起使用。
|
||||
|
||||
### 动态主题色
|
||||
|
||||
在运行时调整主题色请[参考此处](/docs/vue/customize-theme-variable)。
|
||||
|
||||
## 没有生效?
|
||||
|
||||
注意样式必须加载 less 格式,一个常见的问题就是引入了多份样式,less 的样式被 css 的样式覆盖了。
|
||||
|
||||
- 如果你在使用 [babel-plugin-import](https://github.com/ant-design/babel-plugin-import) 的 `style` 配置来引入样式,需要将配置值从 `'css'` 改为 `true`,这样会引入 less 文件。
|
||||
- 如果你是通过 `'ant-design-vue/dist/antd.css'` 引入样式的,改为 `ant-design-vue/dist/antd.less`。
|
||||
|
||||
## 使用暗黑主题
|
||||
|
||||
方式一:在样式文件全量引入 [antd.dark.less](https://unpkg.com/browse/ant-design-vue@2.0.0/dist/antd.dark.less)。
|
||||
|
||||
```less
|
||||
@import '~ant-design-vue/dist/antd.dark.less'; // 引入官方提供的暗色 less 样式入口文件
|
||||
```
|
||||
|
||||
如果项目不使用 Less,可在 CSS 文件中全量引入 [antd.dark.css](https://unpkg.com/browse/ant-design-vue@2.0.0/dist/antd.dark.css)。
|
||||
|
||||
```css
|
||||
@import '~ant-design-vue/dist/antd.dark.css';
|
||||
```
|
||||
|
||||
> 注意这种方式下你不需要再引入 `ant-design-vue/dist/antd.less` 或 `ant-design-vue/dist/antd.css` 了,可以安全移除掉。也不需要开启 babel-plugin-import 的 `style` 配置。通过此方式不能同时配置两种及以上主题。
|
||||
|
||||
方式二:是用在 `webpack.config.js` 使用 [less-loader](https://github.com/webpack-contrib/less-loader) 按需引入:
|
||||
|
||||
```diff
|
||||
const { getThemeVariables } = require('ant-design-vue/dist/theme');
|
||||
|
||||
// webpack.config.js
|
||||
module.exports = {
|
||||
rules: [{
|
||||
test: /\.less$/,
|
||||
use: [{
|
||||
loader: 'style-loader',
|
||||
}, {
|
||||
loader: 'css-loader', // translates CSS into CommonJS
|
||||
}, {
|
||||
loader: 'less-loader', // compiles Less to CSS
|
||||
+ options: {
|
||||
+ lessOptions: { // 如果使用less-loader@5,请移除 lessOptions 这一级直接配置选项。
|
||||
+ modifyVars: getThemeVariables({
|
||||
+ dark: true, // 开启暗黑模式
|
||||
+ }),
|
||||
+ javascriptEnabled: true,
|
||||
+ },
|
||||
+ },
|
||||
}],
|
||||
}],
|
||||
```jsx
|
||||
const theme = {
|
||||
token: {
|
||||
colorPrimaryBg: '#e6f7ff',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## 社区教程 for Antd React
|
||||
### 别名变量(Alias Token)
|
||||
|
||||
- [How to Customize Ant Design with React & Webpack… the Missing Guide](https://medium.com/@GeoffMiller/how-to-customize-ant-design-with-react-webpack-the-missing-guide-c6430f2db10f)
|
||||
- [Theming Ant Design with Sass and Webpack](https://gist.github.com/Kruemelkatze/057f01b8e15216ae707dc7e6c9061ef7)
|
||||
Alias Token 用于批量控制某些共性组件的样式,基本上是 Map Token 别名,或者特殊处理过的 Map Token。
|
||||
|
||||
```jsx
|
||||
const theme = {
|
||||
token: {
|
||||
colorLink: '#1890ff',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### 基本算法(algorithm)
|
||||
|
||||
基本算法用于将 Seed Token 展开为 Map Token,比如由一个基本色算出一个梯度色板,或者由一个基本的圆角算出各种大小的圆角。算法可以单独使用,也可以任意地组合使用,比如可以将暗色算法和紧凑算法组合使用,得到一个暗色和紧凑相结合的主题。
|
||||
|
||||
```jsx
|
||||
import { theme } from 'ant-design-vue';
|
||||
|
||||
const { darkAlgorithm, compactAlgorithm } = theme;
|
||||
|
||||
const theme = {
|
||||
algorithm: [darkAlgorithm, compactAlgorithm],
|
||||
};
|
||||
```
|
||||
|
||||
### 兼容旧版浏览器
|
||||
|
||||
请参考文档 [样式兼容](/docs/vue/compatible-style-cn)。
|
||||
|
||||
## API
|
||||
|
||||
### Theme
|
||||
|
||||
| 属性 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| token | 用于修改 Design Token | `AliasToken` | - |
|
||||
| inherit | 继承上层 ConfigProvider 中配置的主题。 | boolean | true |
|
||||
| algorithm | 用于修改 Seed Token 到 Map Token 的算法 | `(token: SeedToken) => MapToken` \| `((token: SeedToken) => MapToken)[]` | `defaultAlgorithm` |
|
||||
| components | 用于修改各个组件的 Component Token 以及覆盖该组件消费的 Alias Token | OverrideToken | - |
|
||||
|
||||
### OverrideToken
|
||||
|
||||
| 属性 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| `Component` (可以是任意 antd 组件名,如 `Button`) | 用于修改 Component Token 以及覆盖该组件消费的 Alias Token | `ComponentToken & AliasToken` | - |
|
||||
|
||||
### SeedToken
|
||||
|
||||
<TokenTable type="seed"></TokenTable>
|
||||
|
||||
### MapToken
|
||||
|
||||
> 继承所有 SeedToken 的属性
|
||||
|
||||
<TokenTable type="map"></TokenTable>
|
||||
|
||||
### AliasToken
|
||||
|
||||
> 继承所有 SeedToken 和 MapToken 的属性
|
||||
|
||||
<TokenTable type="alias"></TokenTable>
|
||||
|
||||
## 调试主题
|
||||
|
||||
我们提供了帮助用户调试主题的工具:[主题编辑器](/theme-editor-cn)
|
||||
|
||||
你可以使用此工具自由地修改 Design Token,以达到您对主题的期望。
|
||||
|
@ -40,19 +40,14 @@ This document will help you upgrade from ant-design-vue `3.x` version to ant-des
|
||||
- DatePicker
|
||||
- Mentions
|
||||
|
||||
```html
|
||||
<template>
|
||||
<a-select - dropdownClassName="my-select-popup" + popupClassName="my-select-popup" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
});
|
||||
</script>
|
||||
```
|
||||
```diff
|
||||
<template>
|
||||
<a-select
|
||||
-- dropdownClassName="my-select-popup"
|
||||
++ popupClassName="my-select-popup"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
- The controlled visible API of the component popup is unified to `open`, and `visible` and other similar APIs will be replaced.
|
||||
|
||||
@ -64,50 +59,36 @@ This document will help you upgrade from ant-design-vue `3.x` version to ant-des
|
||||
- Slider `tooltip` related API converged to `tooltip` property.
|
||||
- Table `filterDropdownVisible` changed to `filterDropdownOpen`.
|
||||
|
||||
```html
|
||||
<template>
|
||||
-
|
||||
<a-modal :visible="visible">content</a-modal>
|
||||
+
|
||||
<a-modal :open="visible">content</a-modal>
|
||||
```diff
|
||||
<template>
|
||||
-- <a-modal :visible="visible">content</a-modal>
|
||||
++ <a-modal :open="visible">content</a-modal>
|
||||
|
||||
-
|
||||
<a-tag :visible="visible">tag</a-tag>
|
||||
+
|
||||
<a-tag v-if="visible">tag</a-tag>
|
||||
-- <a-tag :visible="visible">tag</a-tag>
|
||||
++ <a-tag v-if="visible">tag</a-tag>
|
||||
|
||||
<a-table
|
||||
:data="[]"
|
||||
:columns="[
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
- filterDropdownVisible: visible,
|
||||
+ filterDropdownOpen: visible,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
|
||||
-
|
||||
<a-slider :tooltipVisible="visible" />
|
||||
+
|
||||
<a-slider :tooltip="{ open: visible }" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
setup() {
|
||||
const visible = ref(true);
|
||||
return {
|
||||
visible,
|
||||
};
|
||||
<a-table
|
||||
:data="[]"
|
||||
:columns="[
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
-- filterDropdownVisible: visible,
|
||||
++ filterDropdownOpen: visible,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
```
|
||||
]"
|
||||
/>
|
||||
|
||||
-- <a-slider :tooltipVisible="visible" />
|
||||
++ <a-slider :tooltip="{ open: visible }" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const visible = ref(true);
|
||||
</script>
|
||||
```
|
||||
|
||||
- `getPopupContainer`: All `getPopupContainer` are guaranteed to return a unique div.
|
||||
- Drawer `style` & `class` are migrated to Drawer panel node, the original properties are replaced by `rootClassName` and `rootStyle`.
|
||||
@ -165,7 +146,7 @@ Remove `babel-plugin-import` from package.json and modify `.babelrc`:
|
||||
|
||||
```diff
|
||||
"plugins": [
|
||||
- ["import", { "libraryName": "ant-design-vue", "libraryDirectory": "lib"}, "ant-design-vue"],
|
||||
-- ["import", { "libraryName": "ant-design-vue", "libraryDirectory": "lib"}, "ant-design-vue"],
|
||||
]
|
||||
```
|
||||
|
||||
|
@ -40,19 +40,14 @@
|
||||
- DatePicker 组件
|
||||
- Mentions 组件
|
||||
|
||||
```html
|
||||
<template>
|
||||
<a-select - dropdownClassName="my-select-popup" + popupClassName="my-select-popup" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
});
|
||||
</script>
|
||||
```
|
||||
```diff
|
||||
<template>
|
||||
<a-select
|
||||
-- dropdownClassName="my-select-popup"
|
||||
++ popupClassName="my-select-popup"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
- 组件弹框的受控可见 API 统一为 `open`,`visible` 等类似 API 都会被替换。
|
||||
|
||||
@ -64,50 +59,36 @@
|
||||
- Slider 组件 `tooltip` 相关 API 收敛到 `tooltip` 属性中。
|
||||
- Table 组件 `filterDropdownVisible` 变为 `filterDropdownOpen`。
|
||||
|
||||
```html
|
||||
<template>
|
||||
-
|
||||
<a-modal :visible="visible">content</a-modal>
|
||||
+
|
||||
<a-modal :open="visible">content</a-modal>
|
||||
```diff
|
||||
<template>
|
||||
-- <a-modal :visible="visible">content</a-modal>
|
||||
++ <a-modal :open="visible">content</a-modal>
|
||||
|
||||
-
|
||||
<a-tag :visible="visible">tag</a-tag>
|
||||
+
|
||||
<a-tag v-if="visible">tag</a-tag>
|
||||
-- <a-tag :visible="visible">tag</a-tag>
|
||||
++ <a-tag v-if="visible">tag</a-tag>
|
||||
|
||||
<a-table
|
||||
:data="[]"
|
||||
:columns="[
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
- filterDropdownVisible: visible,
|
||||
+ filterDropdownOpen: visible,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
|
||||
-
|
||||
<a-slider :tooltipVisible="visible" />
|
||||
+
|
||||
<a-slider :tooltip="{ open: visible }" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
setup() {
|
||||
const visible = ref(true);
|
||||
return {
|
||||
visible,
|
||||
};
|
||||
<a-table
|
||||
:data="[]"
|
||||
:columns="[
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
-- filterDropdownVisible: visible,
|
||||
++ filterDropdownOpen: visible,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
```
|
||||
]"
|
||||
/>
|
||||
|
||||
-- <a-slider :tooltipVisible="visible" />
|
||||
++ <a-slider :tooltip="{ open: visible }" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const visible = ref(true);
|
||||
</script>
|
||||
```
|
||||
|
||||
- `getPopupContainer`: 所有的 `getPopupContainer` 都需要保证返回的是唯一的 div。
|
||||
- Drawer `style` 和 `class` 迁移至 Drawer 弹层区域上,原属性替换为 `rootClassName` 和 `rootStyle`。
|
||||
@ -166,7 +147,7 @@ or
|
||||
|
||||
```diff
|
||||
"plugins": [
|
||||
- ["import", { "libraryName": "ant-design-vue", "libraryDirectory": "lib"}, "ant-design-vue"],
|
||||
-- ["import", { "libraryName": "ant-design-vue", "libraryDirectory": "lib"}, "ant-design-vue"],
|
||||
]
|
||||
```
|
||||
|
||||
|
@ -12,6 +12,7 @@ export default {
|
||||
vue: 'vue/dist/vue.esm-bundler.js',
|
||||
'ant-design-vue/es': path.resolve(__dirname, '../components'),
|
||||
'ant-design-vue': path.resolve(__dirname, '../components'),
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
|
Loading…
Reference in New Issue
Block a user