@react-navigation/elements 3.0.0-alpha.4 → 3.0.0-alpha.40

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 (212) hide show
  1. package/lib/module/ActivityView.js +90 -0
  2. package/lib/module/ActivityView.js.map +1 -0
  3. package/lib/module/ActivityView.native.js +67 -0
  4. package/lib/module/ActivityView.native.js.map +1 -0
  5. package/lib/module/Badge.js +5 -4
  6. package/lib/module/Badge.js.map +1 -1
  7. package/lib/module/BlurEffectBackground.js +3 -3
  8. package/lib/module/BlurEffectBackground.js.map +1 -1
  9. package/lib/module/Button.js +96 -14
  10. package/lib/module/Button.js.map +1 -1
  11. package/lib/module/Color.js +565 -4
  12. package/lib/module/Color.js.map +1 -1
  13. package/lib/module/Container.js +11 -1
  14. package/lib/module/Container.js.map +1 -1
  15. package/lib/module/Header/Header.js +29 -17
  16. package/lib/module/Header/Header.js.map +1 -1
  17. package/lib/module/Header/HeaderBackButton.js +27 -13
  18. package/lib/module/Header/HeaderBackButton.js.map +1 -1
  19. package/lib/module/Header/HeaderButton.js +4 -5
  20. package/lib/module/Header/HeaderButton.js.map +1 -1
  21. package/lib/module/Header/HeaderButtonBackground.js +1 -2
  22. package/lib/module/Header/HeaderButtonBackground.js.map +1 -1
  23. package/lib/module/Header/HeaderIcon.js +14 -17
  24. package/lib/module/Header/HeaderIcon.js.map +1 -1
  25. package/lib/module/Header/HeaderSearchBar.js +35 -25
  26. package/lib/module/Header/HeaderSearchBar.js.map +1 -1
  27. package/lib/module/Header/getHeaderTitle.js.map +1 -1
  28. package/lib/module/Header/useHeaderHeight.js +1 -1
  29. package/lib/module/Header/useHeaderHeight.js.map +1 -1
  30. package/lib/module/Label/getLabel.js.map +1 -1
  31. package/lib/module/LiquidGlassView.ios.js +1 -2
  32. package/lib/module/LiquidGlassView.ios.js.map +1 -1
  33. package/lib/module/LiquidGlassView.js +0 -4
  34. package/lib/module/LiquidGlassView.js.map +1 -1
  35. package/lib/module/PlatformColor.js +1 -0
  36. package/lib/module/PlatformColor.js.map +1 -1
  37. package/lib/module/PlatformColor.native.js +1 -1
  38. package/lib/module/PlatformColor.native.js.map +1 -1
  39. package/lib/module/PlatformIcon.js +46 -0
  40. package/lib/module/PlatformIcon.js.map +1 -0
  41. package/lib/module/PlatformPressable.js +56 -14
  42. package/lib/module/PlatformPressable.js.map +1 -1
  43. package/lib/module/SafeAreaProviderCompat.js +1 -1
  44. package/lib/module/SafeAreaProviderCompat.js.map +1 -1
  45. package/lib/module/Screen.js +31 -18
  46. package/lib/module/Screen.js.map +1 -1
  47. package/lib/module/index.js +2 -7
  48. package/lib/module/index.js.map +1 -1
  49. package/lib/module/internal.js +1 -1
  50. package/lib/module/internal.js.map +1 -1
  51. package/lib/module/useFrameSize.js +6 -2
  52. package/lib/module/useFrameSize.js.map +1 -1
  53. package/lib/typescript/src/ActivityView.d.ts +31 -0
  54. package/lib/typescript/src/ActivityView.d.ts.map +1 -0
  55. package/lib/typescript/src/ActivityView.native.d.ts +3 -0
  56. package/lib/typescript/src/ActivityView.native.d.ts.map +1 -0
  57. package/lib/typescript/src/Badge.d.ts +6 -5
  58. package/lib/typescript/src/Badge.d.ts.map +1 -1
  59. package/lib/typescript/src/BlurEffectBackground.d.ts +2 -2
  60. package/lib/typescript/src/BlurEffectBackground.d.ts.map +1 -1
  61. package/lib/typescript/src/Button.d.ts +54 -6
  62. package/lib/typescript/src/Button.d.ts.map +1 -1
  63. package/lib/typescript/src/Color.d.ts +4 -4
  64. package/lib/typescript/src/Color.d.ts.map +1 -1
  65. package/lib/typescript/src/Container.d.ts +7 -4
  66. package/lib/typescript/src/Container.d.ts.map +1 -1
  67. package/lib/typescript/src/Header/Header.d.ts +5 -4
  68. package/lib/typescript/src/Header/Header.d.ts.map +1 -1
  69. package/lib/typescript/src/Header/HeaderBackButton.d.ts +3 -2
  70. package/lib/typescript/src/Header/HeaderBackButton.d.ts.map +1 -1
  71. package/lib/typescript/src/Header/HeaderBackground.d.ts +5 -5
  72. package/lib/typescript/src/Header/HeaderBackground.d.ts.map +1 -1
  73. package/lib/typescript/src/Header/HeaderButton.d.ts +7 -2
  74. package/lib/typescript/src/Header/HeaderButton.d.ts.map +1 -1
  75. package/lib/typescript/src/Header/HeaderButtonBackground.d.ts +1 -1
  76. package/lib/typescript/src/Header/HeaderButtonBackground.d.ts.map +1 -1
  77. package/lib/typescript/src/Header/HeaderIcon.d.ts +9 -4
  78. package/lib/typescript/src/Header/HeaderIcon.d.ts.map +1 -1
  79. package/lib/typescript/src/Header/HeaderSearchBar.d.ts +10 -7
  80. package/lib/typescript/src/Header/HeaderSearchBar.d.ts.map +1 -1
  81. package/lib/typescript/src/Header/HeaderTitle.d.ts +4 -4
  82. package/lib/typescript/src/Header/HeaderTitle.d.ts.map +1 -1
  83. package/lib/typescript/src/Header/getHeaderTitle.d.ts +2 -2
  84. package/lib/typescript/src/Header/getHeaderTitle.d.ts.map +1 -1
  85. package/lib/typescript/src/Label/Label.d.ts +1 -1
  86. package/lib/typescript/src/Label/Label.d.ts.map +1 -1
  87. package/lib/typescript/src/Label/getLabel.d.ts +2 -2
  88. package/lib/typescript/src/Label/getLabel.d.ts.map +1 -1
  89. package/lib/typescript/src/LiquidGlassView.d.ts +7 -5
  90. package/lib/typescript/src/LiquidGlassView.d.ts.map +1 -1
  91. package/lib/typescript/src/LiquidGlassView.ios.d.ts +4 -2
  92. package/lib/typescript/src/LiquidGlassView.ios.d.ts.map +1 -1
  93. package/lib/typescript/src/MissingIcon.d.ts +1 -1
  94. package/lib/typescript/src/MissingIcon.d.ts.map +1 -1
  95. package/lib/typescript/src/PlatformColor.d.ts +1 -0
  96. package/lib/typescript/src/PlatformColor.d.ts.map +1 -1
  97. package/lib/typescript/src/PlatformColor.native.d.ts +1 -1
  98. package/lib/typescript/src/PlatformColor.native.d.ts.map +1 -1
  99. package/lib/typescript/src/PlatformIcon.d.ts +10 -0
  100. package/lib/typescript/src/PlatformIcon.d.ts.map +1 -0
  101. package/lib/typescript/src/PlatformPressable.d.ts +12 -15
  102. package/lib/typescript/src/PlatformPressable.d.ts.map +1 -1
  103. package/lib/typescript/src/SafeAreaProviderCompat.d.ts +1 -1
  104. package/lib/typescript/src/SafeAreaProviderCompat.d.ts.map +1 -1
  105. package/lib/typescript/src/Screen.d.ts +7 -7
  106. package/lib/typescript/src/Screen.d.ts.map +1 -1
  107. package/lib/typescript/src/Text.d.ts +1 -1
  108. package/lib/typescript/src/Text.d.ts.map +1 -1
  109. package/lib/typescript/src/index.d.ts +21 -21
  110. package/lib/typescript/src/index.d.ts.map +1 -1
  111. package/lib/typescript/src/internal.d.ts +6 -6
  112. package/lib/typescript/src/internal.d.ts.map +1 -1
  113. package/lib/typescript/src/types.d.ts +120 -70
  114. package/lib/typescript/src/types.d.ts.map +1 -1
  115. package/lib/typescript/src/useFrameSize.d.ts +1 -1
  116. package/lib/typescript/src/useFrameSize.d.ts.map +1 -1
  117. package/package.json +23 -22
  118. package/src/ActivityView.native.tsx +90 -0
  119. package/src/ActivityView.tsx +129 -0
  120. package/src/Badge.tsx +10 -8
  121. package/src/BlurEffectBackground.tsx +3 -3
  122. package/src/Button.tsx +173 -28
  123. package/src/Color.tsx +755 -8
  124. package/src/Container.tsx +20 -4
  125. package/src/Header/Header.tsx +46 -26
  126. package/src/Header/HeaderBackButton.tsx +31 -17
  127. package/src/Header/HeaderBackground.tsx +3 -3
  128. package/src/Header/HeaderButton.tsx +16 -18
  129. package/src/Header/HeaderButtonBackground.tsx +1 -4
  130. package/src/Header/HeaderIcon.tsx +27 -13
  131. package/src/Header/HeaderSearchBar.tsx +52 -44
  132. package/src/Header/HeaderTitle.tsx +3 -3
  133. package/src/Header/getHeaderTitle.tsx +4 -1
  134. package/src/Header/useHeaderHeight.tsx +1 -1
  135. package/src/Label/getLabel.tsx +1 -1
  136. package/src/LiquidGlassView.ios.tsx +14 -9
  137. package/src/LiquidGlassView.tsx +15 -11
  138. package/src/PlatformColor.native.tsx +1 -1
  139. package/src/PlatformColor.tsx +4 -0
  140. package/src/PlatformIcon.tsx +66 -0
  141. package/src/PlatformPressable.tsx +99 -41
  142. package/src/SafeAreaProviderCompat.tsx +1 -1
  143. package/src/Screen.tsx +40 -27
  144. package/src/index.tsx +2 -16
  145. package/src/internal.tsx +1 -1
  146. package/src/types.tsx +153 -76
  147. package/src/useFrameSize.tsx +9 -2
  148. package/lib/module/Lazy.js +0 -42
  149. package/lib/module/Lazy.js.map +0 -1
  150. package/lib/module/assets/back-icon.ios.svg +0 -4
  151. package/lib/module/assets/back-icon@1x.android.png +0 -0
  152. package/lib/module/assets/back-icon@1x.ios.png +0 -0
  153. package/lib/module/assets/back-icon@2x.android.png +0 -0
  154. package/lib/module/assets/back-icon@2x.ios.png +0 -0
  155. package/lib/module/assets/back-icon@3x.android.png +0 -0
  156. package/lib/module/assets/back-icon@3x.ios.png +0 -0
  157. package/lib/module/assets/back-icon@4x.android.png +0 -0
  158. package/lib/module/assets/back-icon@4x.ios.png +0 -0
  159. package/lib/module/assets/clear-icon@1x.png +0 -0
  160. package/lib/module/assets/clear-icon@2x.png +0 -0
  161. package/lib/module/assets/clear-icon@3x.png +0 -0
  162. package/lib/module/assets/clear-icon@4x.png +0 -0
  163. package/lib/module/assets/close-icon@1x.png +0 -0
  164. package/lib/module/assets/close-icon@2x.png +0 -0
  165. package/lib/module/assets/close-icon@3x.png +0 -0
  166. package/lib/module/assets/close-icon@4x.png +0 -0
  167. package/lib/module/assets/search-icon-legacy@1x.ios.png +0 -0
  168. package/lib/module/assets/search-icon-legacy@2x.ios.png +0 -0
  169. package/lib/module/assets/search-icon-legacy@3x.ios.png +0 -0
  170. package/lib/module/assets/search-icon-legacy@4x.ios.png +0 -0
  171. package/lib/module/assets/search-icon.ios.svg +0 -4
  172. package/lib/module/assets/search-icon@1x.android.png +0 -0
  173. package/lib/module/assets/search-icon@1x.ios.png +0 -0
  174. package/lib/module/assets/search-icon@2x.android.png +0 -0
  175. package/lib/module/assets/search-icon@2x.ios.png +0 -0
  176. package/lib/module/assets/search-icon@3x.android.png +0 -0
  177. package/lib/module/assets/search-icon@3x.ios.png +0 -0
  178. package/lib/module/assets/search-icon@4x.android.png +0 -0
  179. package/lib/module/assets/search-icon@4x.ios.png +0 -0
  180. package/lib/typescript/src/Lazy.d.ts +0 -31
  181. package/lib/typescript/src/Lazy.d.ts.map +0 -1
  182. package/src/Lazy.tsx +0 -59
  183. package/src/assets/back-icon.ios.svg +0 -4
  184. package/src/assets/back-icon@1x.android.png +0 -0
  185. package/src/assets/back-icon@1x.ios.png +0 -0
  186. package/src/assets/back-icon@2x.android.png +0 -0
  187. package/src/assets/back-icon@2x.ios.png +0 -0
  188. package/src/assets/back-icon@3x.android.png +0 -0
  189. package/src/assets/back-icon@3x.ios.png +0 -0
  190. package/src/assets/back-icon@4x.android.png +0 -0
  191. package/src/assets/back-icon@4x.ios.png +0 -0
  192. package/src/assets/clear-icon@1x.png +0 -0
  193. package/src/assets/clear-icon@2x.png +0 -0
  194. package/src/assets/clear-icon@3x.png +0 -0
  195. package/src/assets/clear-icon@4x.png +0 -0
  196. package/src/assets/close-icon@1x.png +0 -0
  197. package/src/assets/close-icon@2x.png +0 -0
  198. package/src/assets/close-icon@3x.png +0 -0
  199. package/src/assets/close-icon@4x.png +0 -0
  200. package/src/assets/search-icon-legacy@1x.ios.png +0 -0
  201. package/src/assets/search-icon-legacy@2x.ios.png +0 -0
  202. package/src/assets/search-icon-legacy@3x.ios.png +0 -0
  203. package/src/assets/search-icon-legacy@4x.ios.png +0 -0
  204. package/src/assets/search-icon.ios.svg +0 -4
  205. package/src/assets/search-icon@1x.android.png +0 -0
  206. package/src/assets/search-icon@1x.ios.png +0 -0
  207. package/src/assets/search-icon@2x.android.png +0 -0
  208. package/src/assets/search-icon@2x.ios.png +0 -0
  209. package/src/assets/search-icon@3x.android.png +0 -0
  210. package/src/assets/search-icon@3x.ios.png +0 -0
  211. package/src/assets/search-icon@4x.android.png +0 -0
  212. package/src/assets/search-icon@4x.ios.png +0 -0
package/src/Color.tsx CHANGED
@@ -1,21 +1,768 @@
1
- // eslint-disable-next-line no-restricted-imports
2
- import OriginalColor from 'color';
3
- import type { ColorValue } from 'react-native';
1
+ import { type ColorValue, Platform } from 'react-native';
2
+
3
+ import { DynamicColorIOS, PlatformColor } from './PlatformColor';
4
4
 
5
5
  type ColorType = {
6
- isLight(): boolean;
7
- isDark(): boolean;
8
6
  alpha(amount: number): ColorType;
9
7
  alpha(): number;
10
8
  fade(amount: number): ColorType;
11
- darken(amount: number): ColorType;
12
9
  string(): string;
13
10
  };
14
11
 
12
+ type ColorParts = [number, number, number, number];
13
+
14
+ type ParsedColor = {
15
+ parts: ColorParts;
16
+ type: (typeof COLOR_TYPES)[number];
17
+ };
18
+
15
19
  export function Color(value: ColorValue): ColorType | undefined {
16
- if (typeof value === 'string' && !value.startsWith('var(')) {
17
- return OriginalColor(value);
20
+ if (typeof value === 'string') {
21
+ const parsed = parse(value);
22
+
23
+ if (parsed) {
24
+ return build(parsed);
25
+ }
18
26
  }
19
27
 
20
28
  return undefined;
21
29
  }
30
+
31
+ Color.foreground = (color: ColorValue): ColorValue => {
32
+ const value: unknown = color;
33
+
34
+ if (typeof value === 'object' && value != null) {
35
+ // Special case for Android platform colors
36
+ // Available colors: https://developer.android.com/reference/android/R.color
37
+ if (
38
+ Platform.OS === 'android' &&
39
+ PlatformColor &&
40
+ 'resource_paths' in value &&
41
+ Array.isArray(value.resource_paths) &&
42
+ typeof value.resource_paths[0] === 'string'
43
+ ) {
44
+ const name = value.resource_paths[0].replace('@android:color/', '');
45
+ const foreground = ANDROID_COLOR_MAP[name];
46
+
47
+ if (foreground != null) {
48
+ return PlatformColor(`@android:color/${foreground}`);
49
+ }
50
+ }
51
+
52
+ // Special case for iOS platform colors
53
+ if (
54
+ Platform.OS === 'ios' &&
55
+ PlatformColor &&
56
+ 'semantic' in value &&
57
+ Array.isArray(value.semantic) &&
58
+ typeof value.semantic[0] === 'string'
59
+ ) {
60
+ const name = value.semantic[0];
61
+
62
+ const foreground = IOS_COLOR_MAP[name];
63
+
64
+ if (foreground != null) {
65
+ return foreground === 'white' || foreground === 'black'
66
+ ? foreground
67
+ : PlatformColor(foreground);
68
+ }
69
+ }
70
+
71
+ // Special case for iOS dynamic colors
72
+ if (
73
+ Platform.OS === 'ios' &&
74
+ DynamicColorIOS &&
75
+ 'dynamic' in value &&
76
+ typeof value.dynamic === 'object' &&
77
+ value.dynamic != null &&
78
+ 'light' in value.dynamic &&
79
+ typeof value.dynamic.light === 'string' &&
80
+ 'dark' in value.dynamic &&
81
+ typeof value.dynamic.dark === 'string'
82
+ ) {
83
+ const lightForeground = Color.foreground(value.dynamic.light);
84
+ const darkForeground = Color.foreground(value.dynamic.dark);
85
+
86
+ if (lightForeground && darkForeground) {
87
+ return DynamicColorIOS({
88
+ light: lightForeground,
89
+ dark: darkForeground,
90
+ });
91
+ }
92
+ }
93
+ } else if (typeof color === 'string') {
94
+ const parsed = parse(color);
95
+
96
+ if (parsed && isLight(parsed)) {
97
+ return darken(parsed, 0.71).string();
98
+ }
99
+ }
100
+
101
+ return '#fff';
102
+ };
103
+
104
+ function build(parsed: ParsedColor): ColorType {
105
+ function alpha(amount: number): ColorType;
106
+ function alpha(): number;
107
+ function alpha(amount?: number): ColorType | number {
108
+ if (amount === undefined) {
109
+ return clamp(parsed.parts[3], 0, 1);
110
+ }
111
+
112
+ const newParts: ColorParts = [...parsed.parts];
113
+
114
+ newParts[3] = amount;
115
+
116
+ return build({ type: parsed.type, parts: newParts });
117
+ }
118
+
119
+ function fade(amount: number): ColorType {
120
+ const newParts: ColorParts = [...parsed.parts];
121
+
122
+ newParts[3] = parsed.parts[3] * (1 - amount);
123
+
124
+ return build({ type: parsed.type, parts: newParts });
125
+ }
126
+
127
+ function string(): string {
128
+ return stringify(parsed);
129
+ }
130
+
131
+ return {
132
+ alpha,
133
+ fade,
134
+ string,
135
+ };
136
+ }
137
+
138
+ function parse(color: string): ParsedColor | undefined {
139
+ if (color.toLowerCase() === 'transparent') {
140
+ return { type: 'rgb', parts: [0, 0, 0, 0] };
141
+ }
142
+
143
+ const named = NAMED_COLORS[color.toLowerCase()];
144
+
145
+ if (named) {
146
+ return { type: 'rgb', parts: [named[0], named[1], named[2], 1] };
147
+ }
148
+
149
+ const hex = color.match(/^#([\da-f]+)$/i);
150
+
151
+ if (hex) {
152
+ const hexValue = hex[1];
153
+
154
+ if (hexValue == null) {
155
+ return undefined;
156
+ }
157
+
158
+ // Normalize 3-digit (#rgb) and 4-digit (#rgba) hex to 6-digit (#rrggbb) and 8-digit (#rrggbbaa)
159
+ const digits =
160
+ hexValue.length === 3 || hexValue.length === 4
161
+ ? Array.from(hexValue, (d) => d + d).join('')
162
+ : hexValue;
163
+
164
+ if (digits.length !== 6 && digits.length !== 8) {
165
+ return undefined;
166
+ }
167
+
168
+ return {
169
+ type: 'rgb',
170
+ parts: [
171
+ parseInt(digits.slice(0, 2), 16),
172
+ parseInt(digits.slice(2, 4), 16),
173
+ parseInt(digits.slice(4, 6), 16),
174
+ digits.length === 8 ? parseInt(digits.slice(6, 8), 16) / 255 : 1,
175
+ ],
176
+ };
177
+ }
178
+
179
+ const matched = color.match(/^([a-z]+)\(([^)]*)\)$/i);
180
+
181
+ if (!matched) {
182
+ return undefined;
183
+ }
184
+
185
+ // Get the color function name (e.g., "rgb", "hsl", "lab", etc.) and the color values
186
+ const matchedName = matched[1];
187
+ const argStr = matched[2];
188
+
189
+ if (matchedName == null || argStr == null) {
190
+ return undefined;
191
+ }
192
+
193
+ const fnName = matchedName.toLowerCase();
194
+
195
+ const type =
196
+ fnName === 'rgba'
197
+ ? 'rgb'
198
+ : fnName === 'hsla'
199
+ ? 'hsl'
200
+ : COLOR_TYPES.find((t) => t === fnName);
201
+
202
+ if (!type) {
203
+ return undefined;
204
+ }
205
+
206
+ // Get values and optional alpha value based on the color format
207
+ // e.g. CSS L4 (rgb(255 0 0 / 0.5) or rgb(255 0 0 / 50%)) or CSS L3 (rgba(255, 0, 0, 0.5))
208
+ const slashIdx = argStr.indexOf('/');
209
+ const valuesStr = slashIdx >= 0 ? argStr.slice(0, slashIdx) : argStr;
210
+
211
+ let alphaStr = slashIdx >= 0 ? argStr.slice(slashIdx + 1).trim() : undefined;
212
+
213
+ const tokens = valuesStr.split(/[,\s]+/).filter(Boolean);
214
+
215
+ if (alphaStr === undefined && tokens.length === 4) {
216
+ alphaStr = tokens.pop();
217
+ }
218
+
219
+ if (tokens.length !== 3) {
220
+ return undefined;
221
+ }
222
+
223
+ const percent = (max: number) => (value: string) => parsePercent(value, max);
224
+
225
+ const parsers: Record<
226
+ ParsedColor['type'],
227
+ [
228
+ (value: string) => number,
229
+ (value: string) => number,
230
+ (value: string) => number,
231
+ ]
232
+ > = {
233
+ rgb: [percent(255), percent(255), percent(255)],
234
+ hsl: [parseAngle, percent(100), percent(100)],
235
+ hwb: [parseAngle, percent(100), percent(100)],
236
+ lab: [percent(100), percent(125), percent(125)],
237
+ lch: [percent(100), percent(150), parseAngle],
238
+ oklch: [percent(1), percent(0.4), parseAngle],
239
+ oklab: [percent(1), percent(0.4), percent(0.4)],
240
+ };
241
+
242
+ try {
243
+ const [first, second, third] = tokens;
244
+
245
+ if (first == null || second == null || third == null) {
246
+ return undefined;
247
+ }
248
+
249
+ const parser = parsers[type];
250
+ const parts: ColorParts = [
251
+ parser[0](first),
252
+ parser[1](second),
253
+ parser[2](third),
254
+ alphaStr == null ? 1 : parseAlpha(alphaStr),
255
+ ];
256
+
257
+ return { type, parts };
258
+ } catch {
259
+ return undefined;
260
+ }
261
+ }
262
+
263
+ function stringify(color: ParsedColor): string {
264
+ const [p0, p1, p2, rawA] = color.parts;
265
+ const a = trim(clamp(rawA, 0, 1));
266
+
267
+ // Legacy CSS L3: name(v1, v2, v3) / namea(v1, v2, v3, alpha)
268
+ const legacy = (
269
+ name: string,
270
+ v1: string | number,
271
+ v2: string | number,
272
+ v3: string | number
273
+ ) =>
274
+ a < 1
275
+ ? `${name}a(${v1}, ${v2}, ${v3}, ${a})`
276
+ : `${name}(${v1}, ${v2}, ${v3})`;
277
+
278
+ // Modern CSS L4: name(v1 v2 v3) / name(v1 v2 v3 / alpha)
279
+ const modern = (
280
+ name: string,
281
+ v1: string | number,
282
+ v2: string | number,
283
+ v3: string | number
284
+ ) =>
285
+ a < 1 ? `${name}(${v1} ${v2} ${v3} / ${a})` : `${name}(${v1} ${v2} ${v3})`;
286
+
287
+ switch (color.type) {
288
+ case 'rgb':
289
+ return legacy(
290
+ 'rgb',
291
+ clamp(Math.round(p0), 0, 255),
292
+ clamp(Math.round(p1), 0, 255),
293
+ clamp(Math.round(p2), 0, 255)
294
+ );
295
+ case 'hsl':
296
+ return legacy(
297
+ 'hsl',
298
+ trim(p0),
299
+ `${trim(clamp(p1, 0, 100))}%`,
300
+ `${trim(clamp(p2, 0, 100))}%`
301
+ );
302
+ case 'hwb':
303
+ return modern(
304
+ 'hwb',
305
+ trim(p0),
306
+ `${trim(clamp(p1, 0, 100))}%`,
307
+ `${trim(clamp(p2, 0, 100))}%`
308
+ );
309
+ case 'lab':
310
+ return modern(
311
+ 'lab',
312
+ trim(clamp(p0, 0, 100)),
313
+ trim(clamp(p1, -125, 125)),
314
+ trim(clamp(p2, -125, 125))
315
+ );
316
+ case 'oklab':
317
+ return modern(
318
+ 'oklab',
319
+ trim(clamp(p0, 0, 1)),
320
+ trim(clamp(p1, -0.4, 0.4)),
321
+ trim(clamp(p2, -0.4, 0.4))
322
+ );
323
+ case 'lch':
324
+ return modern(
325
+ 'lch',
326
+ trim(clamp(p0, 0, 100)),
327
+ trim(clamp(p1, 0, 150)),
328
+ trim(p2)
329
+ );
330
+ case 'oklch':
331
+ return modern(
332
+ 'oklch',
333
+ trim(clamp(p0, 0, 1)),
334
+ trim(clamp(p1, 0, 0.4)),
335
+ trim(p2)
336
+ );
337
+ default: {
338
+ const _exhaustiveCheck: never = color.type;
339
+
340
+ return _exhaustiveCheck;
341
+ }
342
+ }
343
+ }
344
+
345
+ function darken(color: ParsedColor, amount: number): ColorType {
346
+ const factor = 1 - amount;
347
+ const parts: ColorParts = [...color.parts];
348
+
349
+ switch (color.type) {
350
+ case 'lab':
351
+ case 'lch':
352
+ case 'oklab':
353
+ case 'oklch':
354
+ parts[0] *= factor;
355
+
356
+ return build({ type: color.type, parts });
357
+ case 'hsl':
358
+ parts[2] *= factor;
359
+
360
+ return build({ type: 'hsl', parts });
361
+ case 'rgb':
362
+ case 'hwb': {
363
+ const [r, g, b] =
364
+ color.type === 'hwb' ? hwbToRgb(parts[0], parts[1], parts[2]) : parts;
365
+
366
+ const [h, s, l] = rgbToHsl(r, g, b);
367
+
368
+ return build({ type: 'hsl', parts: [h, s, l * factor, parts[3]] });
369
+ }
370
+ default: {
371
+ const _exhaustiveCheck: never = color.type;
372
+
373
+ return _exhaustiveCheck;
374
+ }
375
+ }
376
+ }
377
+
378
+ // YIQ luma midpoint (0-255)
379
+ // https://en.wikipedia.org/wiki/YIQ
380
+ const YIQ_LIGHT_THRESHOLD = 128;
381
+
382
+ // Rec. 601 luma coefficients (sum to 1.0)
383
+ const YIQ_WEIGHT_R = 0.299;
384
+ const YIQ_WEIGHT_G = 0.587;
385
+ const YIQ_WEIGHT_B = 0.114;
386
+
387
+ function isLight(color: ParsedColor): boolean {
388
+ let rgb: [number, number, number];
389
+
390
+ switch (color.type) {
391
+ case 'lab':
392
+ case 'lch':
393
+ return color.parts[0] > 50;
394
+ case 'oklab':
395
+ case 'oklch':
396
+ return color.parts[0] > 0.5;
397
+ case 'rgb':
398
+ rgb = [color.parts[0], color.parts[1], color.parts[2]];
399
+ break;
400
+ case 'hsl':
401
+ rgb = hslToRgb(color.parts[0], color.parts[1], color.parts[2]);
402
+ break;
403
+ case 'hwb':
404
+ rgb = hwbToRgb(color.parts[0], color.parts[1], color.parts[2]);
405
+ break;
406
+ default: {
407
+ const _exhaustiveCheck: never = color.type;
408
+
409
+ return _exhaustiveCheck;
410
+ }
411
+ }
412
+
413
+ const yiq =
414
+ YIQ_WEIGHT_R * rgb[0] + YIQ_WEIGHT_G * rgb[1] + YIQ_WEIGHT_B * rgb[2];
415
+
416
+ return yiq > YIQ_LIGHT_THRESHOLD;
417
+ }
418
+
419
+ function trim(n: number): number {
420
+ return Number(n.toFixed(3));
421
+ }
422
+
423
+ function clamp(n: number, min: number, max: number): number {
424
+ return n < min ? min : n > max ? max : n;
425
+ }
426
+
427
+ function parseNumber(s: string): number {
428
+ const n = Number(s);
429
+
430
+ if (s === '' || Number.isNaN(n)) {
431
+ throw new Error(`Invalid number: ${s}`);
432
+ }
433
+
434
+ return n;
435
+ }
436
+
437
+ function parsePercent(s: string, max: number): number {
438
+ if (s.endsWith('%')) {
439
+ return (parseNumber(s.slice(0, -1)) / 100) * max;
440
+ }
441
+
442
+ return parseNumber(s);
443
+ }
444
+
445
+ function parseAngle(s: string): number {
446
+ let degrees: number;
447
+
448
+ if (s.endsWith('grad')) {
449
+ degrees = parseNumber(s.slice(0, -4)) * 0.9;
450
+ } else if (s.endsWith('turn')) {
451
+ degrees = parseNumber(s.slice(0, -4)) * 360;
452
+ } else if (s.endsWith('rad')) {
453
+ degrees = (parseNumber(s.slice(0, -3)) * 180) / Math.PI;
454
+ } else if (s.endsWith('deg')) {
455
+ degrees = parseNumber(s.slice(0, -3));
456
+ } else {
457
+ degrees = parseNumber(s);
458
+ }
459
+
460
+ return ((degrees % 360) + 360) % 360;
461
+ }
462
+
463
+ function parseAlpha(s: string): number {
464
+ return s.endsWith('%') ? parseNumber(s.slice(0, -1)) / 100 : parseNumber(s);
465
+ }
466
+
467
+ function rgbToHsl(r: number, g: number, b: number): [number, number, number] {
468
+ r /= 255;
469
+ g /= 255;
470
+ b /= 255;
471
+
472
+ const max = Math.max(r, g, b);
473
+ const min = Math.min(r, g, b);
474
+ const l = (max + min) / 2;
475
+
476
+ if (max === min) {
477
+ return [0, 0, l * 100];
478
+ }
479
+
480
+ const d = max - min;
481
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
482
+ let h: number;
483
+
484
+ if (max === r) {
485
+ h = (g - b) / d + (g < b ? 6 : 0);
486
+ } else if (max === g) {
487
+ h = (b - r) / d + 2;
488
+ } else {
489
+ h = (r - g) / d + 4;
490
+ }
491
+
492
+ return [h * 60, s * 100, l * 100];
493
+ }
494
+
495
+ function hslToRgb(h: number, s: number, l: number): [number, number, number] {
496
+ s /= 100;
497
+ l /= 100;
498
+
499
+ const chroma = (1 - Math.abs(2 * l - 1)) * s;
500
+ const huePrime = (((h % 360) + 360) % 360) / 60;
501
+ const secondary = chroma * (1 - Math.abs((huePrime % 2) - 1));
502
+ const lightnessOffset = l - chroma / 2;
503
+
504
+ let redPrime: number;
505
+ let greenPrime: number;
506
+ let bluePrime: number;
507
+
508
+ if (huePrime < 1) {
509
+ [redPrime, greenPrime, bluePrime] = [chroma, secondary, 0];
510
+ } else if (huePrime < 2) {
511
+ [redPrime, greenPrime, bluePrime] = [secondary, chroma, 0];
512
+ } else if (huePrime < 3) {
513
+ [redPrime, greenPrime, bluePrime] = [0, chroma, secondary];
514
+ } else if (huePrime < 4) {
515
+ [redPrime, greenPrime, bluePrime] = [0, secondary, chroma];
516
+ } else if (huePrime < 5) {
517
+ [redPrime, greenPrime, bluePrime] = [secondary, 0, chroma];
518
+ } else {
519
+ [redPrime, greenPrime, bluePrime] = [chroma, 0, secondary];
520
+ }
521
+
522
+ return [
523
+ (redPrime + lightnessOffset) * 255,
524
+ (greenPrime + lightnessOffset) * 255,
525
+ (bluePrime + lightnessOffset) * 255,
526
+ ];
527
+ }
528
+
529
+ function hwbToRgb(h: number, w: number, b: number): [number, number, number] {
530
+ const whiteRatio = w / 100;
531
+ const blackRatio = b / 100;
532
+
533
+ if (whiteRatio + blackRatio >= 1) {
534
+ const gray = (whiteRatio / (whiteRatio + blackRatio)) * 255;
535
+
536
+ return [gray, gray, gray];
537
+ }
538
+
539
+ const [pureRed, pureGreen, pureBlue] = hslToRgb(h, 100, 50);
540
+ const factor = 1 - whiteRatio - blackRatio;
541
+
542
+ return [
543
+ pureRed * factor + whiteRatio * 255,
544
+ pureGreen * factor + whiteRatio * 255,
545
+ pureBlue * factor + whiteRatio * 255,
546
+ ];
547
+ }
548
+
549
+ const COLOR_TYPES = [
550
+ 'rgb',
551
+ 'hsl',
552
+ 'hwb',
553
+ 'lab',
554
+ 'lch',
555
+ 'oklch',
556
+ 'oklab',
557
+ ] as const;
558
+
559
+ const NAMED_COLORS: Record<string, [number, number, number]> = {
560
+ aliceblue: [240, 248, 255],
561
+ antiquewhite: [250, 235, 215],
562
+ aqua: [0, 255, 255],
563
+ aquamarine: [127, 255, 212],
564
+ azure: [240, 255, 255],
565
+ beige: [245, 245, 220],
566
+ bisque: [255, 228, 196],
567
+ black: [0, 0, 0],
568
+ blanchedalmond: [255, 235, 205],
569
+ blue: [0, 0, 255],
570
+ blueviolet: [138, 43, 226],
571
+ brown: [165, 42, 42],
572
+ burntsienna: [234, 126, 93],
573
+ burlywood: [222, 184, 135],
574
+ cadetblue: [95, 158, 160],
575
+ chartreuse: [127, 255, 0],
576
+ chocolate: [210, 105, 30],
577
+ coral: [255, 127, 80],
578
+ cornflowerblue: [100, 149, 237],
579
+ cornsilk: [255, 248, 220],
580
+ crimson: [220, 20, 60],
581
+ cyan: [0, 255, 255],
582
+ darkblue: [0, 0, 139],
583
+ darkcyan: [0, 139, 139],
584
+ darkgoldenrod: [184, 134, 11],
585
+ darkgray: [169, 169, 169],
586
+ darkgreen: [0, 100, 0],
587
+ darkgrey: [169, 169, 169],
588
+ darkkhaki: [189, 183, 107],
589
+ darkmagenta: [139, 0, 139],
590
+ darkolivegreen: [85, 107, 47],
591
+ darkorange: [255, 140, 0],
592
+ darkorchid: [153, 50, 204],
593
+ darkred: [139, 0, 0],
594
+ darksalmon: [233, 150, 122],
595
+ darkseagreen: [143, 188, 143],
596
+ darkslateblue: [72, 61, 139],
597
+ darkslategray: [47, 79, 79],
598
+ darkslategrey: [47, 79, 79],
599
+ darkturquoise: [0, 206, 209],
600
+ darkviolet: [148, 0, 211],
601
+ deeppink: [255, 20, 147],
602
+ deepskyblue: [0, 191, 255],
603
+ dimgray: [105, 105, 105],
604
+ dimgrey: [105, 105, 105],
605
+ dodgerblue: [30, 144, 255],
606
+ firebrick: [178, 34, 34],
607
+ floralwhite: [255, 250, 240],
608
+ forestgreen: [34, 139, 34],
609
+ fuchsia: [255, 0, 255],
610
+ gainsboro: [220, 220, 220],
611
+ ghostwhite: [248, 248, 255],
612
+ gold: [255, 215, 0],
613
+ goldenrod: [218, 165, 32],
614
+ gray: [128, 128, 128],
615
+ green: [0, 128, 0],
616
+ greenyellow: [173, 255, 47],
617
+ grey: [128, 128, 128],
618
+ honeydew: [240, 255, 240],
619
+ hotpink: [255, 105, 180],
620
+ indianred: [205, 92, 92],
621
+ indigo: [75, 0, 130],
622
+ ivory: [255, 255, 240],
623
+ khaki: [240, 230, 140],
624
+ lavender: [230, 230, 250],
625
+ lavenderblush: [255, 240, 245],
626
+ lawngreen: [124, 252, 0],
627
+ lemonchiffon: [255, 250, 205],
628
+ lightblue: [173, 216, 230],
629
+ lightcoral: [240, 128, 128],
630
+ lightcyan: [224, 255, 255],
631
+ lightgoldenrodyellow: [250, 250, 210],
632
+ lightgray: [211, 211, 211],
633
+ lightgreen: [144, 238, 144],
634
+ lightgrey: [211, 211, 211],
635
+ lightpink: [255, 182, 193],
636
+ lightsalmon: [255, 160, 122],
637
+ lightseagreen: [32, 178, 170],
638
+ lightskyblue: [135, 206, 250],
639
+ lightslategray: [119, 136, 153],
640
+ lightslategrey: [119, 136, 153],
641
+ lightsteelblue: [176, 196, 222],
642
+ lightyellow: [255, 255, 224],
643
+ lime: [0, 255, 0],
644
+ limegreen: [50, 205, 50],
645
+ linen: [250, 240, 230],
646
+ magenta: [255, 0, 255],
647
+ maroon: [128, 0, 0],
648
+ mediumaquamarine: [102, 205, 170],
649
+ mediumblue: [0, 0, 205],
650
+ mediumorchid: [186, 85, 211],
651
+ mediumpurple: [147, 112, 219],
652
+ mediumseagreen: [60, 179, 113],
653
+ mediumslateblue: [123, 104, 238],
654
+ mediumspringgreen: [0, 250, 154],
655
+ mediumturquoise: [72, 209, 204],
656
+ mediumvioletred: [199, 21, 133],
657
+ midnightblue: [25, 25, 112],
658
+ mintcream: [245, 255, 250],
659
+ mistyrose: [255, 228, 225],
660
+ moccasin: [255, 228, 181],
661
+ navajowhite: [255, 222, 173],
662
+ navy: [0, 0, 128],
663
+ oldlace: [253, 245, 230],
664
+ olive: [128, 128, 0],
665
+ olivedrab: [107, 142, 35],
666
+ orange: [255, 165, 0],
667
+ orangered: [255, 69, 0],
668
+ orchid: [218, 112, 214],
669
+ palegoldenrod: [238, 232, 170],
670
+ palegreen: [152, 251, 152],
671
+ paleturquoise: [175, 238, 238],
672
+ palevioletred: [219, 112, 147],
673
+ papayawhip: [255, 239, 213],
674
+ peachpuff: [255, 218, 185],
675
+ peru: [205, 133, 63],
676
+ pink: [255, 192, 203],
677
+ plum: [221, 160, 221],
678
+ powderblue: [176, 224, 230],
679
+ purple: [128, 0, 128],
680
+ rebeccapurple: [102, 51, 153],
681
+ red: [255, 0, 0],
682
+ rosybrown: [188, 143, 143],
683
+ royalblue: [65, 105, 225],
684
+ saddlebrown: [139, 69, 19],
685
+ salmon: [250, 128, 114],
686
+ sandybrown: [244, 164, 96],
687
+ seagreen: [46, 139, 87],
688
+ seashell: [255, 245, 238],
689
+ sienna: [160, 82, 45],
690
+ silver: [192, 192, 192],
691
+ skyblue: [135, 206, 235],
692
+ slateblue: [106, 90, 205],
693
+ slategray: [112, 128, 144],
694
+ slategrey: [112, 128, 144],
695
+ snow: [255, 250, 250],
696
+ springgreen: [0, 255, 127],
697
+ steelblue: [70, 130, 180],
698
+ tan: [210, 180, 140],
699
+ teal: [0, 128, 128],
700
+ thistle: [216, 191, 216],
701
+ tomato: [255, 99, 71],
702
+ turquoise: [64, 224, 208],
703
+ violet: [238, 130, 238],
704
+ wheat: [245, 222, 179],
705
+ white: [255, 255, 255],
706
+ whitesmoke: [245, 245, 245],
707
+ yellow: [255, 255, 0],
708
+ yellowgreen: [154, 205, 50],
709
+ };
710
+
711
+ const ANDROID_COLOR_MAP: Record<string, string> = {
712
+ system_background_dark: 'system_on_background_dark',
713
+ system_background_light: 'system_on_background_light',
714
+ system_error_container_dark: 'system_on_error_container_dark',
715
+ system_error_container_light: 'system_on_error_container_light',
716
+ system_error_dark: 'system_on_error_dark',
717
+ system_error_light: 'system_on_error_light',
718
+ system_primary_container_dark: 'system_on_primary_container_dark',
719
+ system_primary_container_light: 'system_on_primary_container_light',
720
+ system_primary_dark: 'system_on_primary_dark',
721
+ system_primary_fixed: 'system_on_primary_fixed',
722
+ system_primary_light: 'system_on_primary_light',
723
+ system_secondary_container_dark: 'system_on_secondary_container_dark',
724
+ system_secondary_container_light: 'system_on_secondary_container_light',
725
+ system_secondary_dark: 'system_on_secondary_dark',
726
+ system_secondary_fixed: 'system_on_secondary_fixed',
727
+ system_secondary_light: 'system_on_secondary_light',
728
+ system_surface_dark: 'system_on_surface_dark',
729
+ system_surface_disabled: 'system_on_surface_disabled',
730
+ system_surface_light: 'system_on_surface_light',
731
+ system_surface_variant_dark: 'system_on_surface_variant_dark',
732
+ system_surface_variant_light: 'system_on_surface_variant_light',
733
+ };
734
+
735
+ const IOS_COLOR_MAP: Record<string, string> = {
736
+ systemBackground: 'label',
737
+ secondarySystemBackground: 'label',
738
+ tertiarySystemBackground: 'label',
739
+ systemGroupedBackground: 'label',
740
+ secondarySystemGroupedBackground: 'label',
741
+ tertiarySystemGroupedBackground: 'label',
742
+
743
+ systemFill: 'label',
744
+ secondarySystemFill: 'label',
745
+ tertiarySystemFill: 'label',
746
+ quaternarySystemFill: 'label',
747
+
748
+ systemRed: 'white',
749
+ systemGreen: 'white',
750
+ systemBlue: 'white',
751
+ systemIndigo: 'white',
752
+ systemPurple: 'white',
753
+ systemBrown: 'white',
754
+
755
+ systemOrange: 'black',
756
+ systemYellow: 'black',
757
+ systemMint: 'black',
758
+ systemTeal: 'black',
759
+ systemCyan: 'black',
760
+ systemPink: 'black',
761
+
762
+ systemGray: 'label',
763
+ systemGray2: 'label',
764
+ systemGray3: 'label',
765
+ systemGray4: 'label',
766
+ systemGray5: 'label',
767
+ systemGray6: 'label',
768
+ };