@rokkit/ui 1.3.4 → 1.3.6
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/{src → dist}/components/List.svelte +12 -6
- package/dist/components/index.js +63 -0
- package/dist/index.js +8 -0
- package/dist/markdown-plugin.js +1 -0
- package/dist/types/alert-list.js +7 -0
- package/dist/types/avatar.js +4 -0
- package/dist/types/badge.js +4 -0
- package/dist/types/breadcrumbs.js +4 -0
- package/dist/types/button.js +7 -0
- package/dist/types/card.js +4 -0
- package/dist/types/carousel.js +4 -0
- package/dist/types/chat.js +1 -0
- package/dist/types/code.js +12 -0
- package/dist/types/connector.js +8 -0
- package/dist/types/divider.js +4 -0
- package/dist/types/floating-action.js +12 -0
- package/dist/types/floating-navigation.js +1 -0
- package/dist/types/frame.js +8 -0
- package/dist/types/index.js +93 -0
- package/dist/types/item-content.js +8 -0
- package/dist/types/item-switch.js +10 -0
- package/dist/types/item-toggle.js +12 -0
- package/dist/types/list.js +36 -0
- package/dist/types/lock-mode.js +1 -0
- package/dist/types/menu.js +25 -0
- package/dist/types/message.js +4 -0
- package/dist/types/nav-content.js +4 -0
- package/dist/types/palette.js +54 -0
- package/dist/types/pill.js +4 -0
- package/dist/types/progress-bar.js +4 -0
- package/dist/types/range.js +7 -0
- package/dist/types/rating.js +4 -0
- package/dist/types/responsive-grid.js +4 -0
- package/dist/types/reveal.js +4 -0
- package/dist/types/search-filter.js +7 -0
- package/dist/types/select.js +21 -0
- package/dist/types/shine.js +7 -0
- package/dist/types/stack.js +4 -0
- package/dist/types/status-list.js +7 -0
- package/dist/types/stepper.js +4 -0
- package/dist/types/switch.js +7 -0
- package/dist/types/table.js +23 -0
- package/dist/types/tabs.js +7 -0
- package/dist/types/tilt.js +4 -0
- package/dist/types/timeline.js +14 -0
- package/dist/types/toggle.js +7 -0
- package/dist/types/toolbar.js +20 -0
- package/dist/types/tooltip.js +1 -0
- package/dist/types/tree-table.js +10 -0
- package/dist/types/tree.js +94 -0
- package/dist/types/upload-file-status.js +8 -0
- package/dist/types/upload-progress.js +8 -0
- package/dist/types/upload-target.js +7 -0
- package/dist/utils/palette.js +641 -0
- package/dist/utils/relative-time.js +17 -0
- package/dist/utils/shiki.js +118 -0
- package/package.json +13 -14
- package/src/components/index.ts +0 -63
- package/src/index.ts +0 -75
- package/src/markdown-plugin.ts +0 -12
- package/src/types/alert-list.ts +0 -28
- package/src/types/avatar.ts +0 -41
- package/src/types/badge.ts +0 -37
- package/src/types/breadcrumbs.ts +0 -43
- package/src/types/button.ts +0 -92
- package/src/types/card.ts +0 -38
- package/src/types/carousel.ts +0 -49
- package/src/types/chat.ts +0 -81
- package/src/types/code.ts +0 -147
- package/src/types/connector.ts +0 -22
- package/src/types/divider.ts +0 -20
- package/src/types/floating-action.ts +0 -123
- package/src/types/floating-navigation.ts +0 -80
- package/src/types/frame.ts +0 -29
- package/src/types/index.ts +0 -137
- package/src/types/item-content.ts +0 -26
- package/src/types/item-switch.ts +0 -34
- package/src/types/item-toggle.ts +0 -52
- package/src/types/list.ts +0 -200
- package/src/types/lock-mode.ts +0 -16
- package/src/types/menu.ts +0 -95
- package/src/types/message.ts +0 -54
- package/src/types/nav-content.ts +0 -31
- package/src/types/palette.ts +0 -160
- package/src/types/pill.ts +0 -43
- package/src/types/progress-bar.ts +0 -19
- package/src/types/range.ts +0 -51
- package/src/types/rating.ts +0 -43
- package/src/types/responsive-grid.ts +0 -25
- package/src/types/reveal.ts +0 -49
- package/src/types/search-filter.ts +0 -67
- package/src/types/select.ts +0 -176
- package/src/types/shine.ts +0 -37
- package/src/types/stack.ts +0 -40
- package/src/types/status-list.ts +0 -42
- package/src/types/stepper.ts +0 -74
- package/src/types/switch.ts +0 -68
- package/src/types/table.ts +0 -219
- package/src/types/tabs.ts +0 -103
- package/src/types/tilt.ts +0 -28
- package/src/types/timeline.ts +0 -53
- package/src/types/toggle.ts +0 -81
- package/src/types/toolbar.ts +0 -167
- package/src/types/tooltip.ts +0 -21
- package/src/types/tree-table.ts +0 -103
- package/src/types/tree.ts +0 -302
- package/src/types/upload-file-status.ts +0 -45
- package/src/types/upload-progress.ts +0 -111
- package/src/types/upload-target.ts +0 -71
- package/src/utils/palette.ts +0 -750
- package/src/utils/relative-time.ts +0 -12
- package/src/utils/shiki.ts +0 -137
- /package/{src → dist}/MarkdownRenderer.svelte +0 -0
- /package/{src → dist}/components/AlertList.svelte +0 -0
- /package/{src → dist}/components/Avatar.svelte +0 -0
- /package/{src → dist}/components/Badge.svelte +0 -0
- /package/{src → dist}/components/BreadCrumbs.svelte +0 -0
- /package/{src → dist}/components/Button.svelte +0 -0
- /package/{src → dist}/components/ButtonGroup.svelte +0 -0
- /package/{src → dist}/components/Card.svelte +0 -0
- /package/{src → dist}/components/Carousel.svelte +0 -0
- /package/{src → dist}/components/ChatComposer.svelte +0 -0
- /package/{src → dist}/components/ChatHistory.svelte +0 -0
- /package/{src → dist}/components/ChatMessage.svelte +0 -0
- /package/{src → dist}/components/ChatShell.svelte +0 -0
- /package/{src → dist}/components/ChatTimeline.svelte +0 -0
- /package/{src → dist}/components/Code.svelte +0 -0
- /package/{src → dist}/components/CodeBlock.svelte +0 -0
- /package/{src → dist}/components/CodeGroup.svelte +0 -0
- /package/{src → dist}/components/CommandPalette.svelte +0 -0
- /package/{src → dist}/components/Connector.svelte +0 -0
- /package/{src → dist}/components/Divider.svelte +0 -0
- /package/{src → dist}/components/Dropdown.svelte +0 -0
- /package/{src → dist}/components/FloatingAction.svelte +0 -0
- /package/{src → dist}/components/FloatingNavigation.svelte +0 -0
- /package/{src → dist}/components/Frame.svelte +0 -0
- /package/{src → dist}/components/Grid.svelte +0 -0
- /package/{src → dist}/components/ItemContent.svelte +0 -0
- /package/{src → dist}/components/ItemSwitch.svelte +0 -0
- /package/{src → dist}/components/ItemToggle.svelte +0 -0
- /package/{src → dist}/components/LazyTree.svelte +0 -0
- /package/{src → dist}/components/LockMode.svelte +0 -0
- /package/{src → dist}/components/Menu.svelte +0 -0
- /package/{src → dist}/components/Message.svelte +0 -0
- /package/{src → dist}/components/MultiSelect.svelte +0 -0
- /package/{src → dist}/components/NavContent.svelte +0 -0
- /package/{src → dist}/components/PaletteManager.svelte +0 -0
- /package/{src → dist}/components/Pill.svelte +0 -0
- /package/{src → dist}/components/ProgressBar.svelte +0 -0
- /package/{src → dist}/components/Range.svelte +0 -0
- /package/{src → dist}/components/Rating.svelte +0 -0
- /package/{src → dist}/components/ResponsiveGrid.svelte +0 -0
- /package/{src → dist}/components/Reveal.svelte +0 -0
- /package/{src → dist}/components/SearchFilter.svelte +0 -0
- /package/{src → dist}/components/Select.svelte +0 -0
- /package/{src → dist}/components/Shine.svelte +0 -0
- /package/{src → dist}/components/Stack.svelte +0 -0
- /package/{src → dist}/components/StatusList.svelte +0 -0
- /package/{src → dist}/components/Stepper.svelte +0 -0
- /package/{src → dist}/components/Swatch.svelte +0 -0
- /package/{src → dist}/components/Switch.svelte +0 -0
- /package/{src → dist}/components/Table.svelte +0 -0
- /package/{src → dist}/components/Tabs.svelte +0 -0
- /package/{src → dist}/components/Tilt.svelte +0 -0
- /package/{src → dist}/components/Timeline.svelte +0 -0
- /package/{src → dist}/components/Toggle.svelte +0 -0
- /package/{src → dist}/components/Toolbar.svelte +0 -0
- /package/{src → dist}/components/ToolbarGroup.svelte +0 -0
- /package/{src → dist}/components/Tooltip.svelte +0 -0
- /package/{src → dist}/components/Tree.svelte +0 -0
- /package/{src → dist}/components/TreeTable.svelte +0 -0
- /package/{src → dist}/components/UploadFileStatus.svelte +0 -0
- /package/{src → dist}/components/UploadProgress.svelte +0 -0
- /package/{src → dist}/components/UploadTarget.svelte +0 -0
- /package/{src → dist}/utils/upload.js +0 -0
package/src/utils/palette.ts
DELETED
|
@@ -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
|
-
}
|