@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.
- package/dist/normalize/normalize.css +1 -1
- package/dist/theme/component-css-vars.js +19 -13
- package/dist/theme/component-css-vars.js.map +1 -1
- package/dist/theme/helper.d.ts +3 -1
- package/dist/theme/helper.js +5 -1
- package/dist/theme/helper.js.map +1 -1
- package/dist/theme/index.d.ts +2 -2
- package/dist/theme/index.js +2 -2
- package/dist/theme/presets/dark.js +25 -23
- package/dist/theme/presets/dark.js.map +1 -1
- package/dist/theme/presets/glass.js +9 -7
- package/dist/theme/presets/glass.js.map +1 -1
- package/dist/theme/presets/hero.js +7 -5
- package/dist/theme/presets/hero.js.map +1 -1
- package/dist/theme/presets/light.js +35 -26
- package/dist/theme/presets/light.js.map +1 -1
- package/dist/theme/presets/shadcn.js +5 -3
- package/dist/theme/presets/shadcn.js.map +1 -1
- package/dist/theme/type.d.ts +10 -1
- package/dist/theme/ui-theme.js +21 -8
- package/dist/theme/ui-theme.js.map +1 -1
- package/package.json +3 -3
- package/src/normalize/normalize.scss +1 -1
- package/src/theme/__test__/ui-theme.test.ts +30 -1
- package/src/theme/component-css-vars.ts +19 -13
- package/src/theme/helper.ts +5 -0
- package/src/theme/presets/dark.ts +28 -19
- package/src/theme/presets/glass.ts +14 -5
- package/src/theme/presets/hero.ts +7 -5
- package/src/theme/presets/light.ts +38 -19
- package/src/theme/presets/shadcn.ts +11 -2
- package/src/theme/type.ts +17 -0
- package/src/theme/ui-theme.ts +28 -12
|
@@ -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: '
|
|
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"}
|
package/dist/theme/type.d.ts
CHANGED
|
@@ -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; /** 默认间距 */
|
package/dist/theme/ui-theme.js
CHANGED
|
@@ -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 =
|
|
67
|
+
const lines = [];
|
|
68
68
|
for (const type of Object.keys(color)) {
|
|
69
|
-
const
|
|
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
|
|
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" &&
|
|
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: ${
|
|
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.
|
|
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.
|
|
40
|
-
"@veltra/utils": "1.
|
|
39
|
+
"@veltra/compositions": "1.3.1",
|
|
40
|
+
"@veltra/utils": "1.3.1",
|
|
41
41
|
"vue": ">=3.5.39"
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -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 {
|
|
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-
|
|
17
|
-
'--u-table-
|
|
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': '
|
|
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': '
|
|
28
|
-
'--u-nav-hover-bg': '
|
|
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': '
|
|
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-
|
|
116
|
-
'--u-table-
|
|
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': '
|
|
124
|
+
'--u-table-stripe-bg': T('bg-color', 'middle'),
|
|
119
125
|
'--u-table-stripe-color': T('text-color', 'main'),
|
|
120
|
-
'--u-table-hover-bg': '
|
|
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': '
|
|
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': '
|
|
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',
|
package/src/theme/helper.ts
CHANGED
|
@@ -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: '#
|
|
6
|
-
success: '#
|
|
7
|
-
warning: '#
|
|
8
|
-
danger: '#
|
|
9
|
-
info: '#
|
|
10
|
-
disabled: '#
|
|
11
|
-
default: '#
|
|
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: '#
|
|
17
|
-
middle: '#
|
|
18
|
-
top: '#
|
|
19
|
-
hover: '#
|
|
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: '#
|
|
27
|
-
main: '#
|
|
28
|
-
second: '#
|
|
29
|
-
placeholder: '#
|
|
30
|
-
assist: '#
|
|
31
|
-
disabled: '#
|
|
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: '#
|
|
35
|
+
border: { color: '#37373f', mutedColor: '#42424c' },
|
|
36
36
|
|
|
37
|
-
shadow: {
|
|
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(
|
|
25
|
-
mutedColor: 'rgba(
|
|
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: '
|
|
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.
|
|
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: {
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '#
|
|
8
|
-
success: '#
|
|
9
|
-
warning: '#
|
|
10
|
-
danger: '#
|
|
11
|
-
info: '#
|
|
12
|
-
disabled: '#
|
|
13
|
-
default: '#
|
|
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: '#
|
|
18
|
+
bottom: '#f4f4f5',
|
|
19
19
|
middle: '#fafafa',
|
|
20
20
|
top: '#ffffff',
|
|
21
|
-
hover: '#
|
|
21
|
+
hover: '#f4f4f5',
|
|
22
22
|
black: '#000000'
|
|
23
23
|
},
|
|
24
24
|
|
|
25
25
|
filter: { blur: 'none', saturate: 'none' }
|
|
26
26
|
},
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
// color 用于结构分隔线(保持安静);mutedColor 用于表单控件/卡片描边(需要清晰的 affordance)
|
|
29
|
+
border: { color: '#e4e4e7', mutedColor: '#d4d4d8', width: 1, style: 'solid' },
|
|
29
30
|
|
|
30
31
|
'text-color': {
|
|
31
|
-
title: '#
|
|
32
|
-
main: '#
|
|
33
|
-
placeholder: '#
|
|
34
|
-
second: '#
|
|
35
|
-
assist: '#
|
|
36
|
-
disabled: '#
|
|
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
|
-
|
|
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:
|
|
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: {
|
|
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: '
|
|
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: {
|
|
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',
|