@veltra/styles 1.2.34 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { componentCssVarsDarkDecls, componentCssVarsLightDecls } from "./component-css-vars.js";
2
- import { hexRgbOnly, hexWithAlpha, mixColor } from "./helper.js";
2
+ import { hexRgbOnly, hexWithAlpha, isHexColor, mixColor } from "./helper.js";
3
3
  import { isObj, str } from "@cat-kit/core";
4
4
  import { withUnit } from "@veltra/utils";
5
5
  import { reactive, toRaw, watch } from "vue";
@@ -64,9 +64,12 @@ var UITheme = class UITheme {
64
64
  renderBGColorAlpha(theme) {
65
65
  const { color } = theme.bg;
66
66
  const alphas = [70];
67
- const lines = Object.keys(color).map((type) => `--u-bg-color-${type}-alpha: ${color[type]}aa`);
67
+ const lines = [];
68
68
  for (const type of Object.keys(color)) {
69
- const hex = color[type];
69
+ const value = color[type];
70
+ if (typeof value !== "string" || !isHexColor(value)) continue;
71
+ const hex = value;
72
+ lines.push(`--u-bg-color-${type}-alpha: ${hex}aa`);
70
73
  for (const a of alphas) lines.push(`--u-bg-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`);
71
74
  }
72
75
  return lines.join(";");
@@ -92,17 +95,19 @@ var UITheme = class UITheme {
92
95
  ];
93
96
  const lines = [];
94
97
  for (const type of Object.keys(theme.color)) {
95
- const hex = theme.color[type];
98
+ const value = theme.color[type];
99
+ if (typeof value !== "string" || !isHexColor(value)) continue;
100
+ const hex = value;
96
101
  for (const a of alphas) lines.push(`--u-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`);
97
102
  }
98
103
  const borderHex = theme.border.color;
99
- for (const a of alphas) lines.push(`--u-border-color-a-${a}: ${hexWithAlpha(borderHex, a)}`);
104
+ if (isHexColor(borderHex)) for (const a of alphas) lines.push(`--u-border-color-a-${a}: ${hexWithAlpha(borderHex, a)}`);
100
105
  const shadowHex = hexRgbOnly(theme.shadow.color);
101
- for (const a of alphas) lines.push(`--u-shadow-color-a-${a}: ${hexWithAlpha(shadowHex, a)}`);
106
+ if (isHexColor(shadowHex)) for (const a of alphas) lines.push(`--u-shadow-color-a-${a}: ${hexWithAlpha(shadowHex, a)}`);
102
107
  const textColor = theme["text-color"];
103
108
  for (const type of Object.keys(textColor)) {
104
109
  const value = textColor[type];
105
- if (typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value)) {
110
+ if (typeof value === "string" && isHexColor(value)) {
106
111
  const hex = value;
107
112
  for (const a of alphas) lines.push(`--u-text-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`);
108
113
  }
@@ -117,6 +122,13 @@ var UITheme = class UITheme {
117
122
  const primary = theme.color.primary;
118
123
  const borderColor = theme.border.color;
119
124
  const shadowColor = hexRgbOnly(theme.shadow.color);
125
+ if (![
126
+ hover,
127
+ top,
128
+ black,
129
+ primary,
130
+ borderColor
131
+ ].every(isHexColor)) return "";
120
132
  const itemBg = mixColor(hover, top, .28);
121
133
  const itemHoverBg = mixColor(primary, itemBg, .96);
122
134
  const itemActiveBg = mixColor(primary, itemBg, .95);
@@ -124,6 +136,7 @@ var UITheme = class UITheme {
124
136
  const contentBg = mixColor(itemActiveBg, top, .58);
125
137
  const formHeaderBg = mixColor(top, hover, .04);
126
138
  const kbdInset = mixColor(top, black, .25);
139
+ const kbdDropShadow = isHexColor(shadowColor) ? hexWithAlpha(shadowColor, 60) : "rgba(0, 0, 0, 0.6)";
127
140
  return [
128
141
  `--u-collapse-item-bg: ${itemBg}`,
129
142
  `--u-collapse-item-hover-bg: ${itemHoverBg}`,
@@ -133,7 +146,7 @@ var UITheme = class UITheme {
133
146
  `--u-batch-edit-form-header-bg: ${formHeaderBg}`,
134
147
  `--u-kbd-inset-shadow: ${kbdInset}`,
135
148
  `--u-kbd-border-shadow: ${hexWithAlpha(borderColor, 50)}`,
136
- `--u-kbd-drop-shadow: ${hexWithAlpha(shadowColor, 60)}`
149
+ `--u-kbd-drop-shadow: ${kbdDropShadow}`
137
150
  ].join(";");
138
151
  }
139
152
  renderBGFilter(theme) {
@@ -1 +1 @@
1
- {"version":3,"file":"ui-theme.js","names":[],"sources":["../../src/theme/ui-theme.ts"],"sourcesContent":["import { isObj, str } from '@cat-kit/core'\nimport { withUnit } from '@veltra/utils'\nimport { reactive, toRaw, watch } from 'vue'\n\nimport { componentCssVarsDarkDecls, componentCssVarsLightDecls } from './component-css-vars'\nimport { hexRgbOnly, hexWithAlpha, mixColor } from './helper'\nimport type { Theme } from './type'\n\ntype RecursivePartial<T> = {\n [P in keyof T]?: T[P] extends object ? RecursivePartial<T[P]> : T[P]\n}\n\nexport class UITheme {\n static themeID = 'ultra-ui-theme'\n\n private static adoptedSheet: CSSStyleSheet | null = null\n\n readonly theme: Theme\n\n private readonly reactiveEnabled: boolean\n\n constructor(theme: Theme, options?: { reactive?: boolean }) {\n this.reactiveEnabled = options?.reactive !== false\n this.theme = reactive(theme) as Theme\n if (this.reactiveEnabled) {\n watch(this.theme, () => this.render(), { deep: true })\n }\n }\n\n static setTheme(mode: 'light' | 'dark' | 'auto'): void {\n if (typeof document === 'undefined') return\n const el = document.documentElement\n if (mode === 'auto') {\n delete el.dataset.theme\n } else {\n el.dataset.theme = mode\n }\n }\n\n private renderBase(\n theme: Record<string, unknown>,\n themeRules: string[] = [],\n parentKey = '--u'\n ): string[] {\n Object.keys(theme).forEach((key) => {\n const value = theme[key]\n const varKey = `${parentKey.startsWith('-') ? parentKey : str(parentKey).kebabCase()}-${str(key).kebabCase()}`\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n this.renderBase(value as Record<string, unknown>, themeRules, varKey)\n } else {\n if (value || value === 0) {\n const v = withUnit(value as number | string, 'px')\n if (v !== undefined) {\n themeRules.push(`${varKey}: ${v}`)\n }\n }\n }\n })\n\n return themeRules\n }\n\n private renderBorder(theme: Theme): string {\n const shorthandKeys = (Object.keys(theme.border) as Array<keyof typeof theme.border>).filter(\n (key) => key !== 'mutedColor'\n )\n const border = shorthandKeys.map((key) => `var(--u-border-${key})`).join(' ')\n\n return [\n `--u-border: ${border}`,\n `--u-border-muted: var(--u-border-muted-color) var(--u-border-width) var(--u-border-style)`\n ].join(';')\n }\n\n private renderTypeColor(theme: Theme): string {\n const { color } = theme\n\n const types = Object.keys(color)\n const rates = [1, 3, 5, 7, 9]\n\n return types\n .map((type) => {\n const colorValue = color[type as keyof typeof color]! as `#${string}`\n\n return rates\n .map((rate) => {\n const light = `--u-color-${type}-light-${rate}: ${mixColor(\n colorValue,\n '#fff',\n rate / 10\n )}`\n const dark = `--u-color-${type}-dark-${rate}: ${mixColor(\n colorValue,\n '#000',\n rate / 10\n )}`\n return `${light};${dark}`\n })\n .join(';')\n })\n .join(';')\n }\n\n private renderShadow(_theme: Theme): string {\n const shadow = ['x', 'y', 'blur', 'spread', 'color']\n .map((k) => `var(--u-shadow-${k})`)\n .join(' ')\n\n return `--u-shadow: ${shadow}`\n }\n\n private renderBGColorAlpha(theme: Theme): string {\n const { color } = theme.bg\n const alphas = [70]\n const lines: string[] = Object.keys(color).map(\n (type) => `--u-bg-color-${type}-alpha: ${color[type as keyof typeof color]}aa`\n )\n for (const type of Object.keys(color)) {\n const hex = color[type as keyof typeof color] as `#${string}`\n for (const a of alphas) {\n lines.push(`--u-bg-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`)\n }\n }\n return lines.join(';')\n }\n\n /** 语义色 / 边框 / 阴影 / 文本色的 alpha token(替代 color-mix + transparent) */\n private renderAlphaTokens(theme: Theme): string {\n const alphas = [4, 5, 8, 10, 11, 16, 22, 28, 35, 40, 50, 52, 60, 70, 86]\n const lines: string[] = []\n\n for (const type of Object.keys(theme.color)) {\n const hex = theme.color[type as keyof typeof theme.color] as `#${string}`\n for (const a of alphas) {\n lines.push(`--u-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`)\n }\n }\n\n const borderHex = theme.border.color as `#${string}`\n for (const a of alphas) {\n lines.push(`--u-border-color-a-${a}: ${hexWithAlpha(borderHex, a)}`)\n }\n\n const shadowHex = hexRgbOnly(theme.shadow.color)\n for (const a of alphas) {\n lines.push(`--u-shadow-color-a-${a}: ${hexWithAlpha(shadowHex, a)}`)\n }\n\n const textColor = theme['text-color']\n for (const type of Object.keys(textColor)) {\n const value = textColor[type as keyof typeof textColor]\n if (typeof value === 'string' && /^#[0-9a-fA-F]{6}$/.test(value)) {\n const hex = value as `#${string}`\n for (const a of alphas) {\n lines.push(`--u-text-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`)\n }\n }\n }\n\n return lines.join(';')\n }\n\n /** 组件级两色混合 token(collapse / batch-edit / kbd) */\n private renderComponentMixTokens(theme: Theme): string {\n const hover = theme.bg.color.hover as `#${string}`\n const top = theme.bg.color.top as `#${string}`\n const black = theme.bg.color.black as `#${string}`\n const primary = theme.color.primary as `#${string}`\n const borderColor = theme.border.color as `#${string}`\n const shadowColor = hexRgbOnly(theme.shadow.color)\n\n const itemBg = mixColor(hover, top, 0.28)\n const itemHoverBg = mixColor(primary, itemBg as `#${string}`, 0.96)\n const itemActiveBg = mixColor(primary, itemBg as `#${string}`, 0.95)\n const headerActiveBg = mixColor(primary, itemBg as `#${string}`, 0.92)\n const contentBg = mixColor(itemActiveBg as `#${string}`, top, 0.58)\n\n const formHeaderBg = mixColor(top, hover, 0.04)\n\n const kbdInset = mixColor(top, black, 0.25)\n\n const lines = [\n `--u-collapse-item-bg: ${itemBg}`,\n `--u-collapse-item-hover-bg: ${itemHoverBg}`,\n `--u-collapse-item-active-bg: ${itemActiveBg}`,\n `--u-collapse-header-active-bg: ${headerActiveBg}`,\n `--u-collapse-content-bg: ${contentBg}`,\n `--u-batch-edit-form-header-bg: ${formHeaderBg}`,\n `--u-kbd-inset-shadow: ${kbdInset}`,\n `--u-kbd-border-shadow: ${hexWithAlpha(borderColor, 50)}`,\n `--u-kbd-drop-shadow: ${hexWithAlpha(shadowColor, 60)}`\n ]\n\n return lines.join(';')\n }\n\n private renderBGFilter(theme: Theme): string {\n const { filter } = theme.bg\n const value = filter.blur === 'none' ? 'none' : `${filter.blur} ${filter.saturate}`\n return `--u-bg-filter: ${value}`\n }\n\n /** 主题变量声明列表,均为 `--u-` 前缀的 CSS 自定义属性 */\n themeToDeclarationList(theme: Theme): string[] {\n const raw = theme as unknown as Record<string, unknown>\n const lines: string[] = [...this.renderBase(raw)]\n const chunks = [\n this.renderTypeColor(theme),\n this.renderShadow(theme),\n this.renderBGColorAlpha(theme),\n this.renderAlphaTokens(theme),\n this.renderComponentMixTokens(theme),\n this.renderBGFilter(theme),\n this.renderBorder(theme)\n ]\n for (const c of chunks) {\n lines.push(\n ...c\n .split(';')\n .map((s) => s.trim())\n .filter(Boolean)\n )\n }\n return lines\n }\n\n private static declarationBlock(decls: string[]): string {\n return decls.join(';')\n }\n\n private static removeExistingStyleTag(doc: Document): void {\n const el = doc.getElementById(UITheme.themeID)\n if (el?.parentNode) {\n el.parentNode.removeChild(el)\n }\n }\n\n private static applyGlobalCSS(css: string): void {\n const doc = typeof document !== 'undefined' ? document : undefined\n if (!doc) return\n\n const canAdopt =\n typeof CSSStyleSheet !== 'undefined' && 'adoptedStyleSheets' in Document.prototype\n\n if (canAdopt) {\n UITheme.removeExistingStyleTag(doc)\n if (!UITheme.adoptedSheet) {\n UITheme.adoptedSheet = new CSSStyleSheet()\n doc.adoptedStyleSheets = [...doc.adoptedStyleSheets, UITheme.adoptedSheet]\n }\n UITheme.adoptedSheet.replaceSync(css)\n } else {\n if (UITheme.adoptedSheet) {\n UITheme.adoptedSheet = null\n }\n let style = doc.getElementById(UITheme.themeID)\n if (!style) {\n style = doc.createElement('style')\n style.id = UITheme.themeID\n doc.head.appendChild(style)\n }\n style.textContent = css\n }\n }\n\n /** 内置 light/dark:支持 data-theme 与 prefers-color-scheme */\n static injectBuiltInThemes(light: UITheme, dark: UITheme): void {\n const lightDecls = [\n ...light.themeToDeclarationList(toRaw(light.theme)),\n ...componentCssVarsLightDecls\n ]\n const darkDecls = [\n ...dark.themeToDeclarationList(toRaw(dark.theme)),\n ...componentCssVarsDarkDecls\n ]\n const lightBlock = UITheme.declarationBlock(lightDecls)\n const darkBlock = UITheme.declarationBlock(darkDecls)\n\n const css = [\n `html { ${lightBlock} }`,\n `@media (prefers-color-scheme: dark) {`,\n ` html:not([data-theme=\"light\"]) { ${darkBlock} }`,\n `}`,\n `html[data-theme=\"light\"] { ${lightBlock} }`,\n `html[data-theme=\"dark\"] { ${darkBlock} }`\n ].join('\\n')\n\n UITheme.applyGlobalCSS(css)\n }\n\n render(): void {\n const isDark =\n typeof document !== 'undefined' && document.documentElement.dataset.theme === 'dark'\n const componentDecls = isDark ? componentCssVarsDarkDecls : componentCssVarsLightDecls\n const decls = [...this.themeToDeclarationList(toRaw(this.theme)), ...componentDecls]\n const block = UITheme.declarationBlock(decls)\n const css = `html { ${block} }`\n UITheme.applyGlobalCSS(css)\n }\n\n new(customTheme: RecursivePartial<Theme> = {}): UITheme {\n function delEmpty(obj: Record<string, unknown>) {\n Object.keys(obj).forEach((key) => {\n const value = obj[key]\n if (isObj(value)) {\n return delEmpty(value as Record<string, unknown>)\n }\n if (!value && value !== 0) {\n delete obj[key]\n }\n })\n }\n\n delEmpty(customTheme as Record<string, unknown>)\n\n const base = JSON.parse(JSON.stringify(toRaw(this.theme))) as Record<string, any>\n\n function deepMerge(target: any, source: any) {\n if (typeof source !== 'object' || source === null) return\n Object.keys(source).forEach((key) => {\n const sourceVal = source[key]\n if (typeof sourceVal === 'object' && sourceVal !== null && !Array.isArray(sourceVal)) {\n if (typeof target[key] !== 'object' || target[key] === null) {\n target[key] = {}\n }\n deepMerge(target[key], sourceVal)\n } else {\n target[key] = sourceVal\n }\n })\n }\n\n deepMerge(base, customTheme)\n return new UITheme(base as Theme, { reactive: this.reactiveEnabled })\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ui-theme.js","names":[],"sources":["../../src/theme/ui-theme.ts"],"sourcesContent":["import { isObj, str } from '@cat-kit/core'\nimport { withUnit } from '@veltra/utils'\nimport { reactive, toRaw, watch } from 'vue'\n\nimport { componentCssVarsDarkDecls, componentCssVarsLightDecls } from './component-css-vars'\nimport { hexRgbOnly, hexWithAlpha, isHexColor, mixColor } from './helper'\nimport type { Theme } from './type'\n\ntype RecursivePartial<T> = {\n [P in keyof T]?: T[P] extends object ? RecursivePartial<T[P]> : T[P]\n}\n\nexport class UITheme {\n static themeID = 'ultra-ui-theme'\n\n private static adoptedSheet: CSSStyleSheet | null = null\n\n readonly theme: Theme\n\n private readonly reactiveEnabled: boolean\n\n constructor(theme: Theme, options?: { reactive?: boolean }) {\n this.reactiveEnabled = options?.reactive !== false\n this.theme = reactive(theme) as Theme\n if (this.reactiveEnabled) {\n watch(this.theme, () => this.render(), { deep: true })\n }\n }\n\n static setTheme(mode: 'light' | 'dark' | 'auto'): void {\n if (typeof document === 'undefined') return\n const el = document.documentElement\n if (mode === 'auto') {\n delete el.dataset.theme\n } else {\n el.dataset.theme = mode\n }\n }\n\n private renderBase(\n theme: Record<string, unknown>,\n themeRules: string[] = [],\n parentKey = '--u'\n ): string[] {\n Object.keys(theme).forEach((key) => {\n const value = theme[key]\n const varKey = `${parentKey.startsWith('-') ? parentKey : str(parentKey).kebabCase()}-${str(key).kebabCase()}`\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n this.renderBase(value as Record<string, unknown>, themeRules, varKey)\n } else {\n if (value || value === 0) {\n const v = withUnit(value as number | string, 'px')\n if (v !== undefined) {\n themeRules.push(`${varKey}: ${v}`)\n }\n }\n }\n })\n\n return themeRules\n }\n\n private renderBorder(theme: Theme): string {\n const shorthandKeys = (Object.keys(theme.border) as Array<keyof typeof theme.border>).filter(\n (key) => key !== 'mutedColor'\n )\n const border = shorthandKeys.map((key) => `var(--u-border-${key})`).join(' ')\n\n return [\n `--u-border: ${border}`,\n `--u-border-muted: var(--u-border-muted-color) var(--u-border-width) var(--u-border-style)`\n ].join(';')\n }\n\n private renderTypeColor(theme: Theme): string {\n const { color } = theme\n\n const types = Object.keys(color)\n const rates = [1, 3, 5, 7, 9]\n\n return types\n .map((type) => {\n const colorValue = color[type as keyof typeof color]! as `#${string}`\n\n return rates\n .map((rate) => {\n const light = `--u-color-${type}-light-${rate}: ${mixColor(\n colorValue,\n '#fff',\n rate / 10\n )}`\n const dark = `--u-color-${type}-dark-${rate}: ${mixColor(\n colorValue,\n '#000',\n rate / 10\n )}`\n return `${light};${dark}`\n })\n .join(';')\n })\n .join(';')\n }\n\n private renderShadow(_theme: Theme): string {\n const shadow = ['x', 'y', 'blur', 'spread', 'color']\n .map((k) => `var(--u-shadow-${k})`)\n .join(' ')\n\n return `--u-shadow: ${shadow}`\n }\n\n private renderBGColorAlpha(theme: Theme): string {\n const { color } = theme.bg\n const alphas = [70]\n const lines: string[] = []\n for (const type of Object.keys(color)) {\n const value = color[type as keyof typeof color]\n // 非 hex 颜色(如 glass 主题的 rgba())无法推导 alpha token,跳过避免生成 NaN 声明\n if (typeof value !== 'string' || !isHexColor(value)) continue\n const hex = value as `#${string}`\n lines.push(`--u-bg-color-${type}-alpha: ${hex}aa`)\n for (const a of alphas) {\n lines.push(`--u-bg-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`)\n }\n }\n return lines.join(';')\n }\n\n /** 语义色 / 边框 / 阴影 / 文本色的 alpha token(替代 color-mix + transparent) */\n private renderAlphaTokens(theme: Theme): string {\n const alphas = [4, 5, 8, 10, 11, 16, 22, 28, 35, 40, 50, 52, 60, 70, 86]\n const lines: string[] = []\n\n for (const type of Object.keys(theme.color)) {\n const value = theme.color[type as keyof typeof theme.color]\n if (typeof value !== 'string' || !isHexColor(value)) continue\n const hex = value as `#${string}`\n for (const a of alphas) {\n lines.push(`--u-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`)\n }\n }\n\n const borderHex = theme.border.color as `#${string}`\n if (isHexColor(borderHex)) {\n for (const a of alphas) {\n lines.push(`--u-border-color-a-${a}: ${hexWithAlpha(borderHex, a)}`)\n }\n }\n\n const shadowHex = hexRgbOnly(theme.shadow.color)\n // 阴影色非 hex(如 rgba())时无法推导 alpha token,跳过避免生成 NaN 声明\n if (isHexColor(shadowHex)) {\n for (const a of alphas) {\n lines.push(`--u-shadow-color-a-${a}: ${hexWithAlpha(shadowHex, a)}`)\n }\n }\n\n const textColor = theme['text-color']\n for (const type of Object.keys(textColor)) {\n const value = textColor[type as keyof typeof textColor]\n if (typeof value === 'string' && isHexColor(value)) {\n const hex = value as `#${string}`\n for (const a of alphas) {\n lines.push(`--u-text-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`)\n }\n }\n }\n\n return lines.join(';')\n }\n\n /** 组件级两色混合 token(collapse / batch-edit / kbd) */\n private renderComponentMixTokens(theme: Theme): string {\n const hover = theme.bg.color.hover as `#${string}`\n const top = theme.bg.color.top as `#${string}`\n const black = theme.bg.color.black as `#${string}`\n const primary = theme.color.primary as `#${string}`\n const borderColor = theme.border.color as `#${string}`\n const shadowColor = hexRgbOnly(theme.shadow.color)\n\n // 依赖色含非 hex 值(如 glass 主题的 rgba())时整体跳过,避免生成 NaN 声明;\n // 组件侧 var() fallback 会接管,行为等同于此前的无效声明\n if (![hover, top, black, primary, borderColor].every(isHexColor)) return ''\n\n const itemBg = mixColor(hover, top, 0.28)\n const itemHoverBg = mixColor(primary, itemBg as `#${string}`, 0.96)\n const itemActiveBg = mixColor(primary, itemBg as `#${string}`, 0.95)\n const headerActiveBg = mixColor(primary, itemBg as `#${string}`, 0.92)\n const contentBg = mixColor(itemActiveBg as `#${string}`, top, 0.58)\n\n const formHeaderBg = mixColor(top, hover, 0.04)\n\n const kbdInset = mixColor(top, black, 0.25)\n const kbdDropShadow = isHexColor(shadowColor)\n ? hexWithAlpha(shadowColor, 60)\n : 'rgba(0, 0, 0, 0.6)'\n\n const lines = [\n `--u-collapse-item-bg: ${itemBg}`,\n `--u-collapse-item-hover-bg: ${itemHoverBg}`,\n `--u-collapse-item-active-bg: ${itemActiveBg}`,\n `--u-collapse-header-active-bg: ${headerActiveBg}`,\n `--u-collapse-content-bg: ${contentBg}`,\n `--u-batch-edit-form-header-bg: ${formHeaderBg}`,\n `--u-kbd-inset-shadow: ${kbdInset}`,\n `--u-kbd-border-shadow: ${hexWithAlpha(borderColor, 50)}`,\n `--u-kbd-drop-shadow: ${kbdDropShadow}`\n ]\n\n return lines.join(';')\n }\n\n private renderBGFilter(theme: Theme): string {\n const { filter } = theme.bg\n const value = filter.blur === 'none' ? 'none' : `${filter.blur} ${filter.saturate}`\n return `--u-bg-filter: ${value}`\n }\n\n /** 主题变量声明列表,均为 `--u-` 前缀的 CSS 自定义属性 */\n themeToDeclarationList(theme: Theme): string[] {\n const raw = theme as unknown as Record<string, unknown>\n const lines: string[] = [...this.renderBase(raw)]\n const chunks = [\n this.renderTypeColor(theme),\n this.renderShadow(theme),\n this.renderBGColorAlpha(theme),\n this.renderAlphaTokens(theme),\n this.renderComponentMixTokens(theme),\n this.renderBGFilter(theme),\n this.renderBorder(theme)\n ]\n for (const c of chunks) {\n lines.push(\n ...c\n .split(';')\n .map((s) => s.trim())\n .filter(Boolean)\n )\n }\n return lines\n }\n\n private static declarationBlock(decls: string[]): string {\n return decls.join(';')\n }\n\n private static removeExistingStyleTag(doc: Document): void {\n const el = doc.getElementById(UITheme.themeID)\n if (el?.parentNode) {\n el.parentNode.removeChild(el)\n }\n }\n\n private static applyGlobalCSS(css: string): void {\n const doc = typeof document !== 'undefined' ? document : undefined\n if (!doc) return\n\n const canAdopt =\n typeof CSSStyleSheet !== 'undefined' && 'adoptedStyleSheets' in Document.prototype\n\n if (canAdopt) {\n UITheme.removeExistingStyleTag(doc)\n if (!UITheme.adoptedSheet) {\n UITheme.adoptedSheet = new CSSStyleSheet()\n doc.adoptedStyleSheets = [...doc.adoptedStyleSheets, UITheme.adoptedSheet]\n }\n UITheme.adoptedSheet.replaceSync(css)\n } else {\n if (UITheme.adoptedSheet) {\n UITheme.adoptedSheet = null\n }\n let style = doc.getElementById(UITheme.themeID)\n if (!style) {\n style = doc.createElement('style')\n style.id = UITheme.themeID\n doc.head.appendChild(style)\n }\n style.textContent = css\n }\n }\n\n /** 内置 light/dark:支持 data-theme 与 prefers-color-scheme */\n static injectBuiltInThemes(light: UITheme, dark: UITheme): void {\n const lightDecls = [\n ...light.themeToDeclarationList(toRaw(light.theme)),\n ...componentCssVarsLightDecls\n ]\n const darkDecls = [\n ...dark.themeToDeclarationList(toRaw(dark.theme)),\n ...componentCssVarsDarkDecls\n ]\n const lightBlock = UITheme.declarationBlock(lightDecls)\n const darkBlock = UITheme.declarationBlock(darkDecls)\n\n const css = [\n `html { ${lightBlock} }`,\n `@media (prefers-color-scheme: dark) {`,\n ` html:not([data-theme=\"light\"]) { ${darkBlock} }`,\n `}`,\n `html[data-theme=\"light\"] { ${lightBlock} }`,\n `html[data-theme=\"dark\"] { ${darkBlock} }`\n ].join('\\n')\n\n UITheme.applyGlobalCSS(css)\n }\n\n render(): void {\n const isDark =\n typeof document !== 'undefined' && document.documentElement.dataset.theme === 'dark'\n const componentDecls = isDark ? componentCssVarsDarkDecls : componentCssVarsLightDecls\n const decls = [...this.themeToDeclarationList(toRaw(this.theme)), ...componentDecls]\n const block = UITheme.declarationBlock(decls)\n const css = `html { ${block} }`\n UITheme.applyGlobalCSS(css)\n }\n\n new(customTheme: RecursivePartial<Theme> = {}): UITheme {\n function delEmpty(obj: Record<string, unknown>) {\n Object.keys(obj).forEach((key) => {\n const value = obj[key]\n if (isObj(value)) {\n return delEmpty(value as Record<string, unknown>)\n }\n if (!value && value !== 0) {\n delete obj[key]\n }\n })\n }\n\n delEmpty(customTheme as Record<string, unknown>)\n\n const base = JSON.parse(JSON.stringify(toRaw(this.theme))) as Record<string, any>\n\n function deepMerge(target: any, source: any) {\n if (typeof source !== 'object' || source === null) return\n Object.keys(source).forEach((key) => {\n const sourceVal = source[key]\n if (typeof sourceVal === 'object' && sourceVal !== null && !Array.isArray(sourceVal)) {\n if (typeof target[key] !== 'object' || target[key] === null) {\n target[key] = {}\n }\n deepMerge(target[key], sourceVal)\n } else {\n target[key] = sourceVal\n }\n })\n }\n\n deepMerge(base, customTheme)\n return new UITheme(base as Theme, { reactive: this.reactiveEnabled })\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veltra/styles",
3
- "version": "1.2.34",
3
+ "version": "1.3.0",
4
4
  "files": [
5
5
  "dist",
6
6
  "src"
@@ -36,8 +36,8 @@
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@cat-kit/core": ">=1.1.8",
39
- "@veltra/compositions": "1.2.34",
40
- "@veltra/utils": "1.2.34",
39
+ "@veltra/compositions": "1.3.0",
40
+ "@veltra/utils": "1.3.0",
41
41
  "vue": ">=3.5.39"
42
42
  }
43
43
  }
@@ -8,7 +8,7 @@ html {
8
8
  font-family: fn.use-var(font-family);
9
9
  font-weight: normal;
10
10
  color: fn.use-var(text-color, main);
11
- line-height: 1.4;
11
+ line-height: 1.5;
12
12
 
13
13
  @each $size in vars.$sizes {
14
14
  &.#{$size} {
@@ -1,7 +1,13 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
 
3
3
  import { componentCssVarsDark, componentCssVarsLight } from '../component-css-vars'
4
- import { lightTheme } from '../presets'
4
+ import {
5
+ darkTheme,
6
+ glassLightTheme,
7
+ heroLightTheme,
8
+ lightTheme,
9
+ shadcnLightTheme
10
+ } from '../presets'
5
11
  import { UITheme } from '../ui-theme'
6
12
 
7
13
  describe('UITheme', () => {
@@ -44,4 +50,27 @@ describe('UITheme', () => {
44
50
  expect(decls.some((d) => d.startsWith('--u-kbd-inset-shadow:'))).toBe(true)
45
51
  expect(decls.some((d) => d.startsWith('--u-batch-edit-form-header-bg:'))).toBe(true)
46
52
  })
53
+
54
+ it('emits elevation and motion tokens', () => {
55
+ const decls = lightTheme.themeToDeclarationList(lightTheme.theme)
56
+ expect(decls.some((d) => d.startsWith('--u-shadow-sm:'))).toBe(true)
57
+ expect(decls.some((d) => d.startsWith('--u-shadow-lg:'))).toBe(true)
58
+ expect(decls.some((d) => d.startsWith('--u-transition-fast:'))).toBe(true)
59
+ expect(decls.some((d) => d.startsWith('--u-transition-ease-out:'))).toBe(true)
60
+ })
61
+
62
+ it('emits focus ring component var', () => {
63
+ expect(componentCssVarsLight['--u-focus-ring']).toContain('--u-color-primary-a-')
64
+ expect(componentCssVarsDark['--u-focus-ring']).toContain('--u-color-primary-a-')
65
+ })
66
+
67
+ it('no preset emits invalid NaN declarations', () => {
68
+ const themes = [lightTheme, darkTheme, shadcnLightTheme, heroLightTheme, glassLightTheme]
69
+ for (const t of themes) {
70
+ const decls = t.themeToDeclarationList(t.theme)
71
+ for (const d of decls) {
72
+ expect(d).not.toContain('NaN')
73
+ }
74
+ }
75
+ })
47
76
  })
@@ -13,10 +13,11 @@ const T = themeTokenVar
13
13
 
14
14
  /** 亮色(及与暗色相同的尺寸类)下组件 token */
15
15
  export const componentCssVarsLight: Record<string, string> = {
16
- '--u-table-border-color': '#e9e9e9',
17
- '--u-table-header-bg': '#f4f5f7',
16
+ '--u-focus-ring': `0 0 0 3px ${T('color', 'primary', 'a', '22')}`,
17
+ '--u-table-border-color': T('border', 'color'),
18
+ '--u-table-header-bg': T('bg-color', 'hover'),
18
19
  '--u-table-header-color': T('text-color', 'title'),
19
- '--u-table-stripe-bg': '#f8fafc',
20
+ '--u-table-stripe-bg': T('bg-color', 'middle'),
20
21
  '--u-table-stripe-color': 'inherit',
21
22
  '--u-table-hover-bg': T('bg-color', 'hover'),
22
23
  '--u-table-hover-color': 'inherit',
@@ -24,10 +25,10 @@ export const componentCssVarsLight: Record<string, string> = {
24
25
  '--u-table-current-color': 'inherit',
25
26
  '--u-table-checked-bg': T('color', 'primary', 'light-9'),
26
27
  '--u-table-checked-color': 'inherit',
27
- '--u-nav-color': '#0f172a',
28
- '--u-nav-hover-bg': 'rgba(148, 163, 184, 0.12)',
28
+ '--u-nav-color': T('text-color', 'title'),
29
+ '--u-nav-hover-bg': T('text-color', 'title', 'a', '8'),
29
30
  '--u-nav-hover-color': T('text-color', 'title'),
30
- '--u-nav-active-bg': 'rgba(59, 130, 246, 0.12)',
31
+ '--u-nav-active-bg': T('color', 'primary', 'a', '10'),
31
32
  '--u-nav-active-color': T('color', 'primary', 'dark', '1'),
32
33
  '--u-nav-height-small': '32px',
33
34
  '--u-nav-height-default': '36px',
@@ -106,27 +107,31 @@ export const componentCssVarsLight: Record<string, string> = {
106
107
  // ─── File Picker Colors ───
107
108
  '--u-file-picker-hover-bg': T('color', 'primary', 'light', '9'),
108
109
  '--u-card-header-bg': 'rgba(0, 0, 0, 0.015)',
109
- '--u-card-action-bg': 'rgba(0, 0, 0, 0.015)'
110
+ '--u-card-action-bg': 'rgba(0, 0, 0, 0.015)',
111
+ '--u-card-padding-small': '8px',
112
+ '--u-card-padding-default': '16px',
113
+ '--u-card-padding-large': '20px'
110
114
  }
111
115
 
112
116
  /** 暗色下组件 token(含与亮色相同的尺寸项,保证暗色 html 块自洽) */
113
117
  export const componentCssVarsDark: Record<string, string> = {
114
118
  ...componentCssVarsLight,
115
- '--u-table-border-color': '#404040',
116
- '--u-table-header-bg': '#2a2a2a',
119
+ '--u-focus-ring': `0 0 0 3px ${T('color', 'primary', 'a', '28')}`,
120
+ '--u-table-border-color': T('border', 'color'),
121
+ '--u-table-header-bg': T('bg-color', 'hover'),
117
122
  '--u-table-header-color': T('text-color', 'main'),
118
- '--u-table-stripe-bg': '#2a2a2a',
123
+ '--u-table-stripe-bg': T('bg-color', 'middle'),
119
124
  '--u-table-stripe-color': T('text-color', 'main'),
120
- '--u-table-hover-bg': '#333333',
125
+ '--u-table-hover-bg': T('text-color', 'title', 'a', '5'),
121
126
  '--u-table-hover-color': T('text-color', 'title'),
122
127
  '--u-table-current-bg': T('bg-color', 'hover'),
123
128
  '--u-table-current-color': 'inherit',
124
129
  '--u-table-checked-bg': T('color', 'primary', 'dark', '1'),
125
130
  '--u-table-checked-color': 'inherit',
126
131
  '--u-nav-color': T('text-color', 'main'),
127
- '--u-nav-hover-bg': 'rgba(148, 163, 184, 0.14)',
132
+ '--u-nav-hover-bg': T('text-color', 'title', 'a', '8'),
128
133
  '--u-nav-hover-color': T('text-color', 'title'),
129
- '--u-nav-active-bg': 'rgba(96, 165, 250, 0.2)',
134
+ '--u-nav-active-bg': T('color', 'primary', 'a', '16'),
130
135
  '--u-nav-active-color': T('text-color', 'white'),
131
136
  '--u-nav-bg-color': T('bg-color', 'middle'),
132
137
  '--u-radio-border': '#595959',
@@ -64,6 +64,11 @@ export function hexRgbOnly(hex: string): `#${string}` {
64
64
  return hex as `#${string}`
65
65
  }
66
66
 
67
+ /** 校验是否为 `#RRGGBB` 形式的 6 位 hex 颜色 */
68
+ export function isHexColor(value: string): value is `#${string}` {
69
+ return /^#[0-9a-fA-F]{6}$/.test(value)
70
+ }
71
+
67
72
  export function defineBySize(
68
73
  variable: Record<'small' | 'default' | 'large', number>
69
74
  ): Record<'small' | 'default' | 'large', number> {
@@ -2,37 +2,46 @@ import { lightTheme } from './light'
2
2
 
3
3
  export const darkTheme = lightTheme.new({
4
4
  color: {
5
- primary: '#4f8ff7',
6
- success: '#52c41a',
7
- warning: '#faad14',
8
- danger: '#ff4d4f',
9
- info: '#13c2c2',
10
- disabled: '#212020',
11
- default: '#595959'
5
+ primary: '#3b82f6',
6
+ success: '#22c55e',
7
+ warning: '#f59e0b',
8
+ danger: '#ef4444',
9
+ info: '#06b6d4',
10
+ disabled: '#27272a',
11
+ default: '#27272a'
12
12
  },
13
13
 
14
14
  bg: {
15
15
  color: {
16
- bottom: '#0f0f0f',
17
- middle: '#1a1a1a',
18
- top: '#262626',
19
- hover: '#303030',
16
+ bottom: '#0c0c0e',
17
+ middle: '#141417',
18
+ top: '#1c1c21',
19
+ hover: '#26262c',
20
20
  black: '#000000'
21
21
  },
22
22
  filter: { blur: 'none', saturate: 'none' }
23
23
  },
24
24
 
25
25
  'text-color': {
26
- title: '#f0f0f0',
27
- main: '#d9d9d9',
28
- second: '#a6a6a6',
29
- placeholder: '#737373',
30
- assist: '#595959',
31
- disabled: '#434343',
26
+ title: '#f4f4f5',
27
+ main: '#d4d4d8',
28
+ second: '#a1a1aa',
29
+ placeholder: '#71717a',
30
+ assist: '#52525b',
31
+ disabled: '#3f3f46',
32
32
  white: '#ffffff'
33
33
  },
34
34
 
35
- border: { color: '#404040', mutedColor: '#404040' },
35
+ border: { color: '#2e2e36', mutedColor: '#2e2e36' },
36
36
 
37
- shadow: { color: 'rgba(255, 255, 255, 0.2)', x: 0, y: 2, blur: 8, spread: 0, emboss: 'none' }
37
+ shadow: {
38
+ color: '#00000066',
39
+ x: 0,
40
+ y: 1,
41
+ blur: 3,
42
+ spread: 0,
43
+ emboss: 'none',
44
+ sm: '0 1px 2px 0 rgba(0, 0, 0, 0.4)',
45
+ lg: '0 8px 24px 0 rgba(0, 0, 0, 0.5), 0 2px 6px 0 rgba(0, 0, 0, 0.35)'
46
+ }
38
47
  })
@@ -35,7 +35,7 @@ export const glassLightTheme = lightTheme.new({
35
35
  disabled: '#94A3B8',
36
36
  white: '#FFFFFF'
37
37
  },
38
- shadow: { color: 'rgba(0, 0, 0, 0.08)', x: 0, y: 4, blur: 24, spread: 0, emboss: 'none' }
38
+ shadow: { color: '#00000014', x: 0, y: 4, blur: 24, spread: 0, emboss: 'none' }
39
39
  })
40
40
 
41
41
  export const glassDarkTheme = glassLightTheme.new({
@@ -60,5 +60,14 @@ export const glassDarkTheme = glassLightTheme.new({
60
60
  disabled: '#475569',
61
61
  white: '#ffffff'
62
62
  },
63
- shadow: { color: 'rgba(0, 0, 0, 0.35)', x: 0, y: 4, blur: 32, spread: 0, emboss: 'none' }
63
+ shadow: {
64
+ color: '#00000059',
65
+ x: 0,
66
+ y: 4,
67
+ blur: 32,
68
+ spread: 0,
69
+ emboss: 'none',
70
+ sm: '0 1px 2px 0 rgba(0, 0, 0, 0.4)',
71
+ lg: '0 8px 24px 0 rgba(0, 0, 0, 0.55), 0 2px 6px 0 rgba(0, 0, 0, 0.35)'
72
+ }
64
73
  })
@@ -22,7 +22,7 @@ export const heroLightTheme = lightTheme.new({
22
22
  },
23
23
  radius: { small: 8, default: 12, large: 14 },
24
24
  shadow: {
25
- color: 'rgba(0, 0, 0, 0.08)',
25
+ color: '#00000014',
26
26
  x: 0,
27
27
  y: 4,
28
28
  blur: 14,
@@ -54,12 +54,14 @@ export const heroDarkTheme = heroLightTheme.new({
54
54
  white: '#FFFFFF'
55
55
  },
56
56
  shadow: {
57
- color: 'rgba(0, 0, 0, 0.2)',
57
+ color: '#00000033',
58
58
  x: 0,
59
59
  y: 4,
60
60
  blur: 14,
61
61
  spread: 0,
62
- emboss: '0 2px 4px 0 #0000000a,0 1px 2px 0 #0000000f,0 0 1px 0 #0000000f'
62
+ emboss: '0 2px 4px 0 #0000000a,0 1px 2px 0 #0000000f,0 0 1px 0 #0000000f',
63
+ sm: '0 1px 2px 0 rgba(0, 0, 0, 0.4)',
64
+ lg: '0 8px 24px 0 rgba(0, 0, 0, 0.5), 0 2px 6px 0 rgba(0, 0, 0, 0.35)'
63
65
  },
64
66
  button: { 'default-bg': 'var(--u-bg-color-top)' }
65
67
  })
@@ -4,36 +4,36 @@ import { UITheme } from '../ui-theme'
4
4
  export const lightTheme: UITheme = new UITheme(
5
5
  {
6
6
  color: {
7
- primary: '#1E88E5',
8
- success: '#2ba471',
9
- warning: '#e37318',
10
- danger: '#d54941',
11
- info: '#009688',
12
- disabled: '#f5f7fa',
13
- default: '#f1f5f9'
7
+ primary: '#2563eb',
8
+ success: '#16a34a',
9
+ warning: '#d97706',
10
+ danger: '#dc2626',
11
+ info: '#0891b2',
12
+ disabled: '#f4f4f5',
13
+ default: '#f4f4f5'
14
14
  },
15
15
 
16
16
  bg: {
17
17
  color: {
18
- bottom: '#f5f5f5',
18
+ bottom: '#f4f4f5',
19
19
  middle: '#fafafa',
20
20
  top: '#ffffff',
21
- hover: '#f5f7fa',
21
+ hover: '#f4f4f5',
22
22
  black: '#000000'
23
23
  },
24
24
 
25
25
  filter: { blur: 'none', saturate: 'none' }
26
26
  },
27
27
 
28
- border: { color: '#dcdfe6', mutedColor: '#dcdfe6', width: 1, style: 'solid' },
28
+ border: { color: '#e4e4e7', mutedColor: '#e4e4e7', width: 1, style: 'solid' },
29
29
 
30
30
  'text-color': {
31
- title: '#303133',
32
- main: '#606266',
33
- placeholder: '#a8abb2',
34
- second: '#979797',
35
- assist: '#c0c4cc',
36
- disabled: '#a8abb2',
31
+ title: '#18181b',
32
+ main: '#3f3f46',
33
+ placeholder: '#a1a1aa',
34
+ second: '#71717a',
35
+ assist: '#d4d4d8',
36
+ disabled: '#a1a1aa',
37
37
  white: '#fff'
38
38
  },
39
39
 
@@ -44,13 +44,30 @@ export const lightTheme: UITheme = new UITheme(
44
44
  'font-family':
45
45
  'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif',
46
46
 
47
- 'font-size-title': defineBySize({ small: 16, default: 16, large: 18 }),
47
+ 'font-size-title': defineBySize({ small: 14, default: 16, large: 18 }),
48
48
 
49
49
  'font-size-main': defineBySize({ small: 12, default: 14, large: 16 }),
50
50
 
51
51
  'font-size-assist': defineBySize({ small: 12, default: 12, large: 14 }),
52
52
 
53
- shadow: { color: '#0000001a', x: 0, y: 0, blur: 4, spread: 1, emboss: 'none' },
53
+ shadow: {
54
+ color: '#00000014',
55
+ x: 0,
56
+ y: 1,
57
+ blur: 3,
58
+ spread: 0,
59
+ emboss: 'none',
60
+ sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
61
+ lg: '0 8px 24px 0 rgba(0, 0, 0, 0.12), 0 2px 6px 0 rgba(0, 0, 0, 0.06)'
62
+ },
63
+
64
+ transition: {
65
+ fast: '0.15s',
66
+ normal: '0.25s',
67
+ slow: '0.35s',
68
+ ease: 'cubic-bezier(0.4, 0, 0.2, 1)',
69
+ easeOut: 'cubic-bezier(0, 0, 0.2, 1)'
70
+ },
54
71
 
55
72
  gap: defineBySize({ small: 6, default: 8, large: 12 }),
56
73
 
@@ -30,7 +30,7 @@ export const shadcnLightTheme = lightTheme.new({
30
30
  white: '#ffffff'
31
31
  },
32
32
  radius: { small: 4, default: 6, large: 8 },
33
- shadow: { color: 'rgba(0, 0, 0, 0.05)', x: 0, y: 1, blur: 2, spread: 0, emboss: 'none' },
33
+ shadow: { color: '#0000000d', x: 0, y: 1, blur: 2, spread: 0, emboss: 'none' },
34
34
  button: {
35
35
  'primary-text-color': '#ffffff',
36
36
  'primary-hover-bg': '#27272a',
@@ -63,7 +63,16 @@ export const shadcnDarkTheme = shadcnLightTheme.new({
63
63
  disabled: '#71717a',
64
64
  white: '#ffffff'
65
65
  },
66
- shadow: { color: 'rgba(0, 0, 0, 0.4)', x: 0, y: 1, blur: 2, spread: 0, emboss: 'none' },
66
+ shadow: {
67
+ color: '#00000066',
68
+ x: 0,
69
+ y: 1,
70
+ blur: 2,
71
+ spread: 0,
72
+ emboss: 'none',
73
+ sm: '0 1px 2px 0 rgba(0, 0, 0, 0.4)',
74
+ lg: '0 8px 24px 0 rgba(0, 0, 0, 0.5), 0 2px 6px 0 rgba(0, 0, 0, 0.35)'
75
+ },
67
76
  button: {
68
77
  'primary-text-color': '#000000',
69
78
  'primary-hover-bg': '#e4e4e7',
package/src/theme/type.ts CHANGED
@@ -136,6 +136,23 @@ export type Theme = {
136
136
  spread: number
137
137
  /** 浮雕阴影:非浮雕主题为 'none',浮雕主题为完整 box-shadow 值 */
138
138
  emboss: string
139
+ /** 低层级阴影(卡片等贴面元素),完整 box-shadow 值 */
140
+ sm: string
141
+ /** 高层级阴影(弹窗、下拉等浮层),完整 box-shadow 值 */
142
+ lg: string
143
+ }
144
+ /** 动效:时长与缓动(值须带单位或为合法 CSS 字符串,数字会被补 px) */
145
+ transition: {
146
+ /** 快速过渡时长(微交互:hover、焦点) */
147
+ fast: string
148
+ /** 常规过渡时长 */
149
+ normal: string
150
+ /** 慢速过渡时长(浮层进出、展开收起) */
151
+ slow: string
152
+ /** 标准缓动曲线 */
153
+ ease: string
154
+ /** 入场缓动曲线 */
155
+ easeOut: string
139
156
  }
140
157
  /** 间距 */
141
158
  gap: {