@veltra/styles 1.2.34 → 1.3.1

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 +1 @@
1
- {"version":3,"file":"shadcn.js","names":[],"sources":["../../../src/theme/presets/shadcn.ts"],"sourcesContent":["import { lightTheme } from './light'\n\nexport const shadcnLightTheme = lightTheme.new({\n color: {\n primary: '#18181b', // zinc-900\n success: '#22c55e',\n warning: '#f59e0b',\n danger: '#ef4444',\n info: '#3b82f6',\n disabled: '#f4f4f5',\n default: '#fafafa'\n },\n bg: {\n color: {\n bottom: '#f4f4f5',\n middle: '#fafafa',\n top: '#ffffff',\n hover: '#f4f4f5',\n black: '#000000'\n }\n },\n border: { color: '#e4e4e7', mutedColor: '#e4e4e7' },\n 'text-color': {\n title: '#09090b',\n main: '#09090b',\n second: '#71717a',\n placeholder: '#a1a1aa',\n assist: '#a1a1aa',\n disabled: '#a1a1aa',\n white: '#ffffff'\n },\n radius: { small: 4, default: 6, large: 8 },\n shadow: { color: 'rgba(0, 0, 0, 0.05)', x: 0, y: 1, blur: 2, spread: 0, emboss: 'none' },\n button: {\n 'primary-text-color': '#ffffff',\n 'primary-hover-bg': '#27272a',\n 'primary-hover-color': '#ffffff'\n }\n})\n\nexport const shadcnDarkTheme = shadcnLightTheme.new({\n color: {\n primary: '#fafafa', // zinc-50\n disabled: '#27272a',\n default: '#18181b'\n },\n bg: {\n color: {\n bottom: '#09090b',\n middle: '#18181b',\n top: '#27272a',\n hover: '#27272a',\n black: '#000000'\n }\n },\n border: { color: '#3f3f46', mutedColor: '#27272a' },\n 'text-color': {\n title: '#fafafa',\n main: '#fafafa',\n second: '#a1a1aa',\n placeholder: '#71717a',\n assist: '#71717a',\n disabled: '#71717a',\n white: '#ffffff'\n },\n shadow: { color: 'rgba(0, 0, 0, 0.4)', x: 0, y: 1, blur: 2, spread: 0, emboss: 'none' },\n button: {\n 'primary-text-color': '#000000',\n 'primary-hover-bg': '#e4e4e7',\n 'primary-hover-color': '#000000',\n 'primary-plain-color': '#fafafa'\n },\n checkbox: { color: '#000000' },\n tag: { 'primary-dark-color': '#000000' }\n})\n"],"mappings":";;AAEA,MAAa,mBAAmB,WAAW,IAAI;CAC7C,OAAO;EACL,SAAS;EACT,SAAS;EACT,SAAS;EACT,QAAQ;EACR,MAAM;EACN,UAAU;EACV,SAAS;CACX;CACA,IAAI,EACF,OAAO;EACL,QAAQ;EACR,QAAQ;EACR,KAAK;EACL,OAAO;EACP,OAAO;CACT,EACF;CACA,QAAQ;EAAE,OAAO;EAAW,YAAY;CAAU;CAClD,cAAc;EACZ,OAAO;EACP,MAAM;EACN,QAAQ;EACR,aAAa;EACb,QAAQ;EACR,UAAU;EACV,OAAO;CACT;CACA,QAAQ;EAAE,OAAO;EAAG,SAAS;EAAG,OAAO;CAAE;CACzC,QAAQ;EAAE,OAAO;EAAuB,GAAG;EAAG,GAAG;EAAG,MAAM;EAAG,QAAQ;EAAG,QAAQ;CAAO;CACvF,QAAQ;EACN,sBAAsB;EACtB,oBAAoB;EACpB,uBAAuB;CACzB;AACF,CAAC;AAED,MAAa,kBAAkB,iBAAiB,IAAI;CAClD,OAAO;EACL,SAAS;EACT,UAAU;EACV,SAAS;CACX;CACA,IAAI,EACF,OAAO;EACL,QAAQ;EACR,QAAQ;EACR,KAAK;EACL,OAAO;EACP,OAAO;CACT,EACF;CACA,QAAQ;EAAE,OAAO;EAAW,YAAY;CAAU;CAClD,cAAc;EACZ,OAAO;EACP,MAAM;EACN,QAAQ;EACR,aAAa;EACb,QAAQ;EACR,UAAU;EACV,OAAO;CACT;CACA,QAAQ;EAAE,OAAO;EAAsB,GAAG;EAAG,GAAG;EAAG,MAAM;EAAG,QAAQ;EAAG,QAAQ;CAAO;CACtF,QAAQ;EACN,sBAAsB;EACtB,oBAAoB;EACpB,uBAAuB;EACvB,uBAAuB;CACzB;CACA,UAAU,EAAE,OAAO,UAAU;CAC7B,KAAK,EAAE,sBAAsB,UAAU;AACzC,CAAC"}
1
+ {"version":3,"file":"shadcn.js","names":[],"sources":["../../../src/theme/presets/shadcn.ts"],"sourcesContent":["import { lightTheme } from './light'\n\nexport const shadcnLightTheme = lightTheme.new({\n color: {\n primary: '#18181b', // zinc-900\n success: '#22c55e',\n warning: '#f59e0b',\n danger: '#ef4444',\n info: '#3b82f6',\n disabled: '#f4f4f5',\n default: '#fafafa'\n },\n bg: {\n color: {\n bottom: '#f4f4f5',\n middle: '#fafafa',\n top: '#ffffff',\n hover: '#f4f4f5',\n black: '#000000'\n }\n },\n border: { color: '#e4e4e7', mutedColor: '#e4e4e7' },\n 'text-color': {\n title: '#09090b',\n main: '#09090b',\n second: '#71717a',\n placeholder: '#a1a1aa',\n assist: '#a1a1aa',\n disabled: '#a1a1aa',\n white: '#ffffff'\n },\n radius: { small: 4, default: 6, large: 8 },\n shadow: { color: '#0000000d', x: 0, y: 1, blur: 2, spread: 0, emboss: 'none' },\n button: {\n 'primary-text-color': '#ffffff',\n 'primary-hover-bg': '#27272a',\n 'primary-hover-color': '#ffffff'\n }\n})\n\nexport const shadcnDarkTheme = shadcnLightTheme.new({\n color: {\n primary: '#fafafa', // zinc-50\n disabled: '#27272a',\n default: '#18181b'\n },\n bg: {\n color: {\n bottom: '#09090b',\n middle: '#18181b',\n top: '#27272a',\n hover: '#27272a',\n black: '#000000'\n }\n },\n border: { color: '#3f3f46', mutedColor: '#27272a' },\n 'text-color': {\n title: '#fafafa',\n main: '#fafafa',\n second: '#a1a1aa',\n placeholder: '#71717a',\n assist: '#71717a',\n disabled: '#71717a',\n white: '#ffffff'\n },\n shadow: {\n color: '#00000066',\n x: 0,\n y: 1,\n blur: 2,\n spread: 0,\n emboss: 'none',\n sm: '0 1px 2px 0 rgba(0, 0, 0, 0.4)',\n lg: '0 8px 24px 0 rgba(0, 0, 0, 0.5), 0 2px 6px 0 rgba(0, 0, 0, 0.35)'\n },\n button: {\n 'primary-text-color': '#000000',\n 'primary-hover-bg': '#e4e4e7',\n 'primary-hover-color': '#000000',\n 'primary-plain-color': '#fafafa'\n },\n checkbox: { color: '#000000' },\n tag: { 'primary-dark-color': '#000000' }\n})\n"],"mappings":";;AAEA,MAAa,mBAAmB,WAAW,IAAI;CAC7C,OAAO;EACL,SAAS;EACT,SAAS;EACT,SAAS;EACT,QAAQ;EACR,MAAM;EACN,UAAU;EACV,SAAS;CACX;CACA,IAAI,EACF,OAAO;EACL,QAAQ;EACR,QAAQ;EACR,KAAK;EACL,OAAO;EACP,OAAO;CACT,EACF;CACA,QAAQ;EAAE,OAAO;EAAW,YAAY;CAAU;CAClD,cAAc;EACZ,OAAO;EACP,MAAM;EACN,QAAQ;EACR,aAAa;EACb,QAAQ;EACR,UAAU;EACV,OAAO;CACT;CACA,QAAQ;EAAE,OAAO;EAAG,SAAS;EAAG,OAAO;CAAE;CACzC,QAAQ;EAAE,OAAO;EAAa,GAAG;EAAG,GAAG;EAAG,MAAM;EAAG,QAAQ;EAAG,QAAQ;CAAO;CAC7E,QAAQ;EACN,sBAAsB;EACtB,oBAAoB;EACpB,uBAAuB;CACzB;AACF,CAAC;AAED,MAAa,kBAAkB,iBAAiB,IAAI;CAClD,OAAO;EACL,SAAS;EACT,UAAU;EACV,SAAS;CACX;CACA,IAAI,EACF,OAAO;EACL,QAAQ;EACR,QAAQ;EACR,KAAK;EACL,OAAO;EACP,OAAO;CACT,EACF;CACA,QAAQ;EAAE,OAAO;EAAW,YAAY;CAAU;CAClD,cAAc;EACZ,OAAO;EACP,MAAM;EACN,QAAQ;EACR,aAAa;EACb,QAAQ;EACR,UAAU;EACV,OAAO;CACT;CACA,QAAQ;EACN,OAAO;EACP,GAAG;EACH,GAAG;EACH,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,IAAI;EACJ,IAAI;CACN;CACA,QAAQ;EACN,sBAAsB;EACtB,oBAAoB;EACpB,uBAAuB;EACvB,uBAAuB;CACzB;CACA,UAAU,EAAE,OAAO,UAAU;CAC7B,KAAK,EAAE,sBAAsB,UAAU;AACzC,CAAC"}
@@ -74,7 +74,16 @@ type Theme = {
74
74
  y: number; /** 阴影模糊半径 */
75
75
  blur: number; /** 阴影扩散半径 */
76
76
  spread: number; /** 浮雕阴影:非浮雕主题为 'none',浮雕主题为完整 box-shadow 值 */
77
- emboss: string;
77
+ emboss: string; /** 低层级阴影(卡片等贴面元素),完整 box-shadow 值 */
78
+ sm: string; /** 高层级阴影(弹窗、下拉等浮层),完整 box-shadow 值 */
79
+ lg: string;
80
+ }; /** 动效:时长与缓动(值须带单位或为合法 CSS 字符串,数字会被补 px) */
81
+ transition: {
82
+ /** 快速过渡时长(微交互:hover、焦点) */fast: string; /** 常规过渡时长 */
83
+ normal: string; /** 慢速过渡时长(浮层进出、展开收起) */
84
+ slow: string; /** 标准缓动曲线 */
85
+ ease: string; /** 入场缓动曲线 */
86
+ easeOut: string;
78
87
  }; /** 间距 */
79
88
  gap: {
80
89
  /** 小间距 */small: number; /** 默认间距 */
@@ -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.1",
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.1",
40
+ "@veltra/utils": "1.3.1",
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', '28')}`,
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,32 @@ 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': '12px',
113
+ '--u-card-padding-large': '16px',
114
+ '--u-card-radius': T('radius', 'large')
110
115
  }
111
116
 
112
117
  /** 暗色下组件 token(含与亮色相同的尺寸项,保证暗色 html 块自洽) */
113
118
  export const componentCssVarsDark: Record<string, string> = {
114
119
  ...componentCssVarsLight,
115
- '--u-table-border-color': '#404040',
116
- '--u-table-header-bg': '#2a2a2a',
120
+ '--u-focus-ring': `0 0 0 3px ${T('color', 'primary', 'a', '35')}`,
121
+ '--u-table-border-color': T('border', 'color'),
122
+ '--u-table-header-bg': T('bg-color', 'hover'),
117
123
  '--u-table-header-color': T('text-color', 'main'),
118
- '--u-table-stripe-bg': '#2a2a2a',
124
+ '--u-table-stripe-bg': T('bg-color', 'middle'),
119
125
  '--u-table-stripe-color': T('text-color', 'main'),
120
- '--u-table-hover-bg': '#333333',
126
+ '--u-table-hover-bg': T('text-color', 'title', 'a', '5'),
121
127
  '--u-table-hover-color': T('text-color', 'title'),
122
128
  '--u-table-current-bg': T('bg-color', 'hover'),
123
129
  '--u-table-current-color': 'inherit',
124
130
  '--u-table-checked-bg': T('color', 'primary', 'dark', '1'),
125
131
  '--u-table-checked-color': 'inherit',
126
132
  '--u-nav-color': T('text-color', 'main'),
127
- '--u-nav-hover-bg': 'rgba(148, 163, 184, 0.14)',
133
+ '--u-nav-hover-bg': T('text-color', 'title', 'a', '8'),
128
134
  '--u-nav-hover-color': T('text-color', 'title'),
129
- '--u-nav-active-bg': 'rgba(96, 165, 250, 0.2)',
135
+ '--u-nav-active-bg': T('color', 'primary', 'a', '16'),
130
136
  '--u-nav-active-color': T('text-color', 'white'),
131
137
  '--u-nav-bg-color': T('bg-color', 'middle'),
132
138
  '--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: '#37373f', mutedColor: '#42424c' },
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
  })
@@ -21,8 +21,8 @@ export const glassLightTheme = lightTheme.new({
21
21
  filter: { blur: 'blur(24px)', saturate: 'saturate(180%)' }
22
22
  },
23
23
  border: {
24
- color: 'rgba(255, 255, 255, 0.5)',
25
- mutedColor: 'rgba(255, 255, 255, 0.5)',
24
+ color: 'rgba(15, 23, 42, 0.14)',
25
+ mutedColor: 'rgba(15, 23, 42, 0.08)',
26
26
  width: 1,
27
27
  style: 'solid'
28
28
  },
@@ -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({
@@ -50,7 +50,7 @@ export const glassDarkTheme = glassLightTheme.new({
50
50
  },
51
51
  filter: { blur: 'blur(20px)', saturate: 'saturate(200%)' }
52
52
  },
53
- border: { color: 'rgba(255, 255, 255, 0.12)', mutedColor: 'rgba(255, 255, 255, 0.12)' },
53
+ border: { color: 'rgba(255, 255, 255, 0.18)', mutedColor: 'rgba(255, 255, 255, 0.1)' },
54
54
  'text-color': {
55
55
  title: '#F8FAFC',
56
56
  main: '#F1F5F9',
@@ -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
  })
@@ -10,7 +10,7 @@ export const heroLightTheme = lightTheme.new({
10
10
  disabled: '#f4f4f5',
11
11
  default: '#f4f4f5'
12
12
  },
13
- border: { width: 2, color: '#dcdfe6', mutedColor: 'transparent' },
13
+ border: { width: 2, color: '#dcdfe6', mutedColor: '#e4e4e7' },
14
14
  'text-color': {
15
15
  title: '#11181C',
16
16
  main: '#11181C',
@@ -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,
@@ -43,7 +43,7 @@ export const heroDarkTheme = heroLightTheme.new({
43
43
  black: '#000000'
44
44
  }
45
45
  },
46
- border: { color: '#3f3f46', mutedColor: 'transparent' },
46
+ border: { color: '#3f3f46', mutedColor: '#3f3f46' },
47
47
  'text-color': {
48
48
  title: '#ECEDEE',
49
49
  main: '#ECEDEE',
@@ -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,53 +4,72 @@ 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
+ // color 用于结构分隔线(保持安静);mutedColor 用于表单控件/卡片描边(需要清晰的 affordance)
29
+ border: { color: '#e4e4e7', mutedColor: '#d4d4d8', width: 1, style: 'solid' },
29
30
 
30
31
  'text-color': {
31
- title: '#303133',
32
- main: '#606266',
33
- placeholder: '#a8abb2',
34
- second: '#979797',
35
- assist: '#c0c4cc',
36
- disabled: '#a8abb2',
32
+ title: '#18181b',
33
+ main: '#3f3f46',
34
+ placeholder: '#a1a1aa',
35
+ second: '#71717a',
36
+ assist: '#d4d4d8',
37
+ disabled: '#a1a1aa',
37
38
  white: '#fff'
38
39
  },
39
40
 
40
- radius: defineBySize({ small: 4, default: 6, large: 8 }),
41
+ // 对齐 Tailwind rounded-md / rounded-lg / rounded-xl
42
+ radius: defineBySize({ small: 6, default: 8, large: 12 }),
41
43
 
42
44
  'form-component-height': defineBySize({ small: 24, default: 32, large: 40 }),
43
45
 
44
46
  'font-family':
45
47
  'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif',
46
48
 
47
- 'font-size-title': defineBySize({ small: 16, default: 16, large: 18 }),
49
+ 'font-size-title': defineBySize({ small: 14, default: 16, large: 18 }),
48
50
 
49
51
  'font-size-main': defineBySize({ small: 12, default: 14, large: 16 }),
50
52
 
51
53
  'font-size-assist': defineBySize({ small: 12, default: 12, large: 14 }),
52
54
 
53
- shadow: { color: '#0000001a', x: 0, y: 0, blur: 4, spread: 1, emboss: 'none' },
55
+ shadow: {
56
+ color: '#00000014',
57
+ x: 0,
58
+ y: 1,
59
+ blur: 3,
60
+ spread: 0,
61
+ emboss: 'none',
62
+ sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
63
+ lg: '0 8px 24px 0 rgba(0, 0, 0, 0.12), 0 2px 6px 0 rgba(0, 0, 0, 0.06)'
64
+ },
65
+
66
+ transition: {
67
+ fast: '0.15s',
68
+ normal: '0.25s',
69
+ slow: '0.35s',
70
+ ease: 'cubic-bezier(0.4, 0, 0.2, 1)',
71
+ easeOut: 'cubic-bezier(0, 0, 0.2, 1)'
72
+ },
54
73
 
55
74
  gap: defineBySize({ small: 6, default: 8, large: 12 }),
56
75
 
@@ -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',