@@ -3,39 +3,85 @@ export function generateHarmonizedColors(baseHue: number, isDarkMode: boolean =
3
3
const analogous1 = ( baseHue + 30 ) % 360 ;
4
4
const analogous2 = ( baseHue - 30 + 360 ) % 360 ;
5
5
6
- // Calculate harmonized saturations based on primary color
7
- const secondarySatLight = 15 + Math . random ( ) * 20 ; // 15-35%
8
- const accentSatLight = 15 + Math . random ( ) * 20 ; // 15-35%
9
- const mutedSatLight = 15 + Math . random ( ) * 20 ; // 15-35%
10
-
11
- const secondarySatDark = 75 + Math . random ( ) * 20 ; // 75-95%
12
- const accentSatDark = 75 + Math . random ( ) * 20 ; // 75-95%
13
- const mutedSatDark = 75 + Math . random ( ) * 20 ; // 75-95%
14
-
15
- // Random lightness values
16
- const lightModeLightness = 80 + Math . random ( ) * 20 ; // 80-100%
17
- const darkModeLightness = Math . random ( ) * 20 ; // 0-20%
18
-
19
- // Generate light variants
6
+ // Calculate harmonized saturations with randomization
7
+ const primarySat = 60 + Math . random ( ) * 20 ; // 60-80%
8
+ const primaryLightLight = 40 + Math . random ( ) * 40 ; // 40-90%
9
+ const primaryDarkLight = 30 + Math . random ( ) * 30 ; // 30-60%
10
+ const primary = `${ baseHue . toFixed ( 2 ) } ${ primarySat . toFixed ( 2 ) } % ${ primaryLightLight . toFixed ( 2 ) } %` ;
11
+ const primaryDark = `${ baseHue . toFixed ( 2 ) } ${ primarySat . toFixed ( 2 ) } % ${ primaryDarkLight . toFixed ( 2 ) } %` ;
12
+
13
+ // Secondary colors with dynamic saturation
14
+ const secondarySatLight = Math . max ( 10 , Math . random ( ) * primarySat * 0.4 ) ; // 10-32%
15
+ const secondarySatDark = Math . min ( 90 , primarySat + Math . random ( ) * 20 ) ; // primarySat-(primarySat+20)%
16
+
17
+ // Accent colors with slightly higher saturation
18
+ const accentSatLight = Math . max ( 15 , secondarySatLight * ( 1 + Math . random ( ) * 0.5 ) ) ;
19
+ const accentSatDark = Math . min ( 95 , secondarySatDark * ( 1 + Math . random ( ) * 0.3 ) ) ;
20
+
21
+ // Dynamic lightness values
22
+ const lightModeLightness = 90 + Math . random ( ) * 10 ; // 90-100%
23
+ const darkModeLightness = Math . random ( ) * 15 ; // 0-15%
24
+
25
+ // Generate light mode colors
20
26
const secondaryLight = `${ analogous1 . toFixed ( 2 ) } ${ secondarySatLight . toFixed ( 2 ) } % ${ lightModeLightness . toFixed ( 2 ) } %` ;
21
27
const accentLight = `${ analogous2 . toFixed ( 2 ) } ${ accentSatLight . toFixed ( 2 ) } % ${ lightModeLightness . toFixed ( 2 ) } %` ;
22
- const mutedLight = `${ analogous2 . toFixed ( 2 ) } ${ mutedSatLight . toFixed ( 2 ) } % ${ lightModeLightness . toFixed ( 2 ) } %` ;
23
28
24
- // Generate dark variants (same hue, different saturation and lightness)
29
+ // Generate dark mode colors
25
30
const secondaryDark = `${ analogous1 . toFixed ( 2 ) } ${ secondarySatDark . toFixed ( 2 ) } % ${ darkModeLightness . toFixed ( 2 ) } %` ;
26
31
const accentDark = `${ analogous2 . toFixed ( 2 ) } ${ accentSatDark . toFixed ( 2 ) } % ${ darkModeLightness . toFixed ( 2 ) } %` ;
27
- const mutedDark = `${ analogous2 . toFixed ( 2 ) } ${ mutedSatDark . toFixed ( 2 ) } % ${ darkModeLightness . toFixed ( 2 ) } %` ;
28
32
29
- return {
30
- primary : generateRandomHSL ( "primary" ) ,
33
+ // Muted colors based on secondary
34
+ const mutedSatLight = secondarySatLight * ( 0.5 + Math . random ( ) * 0.3 ) ; // 50-80% of secondary saturation
35
+ const mutedSatDark = secondarySatDark * ( 0.4 + Math . random ( ) * 0.3 ) ; // 40-70% of secondary saturation
36
+ const mutedLightLight = lightModeLightness * ( 0.9 + Math . random ( ) * 0.1 ) ; // 90-100% of light mode lightness
37
+ const mutedDarkLight = darkModeLightness * ( 1.2 + Math . random ( ) * 0.3 ) ; // 120-150% of dark mode lightness
38
+
39
+ const mutedLight = `${ analogous1 . toFixed ( 2 ) } ${ mutedSatLight . toFixed ( 2 ) } % ${ mutedLightLight . toFixed ( 2 ) } %` ;
40
+ const mutedDark = `${ analogous1 . toFixed ( 2 ) } ${ mutedSatDark . toFixed ( 2 ) } % ${ mutedDarkLight . toFixed ( 2 ) } %` ;
41
+
42
+ // Border and input colors based on secondary with reduced saturation
43
+ const borderSatLight = secondarySatLight * ( 0.7 + Math . random ( ) * 0.2 ) ;
44
+ const borderSatDark = secondarySatDark * ( 0.6 + Math . random ( ) * 0.2 ) ;
45
+ const borderLight = `${ analogous1 . toFixed ( 2 ) } ${ borderSatLight . toFixed ( 2 ) } % ${ ( lightModeLightness * 0.9 ) . toFixed ( 2 ) } %` ;
46
+ const borderDark = `${ analogous1 . toFixed ( 2 ) } ${ borderSatDark . toFixed ( 2 ) } % ${ ( darkModeLightness * 1.2 ) . toFixed ( 2 ) } %` ;
47
+
48
+ // Destructive color with random variation in the red range
49
+ const destructiveHue = Math . random ( ) * 10 ; // 0-10 (red range)
50
+ const destructiveSat = 70 + Math . random ( ) * 20 ; // 70-90%
51
+ const destructiveLight = 45 + Math . random ( ) * 25 ; // 45-70%
52
+ const destructive = `${ destructiveHue . toFixed ( 2 ) } ${ destructiveSat . toFixed ( 2 ) } % ${ destructiveLight . toFixed ( 2 ) } %` ;
53
+
54
+ // Calculate foreground colors dynamically
55
+ const colors : ColorConfig = {
56
+ // Base colors
57
+ primary,
58
+ "primary-dark" : primaryDark ,
31
59
secondary : secondaryLight ,
32
60
"secondary-dark" : secondaryDark ,
33
61
accent : accentLight ,
34
62
"accent-dark" : accentDark ,
35
- destructive : generateRandomHSL ( "destructive" ) ,
36
- muted : mutedDark ,
37
- "muted-dark" : "0 0% 0%"
63
+ destructive,
64
+ muted : mutedLight ,
65
+ "muted-dark" : mutedDark ,
66
+ border : borderLight ,
67
+ "border-dark" : borderDark ,
68
+ input : borderLight ,
69
+ "input-dark" : borderDark ,
70
+ ring : primary ,
71
+
72
+ // Foreground colors
73
+ "primary-foreground" : calculateForegroundColor ( primary ) ,
74
+ "primary-dark-foreground" : calculateForegroundColor ( primary ) ,
75
+ "secondary-foreground" : calculateForegroundColor ( secondaryLight ) ,
76
+ "secondary-dark-foreground" : calculateForegroundColor ( secondaryLight ) ,
77
+ "accent-foreground" : calculateForegroundColor ( accentLight ) ,
78
+ "accent-dark-foreground" : calculateForegroundColor ( accentLight ) ,
79
+ "destructive-foreground" : calculateForegroundColor ( destructive ) ,
80
+ "muted-foreground" : `${ analogous1 . toFixed ( 2 ) } ${ ( mutedSatLight * 1.5 ) . toFixed ( 2 ) } % ${ ( lightModeLightness * 0.4 ) . toFixed ( 2 ) } %` ,
81
+ "muted-dark-foreground" : `${ analogous1 . toFixed ( 2 ) } ${ ( mutedSatDark * 1.2 ) . toFixed ( 2 ) } % ${ Math . min ( 98 , mutedDarkLight * 4 ) . toFixed ( 2 ) } %`
38
82
} ;
83
+
84
+ return colors ;
39
85
}
40
86
41
87
export function generateRandomHSL ( type : keyof ColorConfig = "primary" ) : string {
@@ -58,6 +104,8 @@ export function generateRandomColors(isDarkMode: boolean = false): ColorConfig {
58
104
}
59
105
60
106
export function generateBackground ( primary : string , isDarkMode : boolean ) : string {
107
+ if ( ! primary ) return isDarkMode ? "20 14.3% 4.1%" : "0 0% 100%" ;
108
+
61
109
const [ h , s , l ] = primary . split ( " " ) . map ( parseFloat ) ;
62
110
if ( isNaN ( h ) || isNaN ( s ) || isNaN ( l ) ) {
63
111
return isDarkMode ? "20 14.3% 4.1%" : "0 0% 100%" ;
@@ -69,6 +117,8 @@ export function generateBackground(primary: string, isDarkMode: boolean): string
69
117
}
70
118
71
119
export function generateCard ( primary : string , isDarkMode : boolean ) : string {
120
+ if ( ! primary ) return isDarkMode ? "24 9.8% 10%" : "0 0% 100%" ;
121
+
72
122
const [ h , s , l ] = primary . split ( " " ) . map ( parseFloat ) ;
73
123
if ( isNaN ( h ) || isNaN ( s ) || isNaN ( l ) ) {
74
124
return isDarkMode ? "24 9.8% 10%" : "0 0% 100%" ;
@@ -80,6 +130,8 @@ export function generateCard(primary: string, isDarkMode: boolean): string {
80
130
}
81
131
82
132
export function generatePopover ( primary : string , isDarkMode : boolean ) : string {
133
+ if ( ! primary ) return isDarkMode ? "0 0% 9%" : "0 0% 100%" ;
134
+
83
135
const [ h , s , l ] = primary . split ( " " ) . map ( parseFloat ) ;
84
136
if ( isNaN ( h ) || isNaN ( s ) || isNaN ( l ) ) {
85
137
return isDarkMode ? "0 0% 9%" : "0 0% 100%" ;
@@ -91,21 +143,33 @@ export function generatePopover(primary: string, isDarkMode: boolean): string {
91
143
}
92
144
93
145
export function generateBorder ( primary : string , isDarkMode : boolean ) : string {
146
+ if ( ! primary ) return isDarkMode ? "240 3.7% 15.9%" : "240 5.9% 90%" ;
147
+
94
148
const [ h , s ] = primary . split ( " " ) . map ( parseFloat ) ;
149
+ if ( isNaN ( h ) || isNaN ( s ) ) {
150
+ return isDarkMode ? "240 3.7% 15.9%" : "240 5.9% 90%" ;
151
+ }
152
+
95
153
return isDarkMode
96
154
? `${ h } ${ Math . max ( s * 0.15 , 3.7 ) . toFixed ( 1 ) } % 15.9%`
97
155
: `${ h } ${ Math . max ( s * 0.15 , 5.9 ) . toFixed ( 1 ) } % 90%` ;
98
156
}
99
157
100
158
export function generateInput ( primary : string , isDarkMode : boolean ) : string {
159
+ if ( ! primary ) return isDarkMode ? "240 3.7% 15.9%" : "240 5.9% 90%" ;
160
+
101
161
const [ h , s ] = primary . split ( " " ) . map ( parseFloat ) ;
162
+ if ( isNaN ( h ) || isNaN ( s ) ) {
163
+ return isDarkMode ? "240 3.7% 15.9%" : "240 5.9% 90%" ;
164
+ }
165
+
102
166
return isDarkMode
103
167
? `${ h } ${ Math . max ( s * 0.15 , 3.7 ) . toFixed ( 1 ) } % 15.9%`
104
168
: `${ h } ${ Math . max ( s * 0.15 , 5.9 ) . toFixed ( 1 ) } % 90%` ;
105
169
}
106
170
107
171
export function generateRing ( primary : string ) : string {
108
- return primary ;
172
+ return primary || "346.8 77.2% 49.8%" ;
109
173
}
110
174
111
175
export function hslToHex ( h : number , s : number , l : number ) : string {
@@ -156,16 +220,21 @@ export function hexToHSL(hex: string): string {
156
220
}
157
221
158
222
export function isValidDestructiveColor ( hslString : string ) : boolean {
223
+ if ( ! hslString ) return false ;
159
224
const [ h ] = hslString . split ( " " ) . map ( v => parseFloat ( v ) ) ;
225
+ if ( isNaN ( h ) ) return false ;
160
226
return ( h >= 350 || h <= 10 ) ;
161
227
}
162
228
163
229
export function calculateForegroundColor ( hslString : string ) : string {
230
+ if ( ! hslString ) return "0 0% 100%" ;
164
231
const [ , , l ] = hslString . split ( " " ) . map ( v => parseFloat ( v ) ) ;
165
- return l > 60 ? "240 10% 3.9%" : "0 0% 98%" ;
232
+ return l > 52 ? "240 10% 3.9%" : "0 0% 98%" ;
166
233
}
167
234
168
235
export function parseHSLString ( input : string ) : string | null {
236
+ if ( ! input ) return null ;
237
+
169
238
const cleaned = input . toLowerCase ( ) . replace ( / \s + / g, '' ) ;
170
239
const match = cleaned . match ( / ^ ( \d { 1 , 3 } ) [ , \s ] ( \d { 1 , 3 } ) % [ , \s ] ( \d { 1 , 3 } ) % $ / ) ;
171
240
0 commit comments