@rokkit/ui 1.3.4 → 1.3.5

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 (175) hide show
  1. package/dist/components/index.js +63 -0
  2. package/dist/index.js +8 -0
  3. package/dist/markdown-plugin.js +1 -0
  4. package/dist/types/alert-list.js +7 -0
  5. package/dist/types/avatar.js +4 -0
  6. package/dist/types/badge.js +4 -0
  7. package/dist/types/breadcrumbs.js +4 -0
  8. package/dist/types/button.js +7 -0
  9. package/dist/types/card.js +4 -0
  10. package/dist/types/carousel.js +4 -0
  11. package/dist/types/chat.js +1 -0
  12. package/dist/types/code.js +12 -0
  13. package/dist/types/connector.js +8 -0
  14. package/dist/types/divider.js +4 -0
  15. package/dist/types/floating-action.js +12 -0
  16. package/dist/types/floating-navigation.js +1 -0
  17. package/dist/types/frame.js +8 -0
  18. package/dist/types/index.js +93 -0
  19. package/dist/types/item-content.js +8 -0
  20. package/dist/types/item-switch.js +10 -0
  21. package/dist/types/item-toggle.js +12 -0
  22. package/dist/types/list.js +36 -0
  23. package/dist/types/lock-mode.js +1 -0
  24. package/dist/types/menu.js +25 -0
  25. package/dist/types/message.js +4 -0
  26. package/dist/types/nav-content.js +4 -0
  27. package/dist/types/palette.js +54 -0
  28. package/dist/types/pill.js +4 -0
  29. package/dist/types/progress-bar.js +4 -0
  30. package/dist/types/range.js +7 -0
  31. package/dist/types/rating.js +4 -0
  32. package/dist/types/responsive-grid.js +4 -0
  33. package/dist/types/reveal.js +4 -0
  34. package/dist/types/search-filter.js +7 -0
  35. package/dist/types/select.js +21 -0
  36. package/dist/types/shine.js +7 -0
  37. package/dist/types/stack.js +4 -0
  38. package/dist/types/status-list.js +7 -0
  39. package/dist/types/stepper.js +4 -0
  40. package/dist/types/switch.js +7 -0
  41. package/dist/types/table.js +23 -0
  42. package/dist/types/tabs.js +7 -0
  43. package/dist/types/tilt.js +4 -0
  44. package/dist/types/timeline.js +14 -0
  45. package/dist/types/toggle.js +7 -0
  46. package/dist/types/toolbar.js +20 -0
  47. package/dist/types/tooltip.js +1 -0
  48. package/dist/types/tree-table.js +10 -0
  49. package/dist/types/tree.js +94 -0
  50. package/dist/types/upload-file-status.js +8 -0
  51. package/dist/types/upload-progress.js +8 -0
  52. package/dist/types/upload-target.js +7 -0
  53. package/dist/utils/palette.js +641 -0
  54. package/dist/utils/relative-time.js +17 -0
  55. package/dist/utils/shiki.js +118 -0
  56. package/package.json +13 -14
  57. package/src/components/index.ts +0 -63
  58. package/src/index.ts +0 -75
  59. package/src/markdown-plugin.ts +0 -12
  60. package/src/types/alert-list.ts +0 -28
  61. package/src/types/avatar.ts +0 -41
  62. package/src/types/badge.ts +0 -37
  63. package/src/types/breadcrumbs.ts +0 -43
  64. package/src/types/button.ts +0 -92
  65. package/src/types/card.ts +0 -38
  66. package/src/types/carousel.ts +0 -49
  67. package/src/types/chat.ts +0 -81
  68. package/src/types/code.ts +0 -147
  69. package/src/types/connector.ts +0 -22
  70. package/src/types/divider.ts +0 -20
  71. package/src/types/floating-action.ts +0 -123
  72. package/src/types/floating-navigation.ts +0 -80
  73. package/src/types/frame.ts +0 -29
  74. package/src/types/index.ts +0 -137
  75. package/src/types/item-content.ts +0 -26
  76. package/src/types/item-switch.ts +0 -34
  77. package/src/types/item-toggle.ts +0 -52
  78. package/src/types/list.ts +0 -200
  79. package/src/types/lock-mode.ts +0 -16
  80. package/src/types/menu.ts +0 -95
  81. package/src/types/message.ts +0 -54
  82. package/src/types/nav-content.ts +0 -31
  83. package/src/types/palette.ts +0 -160
  84. package/src/types/pill.ts +0 -43
  85. package/src/types/progress-bar.ts +0 -19
  86. package/src/types/range.ts +0 -51
  87. package/src/types/rating.ts +0 -43
  88. package/src/types/responsive-grid.ts +0 -25
  89. package/src/types/reveal.ts +0 -49
  90. package/src/types/search-filter.ts +0 -67
  91. package/src/types/select.ts +0 -176
  92. package/src/types/shine.ts +0 -37
  93. package/src/types/stack.ts +0 -40
  94. package/src/types/status-list.ts +0 -42
  95. package/src/types/stepper.ts +0 -74
  96. package/src/types/switch.ts +0 -68
  97. package/src/types/table.ts +0 -219
  98. package/src/types/tabs.ts +0 -103
  99. package/src/types/tilt.ts +0 -28
  100. package/src/types/timeline.ts +0 -53
  101. package/src/types/toggle.ts +0 -81
  102. package/src/types/toolbar.ts +0 -167
  103. package/src/types/tooltip.ts +0 -21
  104. package/src/types/tree-table.ts +0 -103
  105. package/src/types/tree.ts +0 -302
  106. package/src/types/upload-file-status.ts +0 -45
  107. package/src/types/upload-progress.ts +0 -111
  108. package/src/types/upload-target.ts +0 -71
  109. package/src/utils/palette.ts +0 -750
  110. package/src/utils/relative-time.ts +0 -12
  111. package/src/utils/shiki.ts +0 -137
  112. /package/{src → dist}/MarkdownRenderer.svelte +0 -0
  113. /package/{src → dist}/components/AlertList.svelte +0 -0
  114. /package/{src → dist}/components/Avatar.svelte +0 -0
  115. /package/{src → dist}/components/Badge.svelte +0 -0
  116. /package/{src → dist}/components/BreadCrumbs.svelte +0 -0
  117. /package/{src → dist}/components/Button.svelte +0 -0
  118. /package/{src → dist}/components/ButtonGroup.svelte +0 -0
  119. /package/{src → dist}/components/Card.svelte +0 -0
  120. /package/{src → dist}/components/Carousel.svelte +0 -0
  121. /package/{src → dist}/components/ChatComposer.svelte +0 -0
  122. /package/{src → dist}/components/ChatHistory.svelte +0 -0
  123. /package/{src → dist}/components/ChatMessage.svelte +0 -0
  124. /package/{src → dist}/components/ChatShell.svelte +0 -0
  125. /package/{src → dist}/components/ChatTimeline.svelte +0 -0
  126. /package/{src → dist}/components/Code.svelte +0 -0
  127. /package/{src → dist}/components/CodeBlock.svelte +0 -0
  128. /package/{src → dist}/components/CodeGroup.svelte +0 -0
  129. /package/{src → dist}/components/CommandPalette.svelte +0 -0
  130. /package/{src → dist}/components/Connector.svelte +0 -0
  131. /package/{src → dist}/components/Divider.svelte +0 -0
  132. /package/{src → dist}/components/Dropdown.svelte +0 -0
  133. /package/{src → dist}/components/FloatingAction.svelte +0 -0
  134. /package/{src → dist}/components/FloatingNavigation.svelte +0 -0
  135. /package/{src → dist}/components/Frame.svelte +0 -0
  136. /package/{src → dist}/components/Grid.svelte +0 -0
  137. /package/{src → dist}/components/ItemContent.svelte +0 -0
  138. /package/{src → dist}/components/ItemSwitch.svelte +0 -0
  139. /package/{src → dist}/components/ItemToggle.svelte +0 -0
  140. /package/{src → dist}/components/LazyTree.svelte +0 -0
  141. /package/{src → dist}/components/List.svelte +0 -0
  142. /package/{src → dist}/components/LockMode.svelte +0 -0
  143. /package/{src → dist}/components/Menu.svelte +0 -0
  144. /package/{src → dist}/components/Message.svelte +0 -0
  145. /package/{src → dist}/components/MultiSelect.svelte +0 -0
  146. /package/{src → dist}/components/NavContent.svelte +0 -0
  147. /package/{src → dist}/components/PaletteManager.svelte +0 -0
  148. /package/{src → dist}/components/Pill.svelte +0 -0
  149. /package/{src → dist}/components/ProgressBar.svelte +0 -0
  150. /package/{src → dist}/components/Range.svelte +0 -0
  151. /package/{src → dist}/components/Rating.svelte +0 -0
  152. /package/{src → dist}/components/ResponsiveGrid.svelte +0 -0
  153. /package/{src → dist}/components/Reveal.svelte +0 -0
  154. /package/{src → dist}/components/SearchFilter.svelte +0 -0
  155. /package/{src → dist}/components/Select.svelte +0 -0
  156. /package/{src → dist}/components/Shine.svelte +0 -0
  157. /package/{src → dist}/components/Stack.svelte +0 -0
  158. /package/{src → dist}/components/StatusList.svelte +0 -0
  159. /package/{src → dist}/components/Stepper.svelte +0 -0
  160. /package/{src → dist}/components/Swatch.svelte +0 -0
  161. /package/{src → dist}/components/Switch.svelte +0 -0
  162. /package/{src → dist}/components/Table.svelte +0 -0
  163. /package/{src → dist}/components/Tabs.svelte +0 -0
  164. /package/{src → dist}/components/Tilt.svelte +0 -0
  165. /package/{src → dist}/components/Timeline.svelte +0 -0
  166. /package/{src → dist}/components/Toggle.svelte +0 -0
  167. /package/{src → dist}/components/Toolbar.svelte +0 -0
  168. /package/{src → dist}/components/ToolbarGroup.svelte +0 -0
  169. /package/{src → dist}/components/Tooltip.svelte +0 -0
  170. /package/{src → dist}/components/Tree.svelte +0 -0
  171. /package/{src → dist}/components/TreeTable.svelte +0 -0
  172. /package/{src → dist}/components/UploadFileStatus.svelte +0 -0
  173. /package/{src → dist}/components/UploadProgress.svelte +0 -0
  174. /package/{src → dist}/components/UploadTarget.svelte +0 -0
  175. /package/{src → dist}/utils/upload.js +0 -0
@@ -1,750 +0,0 @@
1
- /**
2
- * Palette Utilities
3
- *
4
- * Functions for color manipulation, shade generation, and runtime palette application.
5
- */
6
-
7
- // =============================================================================
8
- // Types
9
- // =============================================================================
10
-
11
- export interface RGB {
12
- r: number
13
- g: number
14
- b: number
15
- }
16
-
17
- export interface HSL {
18
- h: number
19
- s: number
20
- l: number
21
- }
22
-
23
- export interface OKLCH {
24
- L: number
25
- C: number
26
- H: number
27
- }
28
-
29
- export type ColorSpace = 'rgb' | 'hsl' | 'oklch'
30
-
31
- export type ShadeKey =
32
- | '50'
33
- | '100'
34
- | '200'
35
- | '300'
36
- | '400'
37
- | '500'
38
- | '600'
39
- | '700'
40
- | '800'
41
- | '900'
42
- | '950'
43
-
44
- export type Shades = Record<ShadeKey, string>
45
-
46
- export type ColorRole =
47
- | 'primary'
48
- | 'secondary'
49
- | 'accent'
50
- | 'surface'
51
- | 'success'
52
- | 'warning'
53
- | 'danger'
54
- | 'info'
55
-
56
- /**
57
- * Mapping of color roles to Tailwind color names or hex values
58
- */
59
- export type ColorMapping = Partial<Record<ColorRole, string>>
60
-
61
- // =============================================================================
62
- // Color Conversion Utilities
63
- // =============================================================================
64
-
65
- /**
66
- * Convert hex color to RGB
67
- */
68
- export function hexToRgb(hex: string): RGB {
69
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
70
- if (!result) {
71
- throw new Error(`Invalid hex color: ${hex}`)
72
- }
73
- return {
74
- r: parseInt(result[1], 16),
75
- g: parseInt(result[2], 16),
76
- b: parseInt(result[3], 16)
77
- }
78
- }
79
-
80
- /**
81
- * Convert RGB to hex color
82
- */
83
- export function rgbToHex(rgb: RGB): string {
84
- const toHex = (n: number) => {
85
- const hex = Math.round(Math.max(0, Math.min(255, n))).toString(16)
86
- return hex.length === 1 ? `0${hex}` : hex
87
- }
88
- return `#${toHex(rgb.r)}${toHex(rgb.g)}${toHex(rgb.b)}`
89
- }
90
-
91
- /**
92
- * Convert RGB to HSL
93
- */
94
- interface NormalizedRGB {
95
- r: number
96
- g: number
97
- b: number
98
- }
99
-
100
- function computeHue(norm: NormalizedRGB, max: number, d: number): number {
101
- const { r, g, b } = norm
102
- if (max === r) return ((g - b) / d + (g < b ? 6 : 0)) / 6
103
- if (max === g) return ((b - r) / d + 2) / 6
104
- return ((r - g) / d + 4) / 6
105
- }
106
-
107
- export function rgbToHsl(rgb: RGB): HSL {
108
- const norm: NormalizedRGB = { r: rgb.r / 255, g: rgb.g / 255, b: rgb.b / 255 }
109
-
110
- const max = Math.max(norm.r, norm.g, norm.b)
111
- const min = Math.min(norm.r, norm.g, norm.b)
112
- const l = (max + min) / 2
113
-
114
- if (max === min) return { h: 0, s: 0, l: l * 100 }
115
-
116
- const d = max - min
117
- const s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
118
- const h = computeHue(norm, max, d)
119
-
120
- return { h: h * 360, s: s * 100, l: l * 100 }
121
- }
122
-
123
- function normalizeT(t: number): number {
124
- if (t < 0) return t + 1
125
- if (t > 1) return t - 1
126
- return t
127
- }
128
-
129
- function hue2rgb(p: number, q: number, t: number): number {
130
- const tt = normalizeT(t)
131
- if (tt < 1 / 6) return p + (q - p) * 6 * tt
132
- if (tt < 1 / 2) return q
133
- if (tt < 2 / 3) return p + (q - p) * (2 / 3 - tt) * 6
134
- return p
135
- }
136
-
137
- /**
138
- * Convert HSL to RGB
139
- */
140
- export function hslToRgb(hsl: HSL): RGB {
141
- const h = hsl.h / 360
142
- const s = hsl.s / 100
143
- const l = hsl.l / 100
144
-
145
- if (s === 0) {
146
- const val = Math.round(l * 255)
147
- return { r: val, g: val, b: val }
148
- }
149
-
150
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s
151
- const p = 2 * l - q
152
-
153
- return {
154
- r: Math.round(hue2rgb(p, q, h + 1 / 3) * 255),
155
- g: Math.round(hue2rgb(p, q, h) * 255),
156
- b: Math.round(hue2rgb(p, q, h - 1 / 3) * 255)
157
- }
158
- }
159
-
160
- /**
161
- * Convert hex to HSL
162
- */
163
- export function hexToHsl(hex: string): HSL {
164
- return rgbToHsl(hexToRgb(hex))
165
- }
166
-
167
- /**
168
- * Convert HSL to hex
169
- */
170
- export function hslToHex(hsl: HSL): string {
171
- return rgbToHex(hslToRgb(hsl))
172
- }
173
-
174
- // =============================================================================
175
- // OKLCH Conversion Utilities
176
- // =============================================================================
177
-
178
- function srgbToLinear(c: number): number {
179
- const s = c / 255
180
- return s <= 0.04045 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4)
181
- }
182
-
183
- function linearToSrgb(c: number): number {
184
- const s = c <= 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - 0.055
185
- return Math.round(Math.max(0, Math.min(255, s * 255)))
186
- }
187
-
188
- // CSS Color Level 4 matrices (via XYZ D65) for round-trip precision.
189
- const SRGB_TO_XYZ: [number, number, number][] = [
190
- [0.4123907993, 0.3575843394, 0.1804807884],
191
- [0.2126390059, 0.7151686788, 0.0721923154],
192
- [0.0193308187, 0.1191947798, 0.9505321522]
193
- ]
194
- const XYZ_TO_LMS: [number, number, number][] = [
195
- [0.8189330101, 0.3618667424, -0.1288597137],
196
- [0.0329845436, 0.9293118715, 0.0361456387],
197
- [0.0482003018, 0.2643662691, 0.633851707]
198
- ]
199
- const LMS3_TO_OKLAB: [number, number, number][] = [
200
- [0.2104542553, 0.793617785, -0.0040720468],
201
- [1.9779984951, -2.428592205, 0.4505937099],
202
- [0.0259040371, 0.7827717662, -0.808675766]
203
- ]
204
- const OKLAB_TO_LMS3: [number, number, number][] = [
205
- [1.0, 0.3963377774, 0.2158037573],
206
- [1.0, -0.1055613458, -0.0638541728],
207
- [1.0, -0.0894841775, -1.291485548]
208
- ]
209
- const LMS_TO_XYZ: [number, number, number][] = [
210
- [1.2270138511, -0.5577999807, 0.281256149],
211
- [-0.0405801784, 1.1122568696, -0.0716766787],
212
- [-0.0763812845, -0.4214819784, 1.5861632204]
213
- ]
214
- const XYZ_TO_SRGB: [number, number, number][] = [
215
- [3.2409699419, -1.5373831776, -0.4986107603],
216
- [-0.9692436363, 1.8759675015, 0.0415550574],
217
- [0.0556300797, -0.2039769589, 1.0569715142]
218
- ]
219
-
220
- function matMul(m: [number, number, number][], v: number[]): number[] {
221
- return m.map((row) => row[0] * v[0] + row[1] * v[1] + row[2] * v[2])
222
- }
223
-
224
- /**
225
- * Convert RGB to OKLCH (via XYZ D65 for precision)
226
- */
227
- export function rgbToOklch(rgb: RGB): OKLCH {
228
- const lin = [srgbToLinear(rgb.r), srgbToLinear(rgb.g), srgbToLinear(rgb.b)]
229
- const xyz = matMul(SRGB_TO_XYZ, lin)
230
- const lms = matMul(XYZ_TO_LMS, xyz)
231
- const lms3 = [Math.cbrt(lms[0]), Math.cbrt(lms[1]), Math.cbrt(lms[2])]
232
- const [L, a, b] = matMul(LMS3_TO_OKLAB, lms3)
233
-
234
- const C = Math.sqrt(a * a + b * b)
235
- let H = (Math.atan2(b, a) * 180) / Math.PI
236
- if (H < 0) H += 360
237
-
238
- return { L, C, H }
239
- }
240
-
241
- /**
242
- * Convert OKLCH to RGB (via XYZ D65 for precision)
243
- */
244
- export function oklchToRgb(oklch: OKLCH): RGB {
245
- const hRad = (oklch.H * Math.PI) / 180
246
- const lab = [oklch.L, oklch.C * Math.cos(hRad), oklch.C * Math.sin(hRad)]
247
-
248
- const lms3 = matMul(OKLAB_TO_LMS3, lab)
249
- const lms = [lms3[0] ** 3, lms3[1] ** 3, lms3[2] ** 3]
250
- const xyz = matMul(LMS_TO_XYZ, lms)
251
- const lin = matMul(XYZ_TO_SRGB, xyz)
252
-
253
- return {
254
- r: linearToSrgb(lin[0]),
255
- g: linearToSrgb(lin[1]),
256
- b: linearToSrgb(lin[2])
257
- }
258
- }
259
-
260
- /**
261
- * Convert hex to OKLCH
262
- */
263
- export function hexToOklch(hex: string): OKLCH {
264
- return rgbToOklch(hexToRgb(hex))
265
- }
266
-
267
- /**
268
- * Convert OKLCH to hex
269
- */
270
- export function oklchToHex(oklch: OKLCH): string {
271
- return rgbToHex(oklchToRgb(oklch))
272
- }
273
-
274
- // =============================================================================
275
- // Shade Generation
276
- // =============================================================================
277
-
278
- /**
279
- * Generate Tailwind-like shades from a base hex color
280
- * The input color is used as the 500 shade
281
- */
282
- export function generateShades(hex: string): Shades {
283
- const hsl = hexToHsl(hex)
284
-
285
- // Shade configuration: [saturation multiplier, target lightness]
286
- // Lighter shades have higher lightness, lower saturation
287
- // Darker shades have lower lightness, slightly higher saturation
288
- const shadeConfig: Record<ShadeKey, [number, number]> = {
289
- '50': [0.9, 97],
290
- '100': [0.92, 94],
291
- '200': [0.94, 86],
292
- '300': [0.96, 75],
293
- '400': [0.98, 60],
294
- '500': [1.0, hsl.l], // Keep original lightness
295
- '600': [1.02, Math.max(hsl.l * 0.82, 25)],
296
- '700': [1.04, Math.max(hsl.l * 0.65, 20)],
297
- '800': [1.06, Math.max(hsl.l * 0.5, 15)],
298
- '900': [1.08, Math.max(hsl.l * 0.35, 12)],
299
- '950': [1.12, Math.max(hsl.l * 0.22, 8)]
300
- }
301
-
302
- const shades: Partial<Shades> = {}
303
-
304
- for (const [shade, [satMult, lightness]] of Object.entries(shadeConfig)) {
305
- const newHsl: HSL = {
306
- h: hsl.h,
307
- s: Math.min(100, hsl.s * satMult),
308
- l: lightness
309
- }
310
- shades[shade as ShadeKey] = hslToHex(newHsl)
311
- }
312
-
313
- return shades as Shades
314
- }
315
-
316
- /**
317
- * Check if a string is a valid hex color
318
- */
319
- export function isHexColor(value: string): boolean {
320
- return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value)
321
- }
322
-
323
- // =============================================================================
324
- // Tailwind Colors (Built-in Presets)
325
- // =============================================================================
326
-
327
- export const tailwindColors: Record<string, Shades> = {
328
- slate: {
329
- '50': '#f8fafc',
330
- '100': '#f1f5f9',
331
- '200': '#e2e8f0',
332
- '300': '#cbd5e1',
333
- '400': '#94a3b8',
334
- '500': '#64748b',
335
- '600': '#475569',
336
- '700': '#334155',
337
- '800': '#1e293b',
338
- '900': '#0f172a',
339
- '950': '#020617'
340
- },
341
- gray: {
342
- '50': '#f9fafb',
343
- '100': '#f3f4f6',
344
- '200': '#e5e7eb',
345
- '300': '#d1d5db',
346
- '400': '#9ca3af',
347
- '500': '#6b7280',
348
- '600': '#4b5563',
349
- '700': '#374151',
350
- '800': '#1f2937',
351
- '900': '#111827',
352
- '950': '#030712'
353
- },
354
- zinc: {
355
- '50': '#fafafa',
356
- '100': '#f4f4f5',
357
- '200': '#e4e4e7',
358
- '300': '#d4d4d8',
359
- '400': '#a1a1aa',
360
- '500': '#71717a',
361
- '600': '#52525b',
362
- '700': '#3f3f46',
363
- '800': '#27272a',
364
- '900': '#18181b',
365
- '950': '#09090b'
366
- },
367
- neutral: {
368
- '50': '#fafafa',
369
- '100': '#f5f5f5',
370
- '200': '#e5e5e5',
371
- '300': '#d4d4d4',
372
- '400': '#a3a3a3',
373
- '500': '#737373',
374
- '600': '#525252',
375
- '700': '#404040',
376
- '800': '#262626',
377
- '900': '#171717',
378
- '950': '#0a0a0a'
379
- },
380
- stone: {
381
- '50': '#fafaf9',
382
- '100': '#f5f5f4',
383
- '200': '#e7e5e4',
384
- '300': '#d6d3d1',
385
- '400': '#a8a29e',
386
- '500': '#78716c',
387
- '600': '#57534e',
388
- '700': '#44403c',
389
- '800': '#292524',
390
- '900': '#1c1917',
391
- '950': '#0c0a09'
392
- },
393
- red: {
394
- '50': '#fef2f2',
395
- '100': '#fee2e2',
396
- '200': '#fecaca',
397
- '300': '#fca5a5',
398
- '400': '#f87171',
399
- '500': '#ef4444',
400
- '600': '#dc2626',
401
- '700': '#b91c1c',
402
- '800': '#991b1b',
403
- '900': '#7f1d1d',
404
- '950': '#450a0a'
405
- },
406
- orange: {
407
- '50': '#fff7ed',
408
- '100': '#ffedd5',
409
- '200': '#fed7aa',
410
- '300': '#fdba74',
411
- '400': '#fb923c',
412
- '500': '#f97316',
413
- '600': '#ea580c',
414
- '700': '#c2410c',
415
- '800': '#9a3412',
416
- '900': '#7c2d12',
417
- '950': '#431407'
418
- },
419
- amber: {
420
- '50': '#fffbeb',
421
- '100': '#fef3c7',
422
- '200': '#fde68a',
423
- '300': '#fcd34d',
424
- '400': '#fbbf24',
425
- '500': '#f59e0b',
426
- '600': '#d97706',
427
- '700': '#b45309',
428
- '800': '#92400e',
429
- '900': '#78350f',
430
- '950': '#451a03'
431
- },
432
- yellow: {
433
- '50': '#fefce8',
434
- '100': '#fef9c3',
435
- '200': '#fef08a',
436
- '300': '#fde047',
437
- '400': '#facc15',
438
- '500': '#eab308',
439
- '600': '#ca8a04',
440
- '700': '#a16207',
441
- '800': '#854d0e',
442
- '900': '#713f12',
443
- '950': '#422006'
444
- },
445
- lime: {
446
- '50': '#f7fee7',
447
- '100': '#ecfccb',
448
- '200': '#d9f99d',
449
- '300': '#bef264',
450
- '400': '#a3e635',
451
- '500': '#84cc16',
452
- '600': '#65a30d',
453
- '700': '#4d7c0f',
454
- '800': '#3f6212',
455
- '900': '#365314',
456
- '950': '#1a2e05'
457
- },
458
- green: {
459
- '50': '#f0fdf4',
460
- '100': '#dcfce7',
461
- '200': '#bbf7d0',
462
- '300': '#86efac',
463
- '400': '#4ade80',
464
- '500': '#22c55e',
465
- '600': '#16a34a',
466
- '700': '#15803d',
467
- '800': '#166534',
468
- '900': '#14532d',
469
- '950': '#052e16'
470
- },
471
- emerald: {
472
- '50': '#ecfdf5',
473
- '100': '#d1fae5',
474
- '200': '#a7f3d0',
475
- '300': '#6ee7b7',
476
- '400': '#34d399',
477
- '500': '#10b981',
478
- '600': '#059669',
479
- '700': '#047857',
480
- '800': '#065f46',
481
- '900': '#064e3b',
482
- '950': '#022c22'
483
- },
484
- teal: {
485
- '50': '#f0fdfa',
486
- '100': '#ccfbf1',
487
- '200': '#99f6e4',
488
- '300': '#5eead4',
489
- '400': '#2dd4bf',
490
- '500': '#14b8a6',
491
- '600': '#0d9488',
492
- '700': '#0f766e',
493
- '800': '#115e59',
494
- '900': '#134e4a',
495
- '950': '#042f2e'
496
- },
497
- cyan: {
498
- '50': '#ecfeff',
499
- '100': '#cffafe',
500
- '200': '#a5f3fc',
501
- '300': '#67e8f9',
502
- '400': '#22d3ee',
503
- '500': '#06b6d4',
504
- '600': '#0891b2',
505
- '700': '#0e7490',
506
- '800': '#155e75',
507
- '900': '#164e63',
508
- '950': '#083344'
509
- },
510
- sky: {
511
- '50': '#f0f9ff',
512
- '100': '#e0f2fe',
513
- '200': '#bae6fd',
514
- '300': '#7dd3fc',
515
- '400': '#38bdf8',
516
- '500': '#0ea5e9',
517
- '600': '#0284c7',
518
- '700': '#0369a1',
519
- '800': '#075985',
520
- '900': '#0c4a6e',
521
- '950': '#082f49'
522
- },
523
- blue: {
524
- '50': '#eff6ff',
525
- '100': '#dbeafe',
526
- '200': '#bfdbfe',
527
- '300': '#93c5fd',
528
- '400': '#60a5fa',
529
- '500': '#3b82f6',
530
- '600': '#2563eb',
531
- '700': '#1d4ed8',
532
- '800': '#1e40af',
533
- '900': '#1e3a8a',
534
- '950': '#172554'
535
- },
536
- indigo: {
537
- '50': '#eef2ff',
538
- '100': '#e0e7ff',
539
- '200': '#c7d2fe',
540
- '300': '#a5b4fc',
541
- '400': '#818cf8',
542
- '500': '#6366f1',
543
- '600': '#4f46e5',
544
- '700': '#4338ca',
545
- '800': '#3730a3',
546
- '900': '#312e81',
547
- '950': '#1e1b4b'
548
- },
549
- violet: {
550
- '50': '#f5f3ff',
551
- '100': '#ede9fe',
552
- '200': '#ddd6fe',
553
- '300': '#c4b5fd',
554
- '400': '#a78bfa',
555
- '500': '#8b5cf6',
556
- '600': '#7c3aed',
557
- '700': '#6d28d9',
558
- '800': '#5b21b6',
559
- '900': '#4c1d95',
560
- '950': '#2e1065'
561
- },
562
- purple: {
563
- '50': '#faf5ff',
564
- '100': '#f3e8ff',
565
- '200': '#e9d5ff',
566
- '300': '#d8b4fe',
567
- '400': '#c084fc',
568
- '500': '#a855f7',
569
- '600': '#9333ea',
570
- '700': '#7e22ce',
571
- '800': '#6b21a8',
572
- '900': '#581c87',
573
- '950': '#3b0764'
574
- },
575
- fuchsia: {
576
- '50': '#fdf4ff',
577
- '100': '#fae8ff',
578
- '200': '#f5d0fe',
579
- '300': '#f0abfc',
580
- '400': '#e879f9',
581
- '500': '#d946ef',
582
- '600': '#c026d3',
583
- '700': '#a21caf',
584
- '800': '#86198f',
585
- '900': '#701a75',
586
- '950': '#4a044e'
587
- },
588
- pink: {
589
- '50': '#fdf2f8',
590
- '100': '#fce7f3',
591
- '200': '#fbcfe8',
592
- '300': '#f9a8d4',
593
- '400': '#f472b6',
594
- '500': '#ec4899',
595
- '600': '#db2777',
596
- '700': '#be185d',
597
- '800': '#9d174d',
598
- '900': '#831843',
599
- '950': '#500724'
600
- },
601
- rose: {
602
- '50': '#fff1f2',
603
- '100': '#ffe4e6',
604
- '200': '#fecdd3',
605
- '300': '#fda4af',
606
- '400': '#fb7185',
607
- '500': '#f43f5e',
608
- '600': '#e11d48',
609
- '700': '#be123c',
610
- '800': '#9f1239',
611
- '900': '#881337',
612
- '950': '#4c0519'
613
- }
614
- }
615
-
616
- /**
617
- * Get shades for a color (either Tailwind preset or custom hex)
618
- */
619
- export function getShades(color: string): Shades {
620
- if (isHexColor(color)) {
621
- return generateShades(color)
622
- }
623
- const preset = tailwindColors[color.toLowerCase()]
624
- if (preset) {
625
- return preset
626
- }
627
- throw new Error(`Unknown color: ${color}. Must be a hex color or Tailwind color name.`)
628
- }
629
-
630
- // =============================================================================
631
- // Runtime Palette Application
632
- // =============================================================================
633
-
634
- /**
635
- * Format a hex color as CSS variable components for the given color space.
636
- */
637
- function formatComponents(hex: string, colorSpace: ColorSpace): string {
638
- switch (colorSpace) {
639
- case 'hsl': {
640
- const hsl = hexToHsl(hex)
641
- return `${Math.round(hsl.h)} ${Math.round(hsl.s)}% ${Math.round(hsl.l)}%`
642
- }
643
- case 'oklch': {
644
- const oklch = hexToOklch(hex)
645
- const L = Math.round(oklch.L * 10000) / 10000
646
- const C = Math.round(oklch.C * 10000) / 10000
647
- const H = Math.round(oklch.H * 100) / 100
648
- return `${L} ${C} ${H}`
649
- }
650
- case 'rgb':
651
- default: {
652
- const rgb = hexToRgb(hex)
653
- return `${rgb.r},${rgb.g},${rgb.b}`
654
- }
655
- }
656
- }
657
-
658
- /**
659
- * Apply a color palette to the document root
660
- * Sets CSS variables for each color role and shade
661
- */
662
- export function applyPalette(
663
- mapping: Partial<Record<ColorRole, string>>,
664
- element: HTMLElement = document.documentElement,
665
- colorSpace: ColorSpace = 'rgb'
666
- ): void {
667
- for (const [role, color] of Object.entries(mapping)) {
668
- if (!color) continue
669
-
670
- const shades = getShades(color)
671
-
672
- for (const [shade, hex] of Object.entries(shades)) {
673
- element.style.setProperty(`--color-${role}-${shade}`, formatComponents(hex, colorSpace))
674
- }
675
-
676
- // Also set the default (500) as the base color variable
677
- element.style.setProperty(`--color-${role}`, formatComponents(shades['500'], colorSpace))
678
- }
679
- }
680
-
681
- const DEFAULT_PALETTE_ROLES: ColorRole[] = [
682
- 'primary',
683
- 'secondary',
684
- 'accent',
685
- 'surface',
686
- 'success',
687
- 'warning',
688
- 'danger',
689
- 'info'
690
- ]
691
-
692
- const ALL_SHADE_KEYS: ShadeKey[] = [
693
- '50',
694
- '100',
695
- '200',
696
- '300',
697
- '400',
698
- '500',
699
- '600',
700
- '700',
701
- '800',
702
- '900',
703
- '950'
704
- ]
705
-
706
- /**
707
- * Remove custom palette CSS variables (reset to theme defaults)
708
- */
709
- export function resetPalette(
710
- roles: ColorRole[] = DEFAULT_PALETTE_ROLES,
711
- element: HTMLElement = document.documentElement
712
- ): void {
713
- for (const role of roles) {
714
- element.style.removeProperty(`--color-${role}`)
715
- for (const shade of ALL_SHADE_KEYS) {
716
- element.style.removeProperty(`--color-${role}-${shade}`)
717
- }
718
- }
719
- }
720
-
721
- /**
722
- * Save palette to localStorage. SSR-safe.
723
- */
724
- export function savePalette(key: string, mapping: Partial<Record<ColorRole, string>>): void {
725
- if (typeof localStorage === 'undefined') return
726
- localStorage.setItem(key, JSON.stringify(mapping))
727
- }
728
-
729
- /**
730
- * Load palette from localStorage. SSR-safe.
731
- */
732
- export function loadPalette(key: string): Partial<Record<ColorRole, string>> | null {
733
- if (typeof localStorage === 'undefined') return null
734
- const stored = localStorage.getItem(key)
735
- if (stored) {
736
- try {
737
- return JSON.parse(stored)
738
- } catch {
739
- return null
740
- }
741
- }
742
- return null
743
- }
744
-
745
- /**
746
- * Get list of available Tailwind color names
747
- */
748
- export function getTailwindColorNames(): string[] {
749
- return Object.keys(tailwindColors)
750
- }