@veltra/styles 1.6.6 → 1.7.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.
Files changed (58) hide show
  1. package/README.md +22 -0
  2. package/dist/_mixins.scss +1 -7
  3. package/dist/theme/component-css-vars.d.ts +24 -0
  4. package/dist/theme/component-css-vars.js +74 -14
  5. package/dist/theme/component-css-vars.js.map +1 -1
  6. package/dist/theme/index.d.ts +11 -7
  7. package/dist/theme/index.js +10 -6
  8. package/dist/theme/load-theme.d.ts +3 -4
  9. package/dist/theme/load-theme.js +15 -22
  10. package/dist/theme/load-theme.js.map +1 -1
  11. package/dist/theme/presets/ancient.d.ts +3 -2
  12. package/dist/theme/presets/ancient.js +6 -3
  13. package/dist/theme/presets/ancient.js.map +1 -1
  14. package/dist/theme/presets/dark.js +1 -1
  15. package/dist/theme/presets/dark.js.map +1 -1
  16. package/dist/theme/presets/glass.d.ts +7 -3
  17. package/dist/theme/presets/glass.js +18 -47
  18. package/dist/theme/presets/glass.js.map +1 -1
  19. package/dist/theme/presets/hero.d.ts +3 -3
  20. package/dist/theme/presets/hero.js +5 -41
  21. package/dist/theme/presets/hero.js.map +1 -1
  22. package/dist/theme/presets/midnight.d.ts +8 -0
  23. package/dist/theme/presets/midnight.js +55 -0
  24. package/dist/theme/presets/midnight.js.map +1 -0
  25. package/dist/theme/presets/neon.d.ts +8 -0
  26. package/dist/theme/presets/neon.js +55 -0
  27. package/dist/theme/presets/neon.js.map +1 -0
  28. package/dist/theme/presets/ocean.d.ts +8 -0
  29. package/dist/theme/presets/ocean.js +57 -0
  30. package/dist/theme/presets/ocean.js.map +1 -0
  31. package/dist/theme/presets/sakura.d.ts +8 -0
  32. package/dist/theme/presets/sakura.js +59 -0
  33. package/dist/theme/presets/sakura.js.map +1 -0
  34. package/dist/theme/type.d.ts +10 -0
  35. package/dist/theme/ui-theme.d.ts +21 -8
  36. package/dist/theme/ui-theme.js +30 -27
  37. package/dist/theme/ui-theme.js.map +1 -1
  38. package/package.json +3 -3
  39. package/src/_mixins.scss +1 -7
  40. package/src/theme/__test__/ui-theme.test.ts +84 -9
  41. package/src/theme/component-css-vars.ts +90 -13
  42. package/src/theme/index.ts +2 -0
  43. package/src/theme/load-theme.ts +12 -27
  44. package/src/theme/presets/ancient.ts +9 -2
  45. package/src/theme/presets/dark.ts +43 -40
  46. package/src/theme/presets/glass.ts +56 -67
  47. package/src/theme/presets/hero.ts +5 -36
  48. package/src/theme/presets/index.ts +7 -4
  49. package/src/theme/presets/midnight.ts +45 -0
  50. package/src/theme/presets/neon.ts +45 -0
  51. package/src/theme/presets/ocean.ts +40 -0
  52. package/src/theme/presets/sakura.ts +42 -0
  53. package/src/theme/type.ts +10 -0
  54. package/src/theme/ui-theme.ts +54 -43
  55. package/dist/theme/presets/shadcn.d.ts +0 -8
  56. package/dist/theme/presets/shadcn.js +0 -100
  57. package/dist/theme/presets/shadcn.js.map +0 -1
  58. package/src/theme/presets/shadcn.ts +0 -84
@@ -1,73 +1,62 @@
1
1
  import { lightTheme } from './light'
2
2
 
3
- export const glassLightTheme = lightTheme.new({
4
- color: {
5
- primary: '#3B82F6',
6
- success: '#10B981',
7
- warning: '#F59E0B',
8
- danger: '#EF4444',
9
- info: '#06B6D4',
10
- disabled: '#E5E7EB',
11
- default: '#F3F4F6'
12
- },
13
- bg: {
3
+ /**
4
+ * 玻璃拟态,天生适合深色(半透明背景 + 背景模糊在深色下层次最好)。
5
+ * 背景为 rgba() 时派生 token(kbd / batch-edit 混合色)无法自动推导,
6
+ * 需通过组件级扩展键显式声明。
7
+ */
8
+ export const glassTheme = lightTheme.new(
9
+ {
14
10
  color: {
15
- bottom: 'rgba(255, 255, 255, 0.45)',
16
- middle: 'rgba(255, 255, 255, 0.55)',
17
- top: 'rgba(255, 255, 255, 0.6)',
18
- hover: 'rgba(255, 255, 255, 0.75)',
19
- black: '#000000'
11
+ primary: '#3B82F6',
12
+ success: '#10B981',
13
+ warning: '#F59E0B',
14
+ danger: '#EF4444',
15
+ info: '#06B6D4',
16
+ disabled: '#374151',
17
+ default: '#1E293B'
20
18
  },
21
- filter: { blur: 'blur(24px)', saturate: 'saturate(180%)' }
22
- },
23
- border: {
24
- color: 'rgba(15, 23, 42, 0.14)',
25
- mutedColor: 'rgba(15, 23, 42, 0.08)',
26
- width: 1,
27
- style: 'solid'
28
- },
29
- 'text-color': {
30
- title: '#1E293B',
31
- main: '#334155',
32
- placeholder: '#94A3B8',
33
- second: '#64748B',
34
- assist: '#CBD5E1',
35
- disabled: '#94A3B8',
36
- white: '#FFFFFF'
37
- },
38
- shadow: { color: '#00000014', x: 0, y: 4, blur: 24, spread: 0, emboss: 'none' }
39
- })
40
-
41
- export const glassDarkTheme = glassLightTheme.new({
42
- color: { disabled: '#374151', default: '#1E293B' },
43
- bg: {
44
- color: {
45
- bottom: 'rgba(15, 23, 42, 0.6)',
46
- middle: 'rgba(15, 23, 42, 0.7)',
47
- top: 'rgba(15, 23, 42, 0.75)',
48
- hover: 'rgba(30, 41, 59, 0.85)',
49
- black: '#000000'
19
+ bg: {
20
+ color: {
21
+ bottom: 'rgba(15, 23, 42, 0.6)',
22
+ middle: 'rgba(15, 23, 42, 0.7)',
23
+ top: 'rgba(15, 23, 42, 0.75)',
24
+ hover: 'rgba(30, 41, 59, 0.85)',
25
+ black: '#000000'
26
+ },
27
+ filter: { blur: 'blur(20px)', saturate: 'saturate(200%)' }
50
28
  },
51
- filter: { blur: 'blur(20px)', saturate: 'saturate(200%)' }
52
- },
53
- border: { color: 'rgba(255, 255, 255, 0.18)', mutedColor: 'rgba(255, 255, 255, 0.1)' },
54
- 'text-color': {
55
- title: '#F8FAFC',
56
- main: '#F1F5F9',
57
- second: '#94A3B8',
58
- placeholder: '#64748B',
59
- assist: '#64748B',
60
- disabled: '#475569',
61
- white: '#ffffff'
29
+ border: {
30
+ color: 'rgba(255, 255, 255, 0.18)',
31
+ mutedColor: 'rgba(255, 255, 255, 0.1)',
32
+ width: 1,
33
+ style: 'solid'
34
+ },
35
+ 'text-color': {
36
+ title: '#F8FAFC',
37
+ main: '#F1F5F9',
38
+ second: '#94A3B8',
39
+ placeholder: '#64748B',
40
+ assist: '#64748B',
41
+ disabled: '#475569',
42
+ white: '#ffffff'
43
+ },
44
+ shadow: {
45
+ color: '#00000059',
46
+ x: 0,
47
+ y: 4,
48
+ blur: 32,
49
+ spread: 0,
50
+ emboss: 'none',
51
+ sm: '0 1px 2px 0 rgba(0, 0, 0, 0.4)',
52
+ lg: '0 8px 24px 0 rgba(0, 0, 0, 0.55), 0 2px 6px 0 rgba(0, 0, 0, 0.35)'
53
+ },
54
+ kbd: {
55
+ 'inset-shadow': 'rgba(0, 0, 0, 0.5)',
56
+ 'border-shadow': 'rgba(255, 255, 255, 0.09)',
57
+ 'drop-shadow': 'rgba(0, 0, 0, 0.6)'
58
+ },
59
+ 'batch-edit': { 'form-header-bg': 'rgba(15, 23, 42, 0.8)' }
62
60
  },
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
- }
73
- })
61
+ { series: 'dark' }
62
+ )
@@ -1,6 +1,7 @@
1
1
  import { lightTheme } from './light'
2
2
 
3
- export const heroLightTheme = lightTheme.new({
3
+ /** HeroUI 风格,天生适合浅色 */
4
+ export const heroTheme = lightTheme.new({
4
5
  color: {
5
6
  primary: '#7828c8', // purple
6
7
  success: '#17c964',
@@ -29,39 +30,7 @@ export const heroLightTheme = lightTheme.new({
29
30
  spread: 0,
30
31
  emboss: '0 2px 4px 0 #0000000a,0 1px 2px 0 #0000000f,0 0 3px 0px #0000000f'
31
32
  },
32
- button: { 'default-bg': '#ebebec' }
33
- })
34
-
35
- export const heroDarkTheme = heroLightTheme.new({
36
- color: { primary: '#9353d3', disabled: '#27272a', default: '#27272a' },
37
- bg: {
38
- color: {
39
- bottom: '#000000',
40
- middle: '#18181b',
41
- top: '#27272a',
42
- hover: '#3f3f46',
43
- black: '#000000'
44
- }
45
- },
46
- border: { color: '#3f3f46', mutedColor: '#3f3f46' },
47
- 'text-color': {
48
- title: '#ECEDEE',
49
- main: '#ECEDEE',
50
- second: '#A1A1AA',
51
- placeholder: '#71717A',
52
- assist: '#71717A',
53
- disabled: '#71717A',
54
- white: '#FFFFFF'
55
- },
56
- shadow: {
57
- color: '#00000033',
58
- x: 0,
59
- y: 4,
60
- blur: 14,
61
- spread: 0,
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)'
65
- },
66
- button: { 'default-bg': 'var(--u-bg-color-top)' }
33
+ button: { 'default-bg': '#ebebec' },
34
+ // HeroUI 风浅灰侧栏(演示浅色变体)
35
+ nav: { variant: 'light' }
67
36
  })
@@ -1,6 +1,9 @@
1
1
  export { lightTheme } from './light'
2
2
  export { darkTheme } from './dark'
3
- export { heroDarkTheme, heroLightTheme } from './hero'
4
- export { shadcnLightTheme, shadcnDarkTheme } from './shadcn'
5
- export { glassDarkTheme, glassLightTheme } from './glass'
6
- export { ancientLightTheme } from './ancient'
3
+ export { heroTheme } from './hero'
4
+ export { glassTheme } from './glass'
5
+ export { ancientTheme } from './ancient'
6
+ export { sakuraTheme } from './sakura'
7
+ export { oceanTheme } from './ocean'
8
+ export { midnightTheme } from './midnight'
9
+ export { neonTheme } from './neon'
@@ -0,0 +1,45 @@
1
+ import { darkTheme } from './dark'
2
+
3
+ /** 午夜(靛蓝 + 深空底,宽松间距、舒缓缓动),天生适合深色 */
4
+ export const midnightTheme = darkTheme.new({
5
+ color: {
6
+ primary: '#6f78ee', // 星靛(白字对比 3.75:1)
7
+ success: '#34d399',
8
+ warning: '#fbbf24',
9
+ danger: '#f87171',
10
+ info: '#38bdf8',
11
+ disabled: '#232d4a',
12
+ default: '#232d4a'
13
+ },
14
+ bg: {
15
+ color: {
16
+ bottom: '#0a0f1e', // 深空
17
+ middle: '#101731',
18
+ top: '#182242',
19
+ hover: '#212d54'
20
+ }
21
+ },
22
+ 'text-color': {
23
+ title: '#e8ecff', // 月光
24
+ main: '#c3cbf0',
25
+ second: '#8d97c6',
26
+ placeholder: '#7d8ab8',
27
+ assist: '#5b6895',
28
+ disabled: '#576493'
29
+ },
30
+ // mutedColor 与卡片底对比 2.6:1,保证输入控件清晰可见
31
+ border: { color: '#36457a', mutedColor: '#52619a' },
32
+ // 更宽松的间距,配合深空的呼吸感
33
+ gap: { small: 8, default: 10, large: 14 },
34
+ shadow: {
35
+ color: '#00000080',
36
+ x: 0,
37
+ y: 2,
38
+ blur: 8,
39
+ spread: 0,
40
+ emboss: 'none',
41
+ sm: '0 1px 2px 0 rgba(0, 0, 0, 0.5)',
42
+ lg: '0 8px 24px 0 rgba(0, 0, 0, 0.6), 0 2px 6px 0 rgba(0, 0, 0, 0.4)'
43
+ },
44
+ transition: { slow: '0.4s', ease: 'cubic-bezier(0.33, 1, 0.68, 1)' }
45
+ })
@@ -0,0 +1,45 @@
1
+ import { darkTheme } from './dark'
2
+
3
+ /** 霓虹(品红 + 夜紫底 + 辉光阴影,小圆角、急速缓动),天生适合深色 */
4
+ export const neonTheme = darkTheme.new({
5
+ color: {
6
+ primary: '#c026d3', // 霓红
7
+ success: '#4ade80',
8
+ warning: '#facc15',
9
+ danger: '#fb7185',
10
+ info: '#22d3ee',
11
+ disabled: '#241532',
12
+ default: '#241532'
13
+ },
14
+ bg: {
15
+ color: {
16
+ bottom: '#0c0612', // 夜紫
17
+ middle: '#130a1d',
18
+ top: '#1c102a',
19
+ hover: '#28163d'
20
+ }
21
+ },
22
+ 'text-color': {
23
+ title: '#f6efff',
24
+ main: '#d9cdee',
25
+ second: '#a08bc4',
26
+ placeholder: '#8f74b8',
27
+ assist: '#6b4f94',
28
+ disabled: '#63498a'
29
+ },
30
+ // mutedColor 与卡片底对比 2.65:1,输入控件带清晰的紫色描边
31
+ border: { color: '#503480', mutedColor: '#684a9e' },
32
+ // 赛博感:小圆角 + 品红辉光
33
+ radius: { small: 2, default: 4, large: 8 },
34
+ shadow: {
35
+ color: 'rgba(192, 38, 211, 0.3)',
36
+ x: 0,
37
+ y: 0,
38
+ blur: 12,
39
+ spread: 0,
40
+ emboss: 'none',
41
+ sm: '0 1px 2px 0 rgba(0, 0, 0, 0.5)',
42
+ lg: '0 0 24px 0 rgba(192, 38, 211, 0.25), 0 8px 24px 0 rgba(0, 0, 0, 0.5)'
43
+ },
44
+ transition: { fast: '0.1s', ease: 'cubic-bezier(0.85, 0, 0.15, 1)' }
45
+ })
@@ -0,0 +1,40 @@
1
+ import { mixColor } from '../helper'
2
+ import { lightTheme } from './light'
3
+
4
+ /** 海盐(松石青 + 冷白底,利落的快缓动),天生适合浅色 */
5
+ export const oceanTheme = lightTheme.new({
6
+ color: {
7
+ primary: '#0d9488', // 松石青
8
+ success: '#16a34a',
9
+ warning: '#d97706',
10
+ danger: '#dc2626',
11
+ info: '#0284c7',
12
+ disabled: '#eff6f5',
13
+ default: '#eff6f5'
14
+ },
15
+ bg: { color: { bottom: '#eef6f5', middle: '#f5fafa', top: '#ffffff', hover: '#e0efee' } },
16
+ 'text-color': {
17
+ title: '#16302e', // 深礁
18
+ main: '#2f4a48',
19
+ second: '#5b7875',
20
+ assist: '#c3d6d4',
21
+ placeholder: '#7fa19e',
22
+ disabled: '#a3bab7'
23
+ },
24
+ border: { width: 1, color: '#c2dedb', mutedColor: '#8fbcb7' },
25
+ radius: { small: 6, default: 10, large: 14 },
26
+ shadow: {
27
+ color: 'rgba(21, 94, 89, 0.12)',
28
+ x: 0,
29
+ y: 2,
30
+ blur: 10,
31
+ spread: -1,
32
+ sm: '0 1px 2px 0 rgba(21, 94, 89, 0.08)',
33
+ lg: '0 8px 24px 0 rgba(21, 94, 89, 0.12), 0 2px 6px 0 rgba(21, 94, 89, 0.06)'
34
+ },
35
+ transition: { fast: '0.12s', ease: 'cubic-bezier(0.22, 1, 0.36, 1)' },
36
+ nav: {
37
+ // 深海礁侧栏:深礁 + 松石青调和
38
+ 'bg-color': mixColor('#16302e', '#0d9488', 0.25)
39
+ }
40
+ })
@@ -0,0 +1,42 @@
1
+ import { mixColor } from '../helper'
2
+ import { lightTheme } from './light'
3
+
4
+ /** 樱花(柔粉 + 花瓣底,大圆角、弹性缓动),天生适合浅色 */
5
+ export const sakuraTheme = lightTheme.new({
6
+ color: {
7
+ primary: '#e64980', // 樱粉
8
+ success: '#2f9e6e',
9
+ warning: '#e8890c',
10
+ danger: '#e03131',
11
+ info: '#7c6cf0',
12
+ disabled: '#faf0f3',
13
+ default: '#faf0f3'
14
+ },
15
+ bg: { color: { bottom: '#fdf1f5', middle: '#fef6f9', top: '#ffffff', hover: '#fbe4ec' } },
16
+ 'text-color': {
17
+ title: '#43272f', // 樱墨
18
+ main: '#5f3d46',
19
+ second: '#92616d',
20
+ assist: '#dcc0c7',
21
+ placeholder: '#b58694',
22
+ disabled: '#c4a0aa'
23
+ },
24
+ border: { width: 1, color: '#f0c3d4', mutedColor: '#e09db6' },
25
+ radius: { small: 8, default: 12, large: 16 },
26
+ // 更宽松的表单控件,配合大圆角的柔和感
27
+ 'form-component-height': { small: 26, default: 34, large: 42 },
28
+ shadow: {
29
+ color: 'rgba(173, 58, 105, 0.12)',
30
+ x: 0,
31
+ y: 4,
32
+ blur: 14,
33
+ spread: -2,
34
+ sm: '0 1px 2px 0 rgba(173, 58, 105, 0.08)',
35
+ lg: '0 8px 24px 0 rgba(173, 58, 105, 0.14), 0 2px 6px 0 rgba(173, 58, 105, 0.08)'
36
+ },
37
+ transition: { easeOut: 'cubic-bezier(0.34, 1.56, 0.64, 1)' },
38
+ nav: {
39
+ // 深酒红侧栏:樱墨 + 樱粉调和
40
+ 'bg-color': mixColor('#43272f', '#e64980', 0.22)
41
+ }
42
+ })
package/src/theme/type.ts CHANGED
@@ -180,6 +180,16 @@ export type Theme = {
180
180
  'default-bg'?: string
181
181
  [key: string]: any
182
182
  }
183
+ /**
184
+ * 侧栏导航(nav / dual-nav / group-nav)组件级变量。
185
+ * variant 选择深/浅侧栏外观,其余键覆盖同名 `--u-nav-*` token
186
+ * (如 `'bg-color'` → `--u-nav-bg-color`),覆盖优先级高于 variant 内置值。
187
+ */
188
+ nav?: {
189
+ /** 侧栏外观:dark 深底浅字 / light 浅底深字,默认 dark */
190
+ variant?: 'dark' | 'light'
191
+ [key: string]: any
192
+ }
183
193
  /** 折叠面板组件级变量 */
184
194
  collapse?: {
185
195
  /** 折叠项标题文字颜色 */
@@ -2,7 +2,12 @@ import { isObj, str } from '@cat-kit/core'
2
2
  import { withUnit } from '@veltra/utils'
3
3
  import { reactive, toRaw, watch } from 'vue'
4
4
 
5
- import { componentCssVarsDarkDecls, componentCssVarsLightDecls } from './component-css-vars'
5
+ import {
6
+ componentCssVarsDarkDecls,
7
+ componentCssVarsLightDecls,
8
+ resolveNavSidebarDecls,
9
+ type NavSidebarVariant
10
+ } from './component-css-vars'
6
11
  import { hexRgbOnly, hexWithAlpha, isHexColor, mixColor } from './helper'
7
12
  import type { Theme } from './type'
8
13
 
@@ -10,6 +15,16 @@ type RecursivePartial<T> = {
10
15
  [P in keyof T]?: T[P] extends object ? RecursivePartial<T[P]> : T[P]
11
16
  }
12
17
 
18
+ /** 主题系列:浅色或深色。决定组件级 token 与 html[data-theme] */
19
+ export type ThemeSeries = 'light' | 'dark'
20
+
21
+ export interface UIThemeOptions {
22
+ /** 主题变更时是否自动重渲染,默认 true */
23
+ reactive?: boolean
24
+ /** 主题所属系列,默认 'light' */
25
+ series?: ThemeSeries
26
+ }
27
+
13
28
  export class UITheme {
14
29
  static themeID = 'ultra-ui-theme'
15
30
 
@@ -17,32 +32,28 @@ export class UITheme {
17
32
 
18
33
  readonly theme: Theme
19
34
 
35
+ /** 主题所属系列:浅色 / 深色 */
36
+ series: ThemeSeries
37
+
20
38
  private readonly reactiveEnabled: boolean
21
39
 
22
- constructor(theme: Theme, options?: { reactive?: boolean }) {
40
+ constructor(theme: Theme, options?: UIThemeOptions) {
23
41
  this.reactiveEnabled = options?.reactive !== false
42
+ this.series = options?.series ?? 'light'
24
43
  this.theme = reactive(theme) as Theme
25
44
  if (this.reactiveEnabled) {
26
45
  watch(this.theme, () => this.render(), { deep: true })
27
46
  }
28
47
  }
29
48
 
30
- static setTheme(mode: 'light' | 'dark' | 'auto'): void {
31
- if (typeof document === 'undefined') return
32
- const el = document.documentElement
33
- if (mode === 'auto') {
34
- delete el.dataset.theme
35
- } else {
36
- el.dataset.theme = mode
37
- }
38
- }
39
-
40
49
  private renderBase(
41
50
  theme: Record<string, unknown>,
42
51
  themeRules: string[] = [],
43
52
  parentKey = '--u'
44
53
  ): string[] {
45
54
  Object.keys(theme).forEach((key) => {
55
+ // nav 由 navSidebarDecls 按 variant + 覆盖项单独注入(须晚于组件级 token 才生效)
56
+ if (parentKey === '--u' && key === 'nav') return
46
57
  const value = theme[key]
47
58
  const varKey = `${parentKey.startsWith('-') ? parentKey : str(parentKey).kebabCase()}-${str(key).kebabCase()}`
48
59
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
@@ -232,6 +243,20 @@ export class UITheme {
232
243
  return decls.join(';')
233
244
  }
234
245
 
246
+ /**
247
+ * nav 侧栏外观声明:按主题 `nav.variant`(默认 dark)取内置深/浅侧栏 token,
248
+ * 主题 `nav` 的其余键作为 `--u-nav-*` 覆盖追加在最后(同规则后者生效)。
249
+ */
250
+ navSidebarDecls(theme: Theme): string[] {
251
+ const nav = theme.nav ?? {}
252
+ const variant: NavSidebarVariant = nav.variant === 'light' ? 'light' : 'dark'
253
+ const decls = resolveNavSidebarDecls(this.series, variant)
254
+ const overrides = Object.keys(nav)
255
+ .filter((key) => key !== 'variant' && typeof nav[key] === 'string' && nav[key])
256
+ .map((key) => `--u-nav-${str(key).kebabCase()}: ${nav[key]}`)
257
+ return [...decls, ...overrides]
258
+ }
259
+
235
260
  private static removeExistingStyleTag(doc: Document): void {
236
261
  const el = doc.getElementById(UITheme.themeID)
237
262
  if (el?.parentNode) {
@@ -267,42 +292,25 @@ export class UITheme {
267
292
  }
268
293
  }
269
294
 
270
- /** 内置 light/dark:支持 data-theme 与 prefers-color-scheme */
271
- static injectBuiltInThemes(light: UITheme, dark: UITheme): void {
272
- const lightDecls = [
273
- ...light.themeToDeclarationList(toRaw(light.theme)),
274
- ...componentCssVarsLightDecls
275
- ]
276
- const darkDecls = [
277
- ...dark.themeToDeclarationList(toRaw(dark.theme)),
278
- ...componentCssVarsDarkDecls
279
- ]
280
- const lightBlock = UITheme.declarationBlock(lightDecls)
281
- const darkBlock = UITheme.declarationBlock(darkDecls)
282
-
283
- const css = [
284
- `html { ${lightBlock} }`,
285
- `@media (prefers-color-scheme: dark) {`,
286
- ` html:not([data-theme="light"]) { ${darkBlock} }`,
287
- `}`,
288
- `html[data-theme="light"] { ${lightBlock} }`,
289
- `html[data-theme="dark"] { ${darkBlock} }`
290
- ].join('\n')
291
-
292
- UITheme.applyGlobalCSS(css)
293
- }
294
-
295
+ /** 注入主题:全局 token + 与主题系列匹配的组件级 token + nav 侧栏外观,并写入 html[data-theme] */
295
296
  render(): void {
296
- const isDark =
297
- typeof document !== 'undefined' && document.documentElement.dataset.theme === 'dark'
298
- const componentDecls = isDark ? componentCssVarsDarkDecls : componentCssVarsLightDecls
299
- const decls = [...this.themeToDeclarationList(toRaw(this.theme)), ...componentDecls]
297
+ const componentDecls =
298
+ this.series === 'dark' ? componentCssVarsDarkDecls : componentCssVarsLightDecls
299
+ const raw = toRaw(this.theme)
300
+ const decls = [
301
+ ...this.themeToDeclarationList(raw),
302
+ ...componentDecls,
303
+ ...this.navSidebarDecls(raw)
304
+ ]
300
305
  const block = UITheme.declarationBlock(decls)
301
306
  const css = `html { ${block} }`
302
307
  UITheme.applyGlobalCSS(css)
308
+ if (typeof document !== 'undefined') {
309
+ document.documentElement.dataset.theme = this.series
310
+ }
303
311
  }
304
312
 
305
- new(customTheme: RecursivePartial<Theme> = {}): UITheme {
313
+ new(customTheme: RecursivePartial<Theme> = {}, options?: { series?: ThemeSeries }): UITheme {
306
314
  function delEmpty(obj: Record<string, unknown>) {
307
315
  Object.keys(obj).forEach((key) => {
308
316
  const value = obj[key]
@@ -335,6 +343,9 @@ export class UITheme {
335
343
  }
336
344
 
337
345
  deepMerge(base, customTheme)
338
- return new UITheme(base as Theme, { reactive: this.reactiveEnabled })
346
+ return new UITheme(base as Theme, {
347
+ reactive: this.reactiveEnabled,
348
+ series: options?.series ?? this.series
349
+ })
339
350
  }
340
351
  }
@@ -1,8 +0,0 @@
1
- import { UITheme } from "../ui-theme.js";
2
- import "../index.js";
3
- //#region src/theme/presets/shadcn.d.ts
4
- declare const shadcnLightTheme: UITheme;
5
- declare const shadcnDarkTheme: UITheme;
6
- //#endregion
7
- export { shadcnDarkTheme, shadcnLightTheme };
8
- //# sourceMappingURL=shadcn.d.ts.map
@@ -1,100 +0,0 @@
1
- import { lightTheme } from "./light.js";
2
- //#region src/theme/presets/shadcn.ts
3
- const shadcnLightTheme = lightTheme.new({
4
- color: {
5
- primary: "#18181b",
6
- success: "#22c55e",
7
- warning: "#f59e0b",
8
- danger: "#ef4444",
9
- info: "#3b82f6",
10
- disabled: "#f4f4f5",
11
- default: "#fafafa"
12
- },
13
- bg: { color: {
14
- bottom: "#f4f4f5",
15
- middle: "#fafafa",
16
- top: "#ffffff",
17
- hover: "#f4f4f5",
18
- black: "#000000"
19
- } },
20
- border: {
21
- color: "#e4e4e7",
22
- mutedColor: "#e4e4e7"
23
- },
24
- "text-color": {
25
- title: "#09090b",
26
- main: "#09090b",
27
- second: "#71717a",
28
- placeholder: "#a1a1aa",
29
- assist: "#a1a1aa",
30
- disabled: "#a1a1aa",
31
- white: "#ffffff"
32
- },
33
- radius: {
34
- small: 4,
35
- default: 6,
36
- large: 8
37
- },
38
- shadow: {
39
- color: "#0000000d",
40
- x: 0,
41
- y: 1,
42
- blur: 2,
43
- spread: 0,
44
- emboss: "none"
45
- },
46
- button: {
47
- "primary-text-color": "#ffffff",
48
- "primary-hover-bg": "#27272a",
49
- "primary-hover-color": "#ffffff"
50
- }
51
- });
52
- const shadcnDarkTheme = shadcnLightTheme.new({
53
- color: {
54
- primary: "#fafafa",
55
- disabled: "#27272a",
56
- default: "#18181b"
57
- },
58
- bg: { color: {
59
- bottom: "#09090b",
60
- middle: "#18181b",
61
- top: "#27272a",
62
- hover: "#27272a",
63
- black: "#000000"
64
- } },
65
- border: {
66
- color: "#3f3f46",
67
- mutedColor: "#27272a"
68
- },
69
- "text-color": {
70
- title: "#fafafa",
71
- main: "#fafafa",
72
- second: "#a1a1aa",
73
- placeholder: "#71717a",
74
- assist: "#71717a",
75
- disabled: "#71717a",
76
- white: "#ffffff"
77
- },
78
- shadow: {
79
- color: "#00000066",
80
- x: 0,
81
- y: 1,
82
- blur: 2,
83
- spread: 0,
84
- emboss: "none",
85
- sm: "0 1px 2px 0 rgba(0, 0, 0, 0.4)",
86
- lg: "0 8px 24px 0 rgba(0, 0, 0, 0.5), 0 2px 6px 0 rgba(0, 0, 0, 0.35)"
87
- },
88
- button: {
89
- "primary-text-color": "#000000",
90
- "primary-hover-bg": "#e4e4e7",
91
- "primary-hover-color": "#000000",
92
- "primary-plain-color": "#fafafa"
93
- },
94
- checkbox: { color: "#000000" },
95
- tag: { "primary-dark-color": "#000000" }
96
- });
97
- //#endregion
98
- export { shadcnDarkTheme, shadcnLightTheme };
99
-
100
- //# sourceMappingURL=shadcn.js.map
@@ -1 +0,0 @@
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"}