@vobs/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/app-shell-dom.d.ts +24 -0
- package/dist/app-shell-dom.js +573 -0
- package/dist/app-shell.d.ts +199 -0
- package/dist/app-shell.js +370 -0
- package/dist/base.css +52 -0
- package/dist/calendar-picker.d.ts +45 -0
- package/dist/calendar-picker.js +217 -0
- package/dist/calendar.d.ts +95 -0
- package/dist/calendar.js +194 -0
- package/dist/complex-inputs.d.ts +300 -0
- package/dist/complex-inputs.js +862 -0
- package/dist/components.css +4568 -0
- package/dist/data-display.d.ts +1063 -0
- package/dist/data-display.js +2298 -0
- package/dist/feedback.d.ts +88 -0
- package/dist/feedback.js +281 -0
- package/dist/forms.d.ts +378 -0
- package/dist/forms.js +1015 -0
- package/dist/icons-config.d.ts +25 -0
- package/dist/icons-config.js +14 -0
- package/dist/icons.d.ts +41 -0
- package/dist/icons.js +185 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +32 -0
- package/dist/locale/en-US.d.ts +31 -0
- package/dist/locale/en-US.js +20 -0
- package/dist/locale/zh-CN.d.ts +31 -0
- package/dist/locale/zh-CN.js +20 -0
- package/dist/navigation.d.ts +609 -0
- package/dist/navigation.js +1707 -0
- package/dist/overlay.d.ts +304 -0
- package/dist/overlay.js +969 -0
- package/dist/primitives.d.ts +268 -0
- package/dist/primitives.js +764 -0
- package/dist/styles.css +3 -0
- package/dist/theme-data.d.ts +363 -0
- package/dist/theme-data.js +374 -0
- package/dist/theme.d.ts +79 -0
- package/dist/theme.js +262 -0
- package/dist/tokens.css +449 -0
- package/dist/tree.d.ts +144 -0
- package/dist/tree.js +469 -0
- package/dist/virtual-list.d.ts +190 -0
- package/dist/virtual-list.js +521 -0
- package/dist/workbench.d.ts +136 -0
- package/dist/workbench.js +156 -0
- package/package.json +113 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/** @license MIT
|
|
2
|
+
* Copyright (c) 2026 vobsjs
|
|
3
|
+
* @vobs/ui
|
|
4
|
+
*/
|
|
5
|
+
export const DEFAULT_THEME = 'dark';
|
|
6
|
+
export const DEFAULT_BRAND_ID = 'green';
|
|
7
|
+
export const DEFAULT_BRAND_COLOR = '#32F08C';
|
|
8
|
+
export const brandPresets = [
|
|
9
|
+
{ id: 'green', label: 'Nimbus Green', color: DEFAULT_BRAND_COLOR },
|
|
10
|
+
{ id: 'blue', label: 'Nimbus Blue', color: '#80C1FF' },
|
|
11
|
+
{ id: 'purple', label: 'Nimbus Purple', color: '#8E80FF' },
|
|
12
|
+
{ id: 'yellow', label: 'Nimbus Yellow', color: '#FFD080' },
|
|
13
|
+
{ id: 'red', label: 'Nimbus Red', color: '#FF8080' },
|
|
14
|
+
{ id: 'teal', label: 'Nimbus Teal', color: '#2DD288' },
|
|
15
|
+
{ id: 'cyan', label: 'Nimbus Cyan', color: '#04CBE5' },
|
|
16
|
+
{ id: 'violet', label: 'Nimbus Violet', color: '#BFA5FF' },
|
|
17
|
+
{ id: 'slate', label: 'Nimbus Slate', color: '#AFB9CF' },
|
|
18
|
+
{ id: 'neutral', label: 'Nimbus Neutral', color: '#BABABA' },
|
|
19
|
+
];
|
|
20
|
+
export const primitiveTokenValues = {
|
|
21
|
+
'--radius-2': '2px',
|
|
22
|
+
'--radius-4': '4px',
|
|
23
|
+
'--radius-6': '6px',
|
|
24
|
+
'--radius-8': '8px',
|
|
25
|
+
'--radius-10': '10px',
|
|
26
|
+
'--radius-full': '9999px',
|
|
27
|
+
'--spacer-0': '0px',
|
|
28
|
+
'--spacer-4': '4px',
|
|
29
|
+
'--spacer-6': '6px',
|
|
30
|
+
'--spacer-8': '8px',
|
|
31
|
+
'--spacer-12': '12px',
|
|
32
|
+
'--spacer-16': '16px',
|
|
33
|
+
'--spacer-24': '24px',
|
|
34
|
+
'--spacer-32': '32px',
|
|
35
|
+
'--spacer-40': '40px',
|
|
36
|
+
'--k-icon-xs': '14px',
|
|
37
|
+
'--k-icon-sm': '16px',
|
|
38
|
+
'--k-icon-md': '20px',
|
|
39
|
+
'--k-icon-lg': '24px',
|
|
40
|
+
'--font-family-default': 'SF Pro Text',
|
|
41
|
+
'--font-family-heading': 'SF Pro',
|
|
42
|
+
'--font-family-mono': 'JetBrains Mono',
|
|
43
|
+
'--font-family-base': 'var(--font-family-default)',
|
|
44
|
+
'--font-family-heading-alias': 'var(--font-family-heading)',
|
|
45
|
+
'--font-family-mono-alias': 'var(--font-family-mono)',
|
|
46
|
+
'--body-xs-font-family': '"SF Pro Text", system-ui, -apple-system, sans-serif',
|
|
47
|
+
'--body-xs-font-size': '10px',
|
|
48
|
+
'--body-xs-font-weight': '400',
|
|
49
|
+
'--body-xs-line-height': '14px',
|
|
50
|
+
'--body-sm-font-family': '"SF Pro Text", system-ui, -apple-system, sans-serif',
|
|
51
|
+
'--body-sm-font-size': '11px',
|
|
52
|
+
'--body-sm-font-weight': '400',
|
|
53
|
+
'--body-sm-line-height': '16px',
|
|
54
|
+
'--body-sm-strong-font-family': '"SF Pro Text", system-ui, -apple-system, sans-serif',
|
|
55
|
+
'--body-sm-strong-font-size': '11px',
|
|
56
|
+
'--body-sm-strong-font-weight': '500',
|
|
57
|
+
'--body-sm-strong-line-height': '16px',
|
|
58
|
+
'--body-md-font-family': '"SF Pro Text", system-ui, -apple-system, sans-serif',
|
|
59
|
+
'--body-md-font-size': '12px',
|
|
60
|
+
'--body-md-font-weight': '400',
|
|
61
|
+
'--body-md-line-height': '18px',
|
|
62
|
+
'--body-md-strong-font-family': '"SF Pro Text", system-ui, -apple-system, sans-serif',
|
|
63
|
+
'--body-md-strong-font-size': '12px',
|
|
64
|
+
'--body-md-strong-font-weight': '500',
|
|
65
|
+
'--body-md-strong-line-height': '18px',
|
|
66
|
+
'--body-base-font-family': '"SF Pro Text", system-ui, -apple-system, sans-serif',
|
|
67
|
+
'--body-base-font-size': '13px',
|
|
68
|
+
'--body-base-font-weight': '400',
|
|
69
|
+
'--body-base-line-height': '20px',
|
|
70
|
+
'--body-base-strong-font-family': '"SF Pro Text", system-ui, -apple-system, sans-serif',
|
|
71
|
+
'--body-base-strong-font-size': '13px',
|
|
72
|
+
'--body-base-strong-font-weight': '500',
|
|
73
|
+
'--body-base-strong-line-height': '20px',
|
|
74
|
+
'--heading-3xs-font-family': '"SF Pro Text", system-ui, -apple-system, sans-serif',
|
|
75
|
+
'--heading-3xs-font-size': '11px',
|
|
76
|
+
'--heading-3xs-font-weight': '600',
|
|
77
|
+
'--heading-3xs-line-height': '16px',
|
|
78
|
+
'--heading-2xs-font-family': '"SF Pro Text", system-ui, -apple-system, sans-serif',
|
|
79
|
+
'--heading-2xs-font-size': '12px',
|
|
80
|
+
'--heading-2xs-font-weight': '600',
|
|
81
|
+
'--heading-2xs-line-height': '18px',
|
|
82
|
+
'--heading-xs-font-family': '"SF Pro Text", system-ui, -apple-system, sans-serif',
|
|
83
|
+
'--heading-xs-font-size': '13px',
|
|
84
|
+
'--heading-xs-font-weight': '600',
|
|
85
|
+
'--heading-xs-line-height': '20px',
|
|
86
|
+
'--heading-sm-font-family': '"SF Pro Text", system-ui, -apple-system, sans-serif',
|
|
87
|
+
'--heading-sm-font-size': '16px',
|
|
88
|
+
'--heading-sm-font-weight': '600',
|
|
89
|
+
'--heading-sm-line-height': '24px',
|
|
90
|
+
'--heading-md-font-family': '"SF Pro", system-ui, -apple-system, sans-serif',
|
|
91
|
+
'--heading-md-font-size': '20px',
|
|
92
|
+
'--heading-md-font-weight': '600',
|
|
93
|
+
'--heading-md-line-height': '28px',
|
|
94
|
+
'--heading-lg-font-family': '"SF Pro", system-ui, -apple-system, sans-serif',
|
|
95
|
+
'--heading-lg-font-size': '22px',
|
|
96
|
+
'--heading-lg-font-weight': '600',
|
|
97
|
+
'--heading-lg-line-height': '30px',
|
|
98
|
+
'--heading-xl-font-family': '"SF Pro", system-ui, -apple-system, sans-serif',
|
|
99
|
+
'--heading-xl-font-size': '24px',
|
|
100
|
+
'--heading-xl-font-weight': '600',
|
|
101
|
+
'--heading-xl-line-height': '32px',
|
|
102
|
+
'--heading-2xl-font-family': '"SF Pro", system-ui, -apple-system, sans-serif',
|
|
103
|
+
'--heading-2xl-font-size': '28px',
|
|
104
|
+
'--heading-2xl-font-weight': '600',
|
|
105
|
+
'--heading-2xl-line-height': '36px',
|
|
106
|
+
'--heading-3xl-font-family': '"SF Pro", system-ui, -apple-system, sans-serif',
|
|
107
|
+
'--heading-3xl-font-size': '32px',
|
|
108
|
+
'--heading-3xl-font-weight': '600',
|
|
109
|
+
'--heading-3xl-line-height': '40px',
|
|
110
|
+
'--code-editor-font-family': '"JetBrains Mono", system-ui, -apple-system, sans-serif',
|
|
111
|
+
'--code-editor-font-size': '13px',
|
|
112
|
+
'--code-editor-font-weight': '450',
|
|
113
|
+
'--code-editor-line-height': '20px',
|
|
114
|
+
'--code-terminal-font-family': '"JetBrains Mono", system-ui, -apple-system, sans-serif',
|
|
115
|
+
'--code-terminal-font-size': '12px',
|
|
116
|
+
'--code-terminal-font-weight': '450',
|
|
117
|
+
'--code-terminal-line-height': '18px',
|
|
118
|
+
'--font-weight-default': '400',
|
|
119
|
+
'--font-weight-code': '450',
|
|
120
|
+
'--font-weight-medium': '500',
|
|
121
|
+
'--font-weight-strong': '600',
|
|
122
|
+
'--z-index-dropdown': '2000',
|
|
123
|
+
'--z-index-drawer': '2500',
|
|
124
|
+
'--z-index-modal': '3000',
|
|
125
|
+
'--z-index-notification': '3500',
|
|
126
|
+
'--z-index-tooltip': '4000',
|
|
127
|
+
'--transition-fast': '0.12s ease',
|
|
128
|
+
'--transition-normal': '0.2s ease',
|
|
129
|
+
'--transition-slow': '0.3s ease',
|
|
130
|
+
'--breakpoint-sm': '576px',
|
|
131
|
+
'--breakpoint-md': '768px',
|
|
132
|
+
'--breakpoint-lg': '1024px',
|
|
133
|
+
'--breakpoint-xl': '1280px',
|
|
134
|
+
};
|
|
135
|
+
const semanticAliases = {
|
|
136
|
+
'--color-background': 'var(--bg-base-default)',
|
|
137
|
+
'--color-surface': 'var(--bg-base-secondary)',
|
|
138
|
+
'--color-surface-variant': 'var(--bg-base-tertiary)',
|
|
139
|
+
'--color-overlay-1': 'var(--bg-overlay-l1)',
|
|
140
|
+
'--color-overlay-2': 'var(--bg-overlay-l2)',
|
|
141
|
+
'--color-overlay-3': 'var(--bg-overlay-l3)',
|
|
142
|
+
'--color-overlay-4': 'var(--bg-overlay-l4)',
|
|
143
|
+
'--color-card': 'var(--bg-base-secondary)',
|
|
144
|
+
'--color-tooltip': 'var(--bg-tooltip)',
|
|
145
|
+
'--color-menu': 'var(--bg-menu)',
|
|
146
|
+
'--color-foreground': 'var(--text-default)',
|
|
147
|
+
'--color-foreground-hover': 'var(--text-default-hover)',
|
|
148
|
+
'--color-on-surface': 'var(--text-default)',
|
|
149
|
+
'--color-on-surface-variant': 'var(--text-secondary)',
|
|
150
|
+
'--color-muted-foreground': 'var(--text-secondary)',
|
|
151
|
+
'--color-disabled-foreground': 'var(--text-disabled)',
|
|
152
|
+
'--color-border': 'var(--border-neutral-l1)',
|
|
153
|
+
'--color-border-strong': 'var(--border-neutral-l2)',
|
|
154
|
+
'--color-border-stronger': 'var(--border-neutral-l3)',
|
|
155
|
+
'--color-border-contrast': 'var(--border-contrast)',
|
|
156
|
+
'--color-outline': 'var(--border-neutral-l1)',
|
|
157
|
+
'--color-primary': 'var(--bg-brand)',
|
|
158
|
+
'--color-primary-hover': 'var(--bg-brand-hover)',
|
|
159
|
+
'--color-primary-disabled': 'var(--bg-brand-disabled)',
|
|
160
|
+
'--color-primary-soft': 'var(--bg-brand-popup)',
|
|
161
|
+
'--color-accent': 'var(--bg-brand)',
|
|
162
|
+
'--color-on-primary': 'var(--text-onbrand)',
|
|
163
|
+
'--color-info': 'var(--status-primary-default)',
|
|
164
|
+
'--color-success': 'var(--status-success-default)',
|
|
165
|
+
'--color-warning': 'var(--status-warning-default)',
|
|
166
|
+
'--color-error': 'var(--status-error-default)',
|
|
167
|
+
'--color-error-soft': 'var(--status-error-surface-l1)',
|
|
168
|
+
};
|
|
169
|
+
export const themeTokenValues = {
|
|
170
|
+
dark: {
|
|
171
|
+
'--bg-base-default': '#1A1B1D',
|
|
172
|
+
'--bg-base-secondary': '#222427',
|
|
173
|
+
'--bg-base-tertiary': '#2A2D31',
|
|
174
|
+
'--bg-tooltip': '#1A1B1D',
|
|
175
|
+
'--bg-menu': '#202123',
|
|
176
|
+
'--bg-invert': '#DADDE5',
|
|
177
|
+
'--bg-invert-hover': '#EDEFF2',
|
|
178
|
+
'--bg-invert-active': '#979AA4',
|
|
179
|
+
'--bg-invert-disabled': 'rgba(224, 226, 242, 0.12)',
|
|
180
|
+
'--bg-overlay-l1': 'rgba(224, 226, 242, 0.04)',
|
|
181
|
+
'--bg-overlay-l2': 'rgba(224, 226, 242, 0.06)',
|
|
182
|
+
'--bg-overlay-l3': 'rgba(224, 226, 242, 0.08)',
|
|
183
|
+
'--bg-overlay-l4': 'rgba(224, 226, 242, 0.12)',
|
|
184
|
+
'--text-default': '#D1D3DB',
|
|
185
|
+
'--text-default-hover': '#F5F9FE',
|
|
186
|
+
'--text-default-active': '#F5F9FE',
|
|
187
|
+
'--text-secondary': '#9599A6',
|
|
188
|
+
'--text-secondary-hover': '#D1D3DB',
|
|
189
|
+
'--text-secondary-active': '#D1D3DB',
|
|
190
|
+
'--text-tertiary': '#666B75',
|
|
191
|
+
'--text-disabled': '#666B75',
|
|
192
|
+
'--text-onbrand': '#0C0C0D',
|
|
193
|
+
'--text-onaccent': '#0C0C0D',
|
|
194
|
+
'--icon-default': '#D1D3DB',
|
|
195
|
+
'--icon-default-hover': '#F5F9FE',
|
|
196
|
+
'--icon-default-active': '#F5F9FE',
|
|
197
|
+
'--icon-secondary': '#9599A6',
|
|
198
|
+
'--icon-secondary-hover': '#D1D3DB',
|
|
199
|
+
'--icon-secondary-active': '#D1D3DB',
|
|
200
|
+
'--icon-tertiary': '#666B75',
|
|
201
|
+
'--icon-disabled': '#666B75',
|
|
202
|
+
'--icon-onbrand': '#0C0C0D',
|
|
203
|
+
'--icon-onaccent': '#0C0C0D',
|
|
204
|
+
'--border-neutral-l1': 'rgba(224, 226, 242, 0.1)',
|
|
205
|
+
'--border-neutral-l2': 'rgba(224, 226, 242, 0.16)',
|
|
206
|
+
'--border-neutral-l3': 'rgba(224, 226, 242, 0.2)',
|
|
207
|
+
'--border-contrast': '#FFFFFF',
|
|
208
|
+
'--status-primary-default': '#387BFF',
|
|
209
|
+
'--status-primary-hover': '#4C88FF',
|
|
210
|
+
'--status-primary-active': '#1759DD',
|
|
211
|
+
'--status-primary-surface-l1': 'rgba(53, 121, 255, 0.18)',
|
|
212
|
+
'--status-primary-surface-l2': 'rgba(53, 121, 255, 0.28)',
|
|
213
|
+
'--status-primary-surface-l3': 'rgba(53, 121, 255, 0.36)',
|
|
214
|
+
'--status-info-default': 'var(--status-primary-default)',
|
|
215
|
+
'--status-info-surface-l1': 'var(--status-primary-surface-l1)',
|
|
216
|
+
'--status-success-default': '#33C192',
|
|
217
|
+
'--status-success-hover': '#5ED4AD',
|
|
218
|
+
'--status-success-active': '#27B082',
|
|
219
|
+
'--status-success-surface-l1': 'rgba(0, 165, 110, 0.18)',
|
|
220
|
+
'--status-success-surface-l2': 'rgba(0, 165, 110, 0.28)',
|
|
221
|
+
'--status-success-surface-l3': 'rgba(0, 165, 110, 0.36)',
|
|
222
|
+
'--status-alert-default': '#D29D00',
|
|
223
|
+
'--status-alert-hover': '#DFB949',
|
|
224
|
+
'--status-alert-active': '#AB8820',
|
|
225
|
+
'--status-alert-surface-l1': 'rgba(210, 157, 0, 0.16)',
|
|
226
|
+
'--status-alert-surface-l2': 'rgba(210, 157, 0, 0.28)',
|
|
227
|
+
'--status-alert-surface-l3': 'rgba(210, 157, 0, 0.36)',
|
|
228
|
+
'--status-warning-default': '#D27E24',
|
|
229
|
+
'--status-warning-hover': '#D78B28',
|
|
230
|
+
'--status-warning-active': '#B46510',
|
|
231
|
+
'--status-warning-surface-l1': 'rgba(210, 126, 36, 0.16)',
|
|
232
|
+
'--status-warning-surface-l2': 'rgba(210, 126, 36, 0.28)',
|
|
233
|
+
'--status-warning-surface-l3': 'rgba(210, 126, 36, 0.36)',
|
|
234
|
+
'--status-error-default': '#F65A5A',
|
|
235
|
+
'--status-error-hover': '#F86262',
|
|
236
|
+
'--status-error-active': '#B33636',
|
|
237
|
+
'--status-error-surface-l1': 'rgba(246, 70, 70, 0.18)',
|
|
238
|
+
'--status-error-surface-l2': 'rgba(246, 70, 70, 0.28)',
|
|
239
|
+
'--status-error-surface-l3': 'rgba(246, 70, 70, 0.36)',
|
|
240
|
+
'--code-text': '#E0E3EE',
|
|
241
|
+
'--code-doc': '#7F838C',
|
|
242
|
+
'--code-link': '#3C7EFF',
|
|
243
|
+
'--code-number': '#F48CCA',
|
|
244
|
+
'--code-action': '#B38CFF',
|
|
245
|
+
'--code-instruction': '#F0D8FF',
|
|
246
|
+
'--code-function': '#F29D79',
|
|
247
|
+
'--code-constant': '#80BBFF',
|
|
248
|
+
'--code-parameter': '#82D99F',
|
|
249
|
+
'--code-attribute': '#DED47E',
|
|
250
|
+
'--code-tag': '#F2858C',
|
|
251
|
+
...semanticAliases,
|
|
252
|
+
},
|
|
253
|
+
light: {
|
|
254
|
+
'--bg-base-default': '#FFFFFF',
|
|
255
|
+
'--bg-base-secondary': '#F5F6F8',
|
|
256
|
+
'--bg-base-tertiary': '#EDF0F5',
|
|
257
|
+
'--bg-overlay-l1': 'rgba(0, 0, 0, 0.04)',
|
|
258
|
+
'--bg-overlay-l2': 'rgba(0, 0, 0, 0.06)',
|
|
259
|
+
'--bg-overlay-l3': 'rgba(0, 0, 0, 0.08)',
|
|
260
|
+
'--bg-overlay-l4': 'rgba(0, 0, 0, 0.12)',
|
|
261
|
+
'--text-default': '#0F1117',
|
|
262
|
+
'--text-secondary': '#4B5563',
|
|
263
|
+
'--text-tertiary': '#5B6370',
|
|
264
|
+
'--text-disabled': '#525A68',
|
|
265
|
+
'--text-onbrand': '#FFFFFF',
|
|
266
|
+
'--icon-default': '#0F1117',
|
|
267
|
+
'--icon-secondary': '#4B5563',
|
|
268
|
+
'--icon-tertiary': '#5B6370',
|
|
269
|
+
'--icon-disabled': '#525A68',
|
|
270
|
+
'--border-neutral-l1': 'rgba(0, 0, 0, 0.12)',
|
|
271
|
+
'--border-neutral-l2': 'rgba(0, 0, 0, 0.18)',
|
|
272
|
+
'--border-neutral-l3': 'rgba(0, 0, 0, 0.25)',
|
|
273
|
+
'--status-success-default': '#16A34A',
|
|
274
|
+
'--status-warning-default': '#CA8A04',
|
|
275
|
+
'--status-error-default': '#DC2626',
|
|
276
|
+
'--status-info-default': '#2E69FF',
|
|
277
|
+
'--status-success-surface-l1': 'rgba(22, 163, 74, 0.08)',
|
|
278
|
+
'--status-warning-surface-l1': 'rgba(202, 138, 4, 0.08)',
|
|
279
|
+
'--status-error-surface-l1': 'rgba(220, 38, 38, 0.08)',
|
|
280
|
+
'--status-info-surface-l1': 'rgba(46, 105, 255, 0.08)',
|
|
281
|
+
...semanticAliases,
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
export function resolveBrandColor(value = DEFAULT_BRAND_ID) {
|
|
285
|
+
if (typeof value === 'object') {
|
|
286
|
+
const color = normalizeHexColor(value.color);
|
|
287
|
+
return {
|
|
288
|
+
id: value.id?.trim() || 'custom',
|
|
289
|
+
label: value.label?.trim() || value.id?.trim() || color,
|
|
290
|
+
color,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
const input = value.trim();
|
|
294
|
+
const preset = brandPresets.find((item) => item.id === input || item.color.toLowerCase() === input.toLowerCase());
|
|
295
|
+
if (preset !== undefined)
|
|
296
|
+
return preset;
|
|
297
|
+
const color = normalizeHexColor(input);
|
|
298
|
+
return { id: 'custom', label: color, color };
|
|
299
|
+
}
|
|
300
|
+
export function deriveBrandTokens(color) {
|
|
301
|
+
const normalized = normalizeHexColor(color);
|
|
302
|
+
if (normalized === DEFAULT_BRAND_COLOR) {
|
|
303
|
+
return {
|
|
304
|
+
background: DEFAULT_BRAND_COLOR,
|
|
305
|
+
hover: '#0FDC78',
|
|
306
|
+
disabled: 'rgba(50, 240, 140, 0.2)',
|
|
307
|
+
soft: 'rgba(50, 240, 140, 0.12)',
|
|
308
|
+
onBrand: '#0C0C0D',
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
const dark = perceivedBrightness(normalized) < 150;
|
|
312
|
+
return {
|
|
313
|
+
background: normalized,
|
|
314
|
+
hover: mixHex(normalized, dark ? '#FFFFFF' : '#000000', 0.14),
|
|
315
|
+
disabled: hexToRgba(normalized, 0.3),
|
|
316
|
+
soft: hexToRgba(normalized, 0.12),
|
|
317
|
+
onBrand: contrastRatio(normalized, '#FFFFFF') >= contrastRatio(normalized, '#0C0C0D')
|
|
318
|
+
? '#FFFFFF'
|
|
319
|
+
: '#0C0C0D',
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
export function normalizeHexColor(value) {
|
|
323
|
+
const input = value.trim();
|
|
324
|
+
const short = /^#?([0-9a-f]{3})$/iu.exec(input);
|
|
325
|
+
if (short?.[1] !== undefined) {
|
|
326
|
+
return `#${Array.from(short[1], (character) => character.repeat(2)).join('')}`.toUpperCase();
|
|
327
|
+
}
|
|
328
|
+
const full = /^#?([0-9a-f]{6})$/iu.exec(input);
|
|
329
|
+
if (full?.[1] !== undefined)
|
|
330
|
+
return `#${full[1]}`.toUpperCase();
|
|
331
|
+
throw new TypeError(`Invalid brand color: ${value}`);
|
|
332
|
+
}
|
|
333
|
+
export function contrastRatio(left, right) {
|
|
334
|
+
const leftLuminance = relativeLuminance(normalizeHexColor(left));
|
|
335
|
+
const rightLuminance = relativeLuminance(normalizeHexColor(right));
|
|
336
|
+
const lighter = Math.max(leftLuminance, rightLuminance);
|
|
337
|
+
const darker = Math.min(leftLuminance, rightLuminance);
|
|
338
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
339
|
+
}
|
|
340
|
+
function relativeLuminance(color) {
|
|
341
|
+
const { red, green, blue } = hexToRgb(color);
|
|
342
|
+
const linear = (channel) => {
|
|
343
|
+
const value = channel / 255;
|
|
344
|
+
return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
|
|
345
|
+
};
|
|
346
|
+
return 0.2126 * linear(red) + 0.7152 * linear(green) + 0.0722 * linear(blue);
|
|
347
|
+
}
|
|
348
|
+
function perceivedBrightness(color) {
|
|
349
|
+
const { red, green, blue } = hexToRgb(color);
|
|
350
|
+
return red * 0.299 + green * 0.587 + blue * 0.114;
|
|
351
|
+
}
|
|
352
|
+
function mixHex(color, target, amount) {
|
|
353
|
+
const sourceRgb = hexToRgb(color);
|
|
354
|
+
const targetRgb = hexToRgb(target);
|
|
355
|
+
const mix = (source, destination) => Math.round(source + (destination - source) * amount);
|
|
356
|
+
return rgbToHex(mix(sourceRgb.red, targetRgb.red), mix(sourceRgb.green, targetRgb.green), mix(sourceRgb.blue, targetRgb.blue));
|
|
357
|
+
}
|
|
358
|
+
function hexToRgba(color, alpha) {
|
|
359
|
+
const { red, green, blue } = hexToRgb(color);
|
|
360
|
+
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
|
|
361
|
+
}
|
|
362
|
+
function hexToRgb(color) {
|
|
363
|
+
const normalized = normalizeHexColor(color).slice(1);
|
|
364
|
+
return {
|
|
365
|
+
red: Number.parseInt(normalized.slice(0, 2), 16),
|
|
366
|
+
green: Number.parseInt(normalized.slice(2, 4), 16),
|
|
367
|
+
blue: Number.parseInt(normalized.slice(4, 6), 16),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function rgbToHex(red, green, blue) {
|
|
371
|
+
return `#${[red, green, blue]
|
|
372
|
+
.map((channel) => channel.toString(16).padStart(2, '0'))
|
|
373
|
+
.join('')}`.toUpperCase();
|
|
374
|
+
}
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** @license MIT
|
|
2
|
+
* Copyright (c) 2026 vobsjs
|
|
3
|
+
* @vobs/ui
|
|
4
|
+
*/
|
|
5
|
+
import { type BrandColor, type BrandInput, type ThemeName } from './theme-data.js';
|
|
6
|
+
export { brandPresets, contrastRatio, DEFAULT_BRAND_COLOR, DEFAULT_BRAND_ID, DEFAULT_THEME, deriveBrandTokens, normalizeHexColor, resolveBrandColor, type BrandColor, type BrandColorInput, type BrandInput, type BrandTokens, type ThemeName, } from './theme-data.js';
|
|
7
|
+
export interface ThemeStyleTarget {
|
|
8
|
+
setProperty(name: string, value: string): void;
|
|
9
|
+
removeProperty(name: string): string;
|
|
10
|
+
}
|
|
11
|
+
export interface ThemeTarget {
|
|
12
|
+
readonly style: ThemeStyleTarget;
|
|
13
|
+
setAttribute(name: string, value: string): void;
|
|
14
|
+
}
|
|
15
|
+
export interface ThemePersistence {
|
|
16
|
+
readTheme?(): unknown;
|
|
17
|
+
writeTheme?(theme: ThemeName): void;
|
|
18
|
+
readBrand?(): unknown;
|
|
19
|
+
writeBrand?(brand: BrandColor): void;
|
|
20
|
+
}
|
|
21
|
+
export interface SystemThemePreference {
|
|
22
|
+
read(): ThemeName;
|
|
23
|
+
subscribe?(listener: (theme: ThemeName) => void): () => void;
|
|
24
|
+
}
|
|
25
|
+
export interface ThemeStorage {
|
|
26
|
+
getItem(key: string): string | null;
|
|
27
|
+
setItem(key: string, value: string): void;
|
|
28
|
+
}
|
|
29
|
+
export interface ThemeMediaQuery {
|
|
30
|
+
readonly matches: boolean;
|
|
31
|
+
addEventListener(type: 'change', listener: (event: {
|
|
32
|
+
readonly matches: boolean;
|
|
33
|
+
}) => void): void;
|
|
34
|
+
removeEventListener(type: 'change', listener: (event: {
|
|
35
|
+
readonly matches: boolean;
|
|
36
|
+
}) => void): void;
|
|
37
|
+
}
|
|
38
|
+
export interface ThemeState {
|
|
39
|
+
readonly theme: ThemeName;
|
|
40
|
+
readonly brand: BrandColor;
|
|
41
|
+
}
|
|
42
|
+
export interface ThemeControllerOptions {
|
|
43
|
+
readonly target: ThemeTarget;
|
|
44
|
+
readonly initialTheme?: ThemeName;
|
|
45
|
+
readonly initialBrand?: BrandInput;
|
|
46
|
+
readonly persistence?: ThemePersistence;
|
|
47
|
+
readonly systemPreference?: SystemThemePreference;
|
|
48
|
+
readonly onWarning?: (message: string) => void;
|
|
49
|
+
}
|
|
50
|
+
export interface ThemeController {
|
|
51
|
+
readonly theme: ThemeName;
|
|
52
|
+
readonly brand: BrandColor;
|
|
53
|
+
readonly state: ThemeState;
|
|
54
|
+
setTheme(theme: ThemeName): void;
|
|
55
|
+
toggleTheme(): ThemeName;
|
|
56
|
+
setBrand(brand: BrandInput): BrandColor;
|
|
57
|
+
subscribe(listener: (state: ThemeState) => void): () => void;
|
|
58
|
+
destroy(): void;
|
|
59
|
+
}
|
|
60
|
+
export interface ThemeAttributes {
|
|
61
|
+
readonly 'data-theme': ThemeName;
|
|
62
|
+
readonly 'data-brand': string;
|
|
63
|
+
readonly style?: string;
|
|
64
|
+
}
|
|
65
|
+
export declare function createThemeController(options: ThemeControllerOptions): ThemeController;
|
|
66
|
+
export declare function createStorageThemePersistence(storage: ThemeStorage, keys?: {
|
|
67
|
+
readonly theme?: string;
|
|
68
|
+
readonly brand?: string;
|
|
69
|
+
}): ThemePersistence;
|
|
70
|
+
export declare function createMediaThemePreference(query: ThemeMediaQuery): SystemThemePreference;
|
|
71
|
+
export declare function createThemeState(theme?: ThemeName, brand?: BrandInput): ThemeState;
|
|
72
|
+
export declare function createThemeAttributes(state: ThemeState): ThemeAttributes;
|
|
73
|
+
export declare function createThemeBootstrapScript(state: ThemeState): string;
|
|
74
|
+
/**
|
|
75
|
+
* Reads a design-token value (CSS variable) for the active theme, for third-party adapters (contract ui-adapters.md §3.1).
|
|
76
|
+
* Data source: tokens.css variables on html[data-theme] (getComputedStyle reads the latest effective value).
|
|
77
|
+
* Returns undefined without a document (SSR/non-browser) or when the variable is undefined — adapters must degrade gracefully.
|
|
78
|
+
*/
|
|
79
|
+
export declare function resolveUiToken(name: `--${string}`): string | undefined;
|