@tamagui/themes 2.0.0-1768530912818 → 2.0.0-1768636514428

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 (76) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/cjs/generated-v4-tamagui.cjs +138 -114
  3. package/dist/cjs/generated-v4-tamagui.js +25 -1
  4. package/dist/cjs/generated-v4-tamagui.js.map +1 -1
  5. package/dist/cjs/generated-v4-tamagui.native.js +138 -114
  6. package/dist/cjs/generated-v4-tamagui.native.js.map +1 -1
  7. package/dist/cjs/generated-v4.cjs +78 -64
  8. package/dist/cjs/generated-v4.js +15 -1
  9. package/dist/cjs/generated-v4.js.map +1 -1
  10. package/dist/cjs/generated-v4.native.js +78 -64
  11. package/dist/cjs/generated-v4.native.js.map +1 -1
  12. package/dist/cjs/generated-v5.cjs +453 -1039
  13. package/dist/cjs/generated-v5.js +693 -1419
  14. package/dist/cjs/generated-v5.js.map +1 -1
  15. package/dist/cjs/generated-v5.native.js +453 -1039
  16. package/dist/cjs/generated-v5.native.js.map +1 -1
  17. package/dist/cjs/opacify.cjs +53 -0
  18. package/dist/cjs/opacify.js +46 -0
  19. package/dist/cjs/opacify.js.map +2 -2
  20. package/dist/cjs/opacify.native.js +57 -0
  21. package/dist/cjs/opacify.native.js.map +1 -1
  22. package/dist/cjs/v5-themes.cjs +114 -288
  23. package/dist/cjs/v5-themes.js +88 -290
  24. package/dist/cjs/v5-themes.js.map +2 -2
  25. package/dist/cjs/v5-themes.native.js +125 -299
  26. package/dist/cjs/v5-themes.native.js.map +1 -1
  27. package/dist/esm/generated-v4-tamagui.js +25 -1
  28. package/dist/esm/generated-v4-tamagui.js.map +1 -1
  29. package/dist/esm/generated-v4-tamagui.mjs +138 -114
  30. package/dist/esm/generated-v4-tamagui.mjs.map +1 -1
  31. package/dist/esm/generated-v4-tamagui.native.js +138 -114
  32. package/dist/esm/generated-v4-tamagui.native.js.map +1 -1
  33. package/dist/esm/generated-v4.js +15 -1
  34. package/dist/esm/generated-v4.js.map +1 -1
  35. package/dist/esm/generated-v4.mjs +78 -64
  36. package/dist/esm/generated-v4.mjs.map +1 -1
  37. package/dist/esm/generated-v4.native.js +78 -64
  38. package/dist/esm/generated-v4.native.js.map +1 -1
  39. package/dist/esm/generated-v5.js +693 -1419
  40. package/dist/esm/generated-v5.js.map +1 -1
  41. package/dist/esm/generated-v5.mjs +453 -1039
  42. package/dist/esm/generated-v5.mjs.map +1 -1
  43. package/dist/esm/generated-v5.native.js +453 -1039
  44. package/dist/esm/generated-v5.native.js.map +1 -1
  45. package/dist/esm/opacify.js +46 -0
  46. package/dist/esm/opacify.js.map +2 -2
  47. package/dist/esm/opacify.mjs +53 -1
  48. package/dist/esm/opacify.mjs.map +1 -1
  49. package/dist/esm/opacify.native.js +57 -1
  50. package/dist/esm/opacify.native.js.map +1 -1
  51. package/dist/esm/v5-themes.js +99 -309
  52. package/dist/esm/v5-themes.js.map +1 -1
  53. package/dist/esm/v5-themes.mjs +112 -287
  54. package/dist/esm/v5-themes.mjs.map +1 -1
  55. package/dist/esm/v5-themes.native.js +123 -298
  56. package/dist/esm/v5-themes.native.js.map +1 -1
  57. package/package.json +7 -7
  58. package/src/generated-v4-tamagui.ts +138 -114
  59. package/src/generated-v4.ts +78 -64
  60. package/src/generated-v5.ts +858 -2035
  61. package/src/opacify.ts +89 -0
  62. package/src/v5-themes.ts +215 -415
  63. package/tests/v5-themes.test.ts +197 -0
  64. package/tsconfig.json +1 -0
  65. package/types/generated-v4-tamagui.d.ts.map +1 -1
  66. package/types/generated-v4.d.ts.map +1 -1
  67. package/types/generated-v5.d.ts +83 -138
  68. package/types/generated-v5.d.ts.map +1 -1
  69. package/types/opacify.d.ts +7 -0
  70. package/types/opacify.d.ts.map +1 -1
  71. package/types/v4-default.d.ts +10 -10
  72. package/types/v4-default.d.ts.map +1 -1
  73. package/types/v4-tamagui.d.ts +5 -5
  74. package/types/v4-tamagui.d.ts.map +1 -1
  75. package/types/v5-themes.d.ts +190 -590
  76. package/types/v5-themes.d.ts.map +1 -1
package/src/v5-themes.ts CHANGED
@@ -1,47 +1,26 @@
1
1
  import {
2
- blueDark as blueDarkIn,
3
- blue as blueIn,
4
- greenDark as greenDarkIn,
5
- green as greenIn,
6
- orangeDark as orangeDarkIn,
7
- orange as orangeIn,
8
- pinkDark as pinkDarkIn,
9
- pink as pinkIn,
10
- purpleDark as purpleDarkIn,
11
- purple as purpleIn,
12
- redDark as redDarkIn,
13
- red as redIn,
14
- tealDark as tealDarkIn,
15
- teal as tealIn,
16
- yellowDark as yellowDarkIn,
17
- yellow as yellowIn,
18
- gray as grayIn,
19
- grayDark as grayDarkIn,
2
+ blue,
3
+ blueDark,
4
+ gray,
5
+ grayDark,
6
+ green,
7
+ greenDark,
8
+ red,
9
+ redDark,
10
+ yellow,
11
+ yellowDark,
20
12
  } from '@tamagui/colors'
21
13
  import { createThemes, defaultComponentThemes } from '@tamagui/theme-builder'
22
- import { opacify } from './opacify'
23
-
24
- // Use Radix colors directly without saturation/lightness adjustments
25
- const blue = blueIn
26
- const blueDark = blueDarkIn
27
- const green = greenIn
28
- const greenDark = greenDarkIn
29
- const purple = purpleIn
30
- const purpleDark = purpleDarkIn
31
- const red = redIn
32
- const redDark = redDarkIn
33
- const yellow = yellowIn
34
- const yellowDark = yellowDarkIn
35
- const pink = pinkIn
36
- const pinkDark = pinkDarkIn
37
- const orange = orangeIn
38
- const orangeDark = orangeDarkIn
39
- const teal = tealIn
40
- const tealDark = tealDarkIn
41
- const gray = grayIn
42
- const grayDark = grayDarkIn
43
-
44
- // Aligned with v4 - darker grays for dark mode
14
+ import { interpolateColor, opacify } from './opacify'
15
+
16
+ /** Generate named colors from a palette: ['#fff', ...] -> { name1: '#fff', name2: ... } */
17
+ function paletteToNamedColors<N extends string>(name: N, palette: readonly string[]) {
18
+ return Object.fromEntries(palette.map((color, i) => [`${name}${i + 1}`, color])) as {
19
+ [K in `${N}${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12}`]: string
20
+ }
21
+ }
22
+
23
+ // Base palettes
45
24
  const darkPalette = [
46
25
  '#050505',
47
26
  '#151515',
@@ -72,12 +51,7 @@ const lightPalette = [
72
51
  'hsl(0, 0%, 2%)',
73
52
  ]
74
53
 
75
- /**
76
- * Neutral grey palette that has sufficient contrast on both white and black backgrounds.
77
- * All values are in the 32-68% lightness range, making them visible in any context.
78
- * Uses the same values for both light and dark themes.
79
- */
80
-
54
+ /** Neutral grey - sufficient contrast on both white and black backgrounds */
81
55
  const neutralPalette = [
82
56
  'hsl(0, 0%, 68%)',
83
57
  'hsl(0, 0%, 65%)',
@@ -93,82 +67,10 @@ const neutralPalette = [
93
67
  'hsl(0, 0%, 32%)',
94
68
  ]
95
69
 
96
- const neutral = {
97
- neutral1: neutralPalette[0]!,
98
- neutral2: neutralPalette[1]!,
99
- neutral3: neutralPalette[2]!,
100
- neutral4: neutralPalette[3]!,
101
- neutral5: neutralPalette[4]!,
102
- neutral6: neutralPalette[5]!,
103
- neutral7: neutralPalette[6]!,
104
- neutral8: neutralPalette[7]!,
105
- neutral9: neutralPalette[8]!,
106
- neutral10: neutralPalette[9]!,
107
- neutral11: neutralPalette[10]!,
108
- neutral12: neutralPalette[11]!,
109
- }
110
-
111
- const tanLightPalette = [
112
- 'hsla(40, 30%, 98%, 1)',
113
- 'hsla(40, 24%, 94%, 1)',
114
- 'hsla(38, 23%, 91%, 1)',
115
- 'hsla(36, 20%, 90%, 1)',
116
- 'hsla(36, 20%, 88%, 1)',
117
- 'hsla(35, 20%, 85%, 1)',
118
- 'hsla(35, 21%, 74%, 1)',
119
- 'hsla(34, 20%, 70%, 1)',
120
- 'hsla(35, 20%, 67%, 1)',
121
- 'hsla(34, 19%, 47%, 1)',
122
- 'hsla(35, 18%, 37%, 1)',
123
- 'hsla(35, 17%, 20%, 1)',
124
- ]
70
+ // Generate neutral colors from palette (used in defaultChildrenThemes)
71
+ const neutral = paletteToNamedColors('neutral', neutralPalette)
125
72
 
126
- const tanDarkPalette = [
127
- 'hsla(30, 9%, 10%, 1)',
128
- 'hsla(30, 10%, 12%, 1)',
129
- 'hsla(31, 11%, 18%, 1)',
130
- 'hsla(30, 12%, 23%, 1)',
131
- 'hsla(30, 14%, 28%, 1)',
132
- 'hsla(30, 16%, 33%, 1)',
133
- 'hsla(30, 18%, 38%, 1)',
134
- 'hsla(30, 20%, 45%, 1)',
135
- 'hsla(30, 21%, 50%, 1)',
136
- 'hsla(29, 22%, 58%, 1)',
137
- 'hsla(34, 24%, 70%, 1)',
138
- 'hsla(11, 12%, 79%, 1)',
139
- ]
140
-
141
- const tan = {
142
- tan1: tanLightPalette[0]!,
143
- tan2: tanLightPalette[1]!,
144
- tan3: tanLightPalette[2]!,
145
- tan4: tanLightPalette[3]!,
146
- tan5: tanLightPalette[4]!,
147
- tan6: tanLightPalette[5]!,
148
- tan7: tanLightPalette[6]!,
149
- tan8: tanLightPalette[7]!,
150
- tan9: tanLightPalette[8]!,
151
- tan10: tanLightPalette[9]!,
152
- tan11: tanLightPalette[10]!,
153
- tan12: tanLightPalette[11]!,
154
- }
155
-
156
- const tanDark = {
157
- tan1: tanDarkPalette[0]!,
158
- tan2: tanDarkPalette[1]!,
159
- tan3: tanDarkPalette[2]!,
160
- tan4: tanDarkPalette[3]!,
161
- tan5: tanDarkPalette[4]!,
162
- tan6: tanDarkPalette[5]!,
163
- tan7: tanDarkPalette[6]!,
164
- tan8: tanDarkPalette[7]!,
165
- tan9: tanDarkPalette[8]!,
166
- tan10: tanDarkPalette[9]!,
167
- tan11: tanDarkPalette[10]!,
168
- tan12: tanDarkPalette[11]!,
169
- }
170
-
171
- // the same on both light and dark, useful for forcing white/black:
73
+ // Constants for forcing white/black with opacity variants
172
74
  const whiteBlack = {
173
75
  white: 'rgba(255,255,255,1)',
174
76
  white0: 'rgba(255,255,255,0)',
@@ -184,45 +86,6 @@ const whiteBlack = {
184
86
  black08: 'rgba(0,0,0,0.8)',
185
87
  }
186
88
 
187
- // highlights are lighter no matter if dark or light theme
188
- // on light theme they are ==== background
189
- // on dark theme === color2-4 looks good
190
-
191
- const extraColorsDark = {
192
- // in between 1/2
193
- color1pt5: 'rgba(20,20,20)',
194
- color2pt5: '#222',
195
-
196
- // TODO: ideally just functions for alpha($color1, 0.1)
197
- // extra low opacities
198
- color01: opacify(darkPalette[darkPalette.length - 1]!, 0.1),
199
- color0075: opacify(darkPalette[darkPalette.length - 1]!, 0.075),
200
- color005: opacify(darkPalette[darkPalette.length - 1]!, 0.05),
201
- color0025: opacify(darkPalette[darkPalette.length - 1]!, 0.025),
202
-
203
- background01: opacify(darkPalette[0]!, 0.1),
204
- background0075: opacify(darkPalette[0]!, 0.075),
205
- background005: opacify(darkPalette[0]!, 0.05),
206
- background0025: opacify(darkPalette[0]!, 0.025),
207
- }
208
-
209
- const extraColorsLight = {
210
- // in between 1/2
211
- color1pt5: '#f9f9f9',
212
- color2pt5: '#f4f4f4',
213
-
214
- // extra low opacities
215
- color01: opacify(lightPalette[lightPalette.length - 1]!, 0.1),
216
- color0075: opacify(lightPalette[lightPalette.length - 1]!, 0.075),
217
- color005: opacify(lightPalette[lightPalette.length - 1]!, 0.05),
218
- color0025: opacify(lightPalette[lightPalette.length - 1]!, 0.025),
219
-
220
- background01: opacify(lightPalette[0]!, 0.1),
221
- background0075: opacify(lightPalette[0]!, 0.075),
222
- background005: opacify(lightPalette[0]!, 0.05),
223
- background0025: opacify(lightPalette[0]!, 0.025),
224
- }
225
-
226
89
  const darkShadows = {
227
90
  shadow1: 'rgba(0,0,0,0.1)',
228
91
  shadow2: 'rgba(0,0,0,0.18)',
@@ -241,109 +104,88 @@ const lightShadows = {
241
104
  shadow6: 'rgba(0,0,0,0.3)',
242
105
  }
243
106
 
244
- const blackColors = {
245
- black1: darkPalette[0]!,
246
- black2: darkPalette[1]!,
247
- black3: darkPalette[2]!,
248
- black4: darkPalette[3]!,
249
- black5: darkPalette[4]!,
250
- black6: darkPalette[5]!,
251
- black7: darkPalette[6]!,
252
- black8: darkPalette[7]!,
253
- black9: darkPalette[8]!,
254
- black10: darkPalette[9]!,
255
- black11: darkPalette[10]!,
256
- black12: darkPalette[11]!,
257
- }
258
-
259
- const whiteColors = {
260
- white1: lightPalette[0]!,
261
- white2: lightPalette[1]!,
262
- white3: lightPalette[2]!,
263
- white4: lightPalette[3]!,
264
- white5: lightPalette[4]!,
265
- white6: lightPalette[5]!,
266
- white7: lightPalette[6]!,
267
- white8: lightPalette[7]!,
268
- white9: lightPalette[8]!,
269
- white10: lightPalette[9]!,
270
- white11: lightPalette[10]!,
271
- white12: lightPalette[11]!,
272
- }
273
-
274
- // Default color palettes exported for customization
275
- export { darkPalette, lightPalette }
276
-
277
- // Default color sets exported for customization
278
- export const defaultColors = {
279
- light: {
280
- blue,
281
- gray,
282
- green,
283
- neutral,
284
- orange,
285
- pink,
286
- purple,
287
- red,
288
- tan,
289
- teal,
290
- yellow,
291
- },
292
- dark: {
293
- blue: blueDark,
294
- gray: grayDark,
295
- green: greenDark,
296
- neutral, // same for both light and dark - that's the point!
297
- orange: orangeDark,
298
- pink: pinkDark,
299
- purple: purpleDark,
300
- red: redDark,
301
- tan: tanDark,
302
- teal: tealDark,
303
- yellow: yellowDark,
304
- },
107
+ // Export palettes for customization
108
+ export { darkPalette as defaultDarkPalette, lightPalette as defaultLightPalette }
109
+
110
+ // Internal types
111
+ type NamedColors = Record<string, string>
112
+ type ChildTheme<T extends NamedColors = NamedColors> = { light: T; dark: T }
113
+ type GrandChildrenThemeDefinition = { template: string }
114
+
115
+ /** Default children themes - accepts radix colors directly */
116
+ export const defaultChildrenThemes = {
117
+ gray: { light: gray, dark: grayDark },
118
+ blue: { light: blue, dark: blueDark },
119
+ red: { light: red, dark: redDark },
120
+ yellow: { light: yellow, dark: yellowDark },
121
+ green: { light: green, dark: greenDark },
122
+ neutral: { light: neutral, dark: neutral },
305
123
  }
306
124
 
307
- export type ColorPalette = string[]
308
-
309
- export type ColorTheme = {
310
- palette: {
311
- dark: ColorPalette
312
- light: ColorPalette
313
- }
314
- }
315
-
316
- export type GrandChildrenThemeDefinition = {
317
- template: string
318
- }
319
-
320
- /** Default color names available in v5 themes */
321
- export type DefaultColorName =
322
- | 'blue'
323
- | 'gray'
324
- | 'green'
325
- | 'orange'
326
- | 'pink'
327
- | 'purple'
328
- | 'red'
329
- | 'tan'
330
- | 'teal'
331
- | 'yellow'
332
-
333
- export type ColorDefinition = { dark: ColorPalette; light: ColorPalette }
334
-
335
- export type CreateV5ThemeOptions = {
125
+ /** Default grandchildren themes available in v5 */
126
+ export const defaultGrandChildrenThemes = {
127
+ accent: { template: 'inverse' },
128
+ // simplified and removed:
129
+ // alt1: { template: 'alt1' },
130
+ // alt2: { template: 'alt2' },
131
+ // surface1: { template: 'surface1' },
132
+ // surface2: { template: 'surface2' },
133
+ // surface3: { template: 'surface3' },
134
+ } satisfies Record<string, GrandChildrenThemeDefinition>
135
+
136
+ /** Union of all color values from children themes (for light or dark) */
137
+ type ChildrenColors<
138
+ T extends Record<string, ChildTheme>,
139
+ Mode extends 'light' | 'dark',
140
+ > = {
141
+ [K in keyof T]: T[K][Mode]
142
+ }[keyof T]
143
+
144
+ /** Merge all color objects from children into one type */
145
+ type MergedChildrenColors<
146
+ T extends Record<string, ChildTheme>,
147
+ Mode extends 'light' | 'dark',
148
+ > = UnionToIntersection<ChildrenColors<T, Mode>>
149
+
150
+ // Helper to convert union to intersection
151
+ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
152
+ k: infer I
153
+ ) => void
154
+ ? I
155
+ : never
156
+
157
+ // Named color types for black/white (generated from palettes in createV5Theme)
158
+ type BlackColors = ReturnType<typeof paletteToNamedColors<'black'>>
159
+ type WhiteColors = ReturnType<typeof paletteToNamedColors<'white'>>
160
+
161
+ // Base extra colors type (always included) - getTheme computes opacity/interpolation colors
162
+ type BaseExtraCommon = BlackColors & WhiteColors & typeof whiteBlack
163
+ type BaseExtraLight = BaseExtraCommon & typeof lightShadows & { shadowColor: string }
164
+ type BaseExtraDark = BaseExtraCommon & typeof darkShadows & { shadowColor: string }
165
+
166
+ export type CreateV5ThemeOptions<
167
+ Children extends Record<string, ChildTheme> = typeof defaultChildrenThemes,
168
+ GrandChildren extends Record<
169
+ string,
170
+ GrandChildrenThemeDefinition
171
+ > = typeof defaultGrandChildrenThemes,
172
+ > = {
336
173
  /** Override the dark base palette (12 colors from darkest to lightest) */
337
- darkPalette?: ColorPalette
174
+ darkPalette?: string[]
338
175
  /** Override the light base palette (12 colors from lightest to darkest) */
339
- lightPalette?: ColorPalette
340
- /** Add or override color themes (e.g., { brand: { dark: [...], light: [...] } }) */
341
- colors?: Partial<Record<DefaultColorName, ColorDefinition>> &
342
- Record<string, ColorDefinition>
343
- /** Whether to include default color themes (blue, red, green, etc.). Defaults to true */
344
- includeDefaultColors?: boolean
345
- /** Add or override grandChildrenThemes (e.g., { alt1: { template: 'alt1' } }) */
346
- grandChildrenThemes?: Record<string, GrandChildrenThemeDefinition>
176
+ lightPalette?: string[]
177
+ /**
178
+ * Override children themes (color themes like blue, red, etc.)
179
+ * Accepts radix color objects directly: { blue: { light: blue, dark: blueDark } }
180
+ */
181
+ childrenThemes?: Children
182
+ /**
183
+ * Override grandChildren themes (alt1, alt2, surface1, etc.)
184
+ * Pass undefined or omit to use defaultGrandChildrenThemes
185
+ */
186
+ grandChildrenThemes?: GrandChildren
187
+ /** Override component themes. Pass false to disable, or provide custom component themes. Defaults to defaultComponentThemes */
188
+ componentThemes?: false | Parameters<typeof createThemes>[0]['componentThemes']
347
189
  }
348
190
 
349
191
  /**
@@ -354,131 +196,98 @@ export type CreateV5ThemeOptions = {
354
196
  * const themes = createV5Theme()
355
197
  *
356
198
  * @example
357
- * Add a custom brand color
199
+ * Custom children themes with brand color (accepts radix colors directly)
358
200
  * const themes = createV5Theme({
359
- * colors: {
360
- * brand: {
361
- * light: ['#fff', '#fef', '#fdf', '#fcf', '#fbf', '#faf', '#f9f', '#f8f', '#f7f', '#f6f', '#f5f', '#f0f'],
362
- * dark: ['#101', '#202', '#303', '#404', '#505', '#606', '#707', '#808', '#909', '#a0a', '#b0b', '#c0c'],
363
- * },
201
+ * childrenThemes: {
202
+ * ...defaultChildrenThemes,
203
+ * brand: { light: brandLight, dark: brandDark },
364
204
  * },
365
205
  * })
366
206
  *
367
207
  * @example
368
- * Override base palettes
208
+ * Minimal - no color themes
369
209
  * const themes = createV5Theme({
370
- * lightPalette: ['#fff', '#fafafa', ...],
371
- * darkPalette: ['#000', '#111', ...],
210
+ * childrenThemes: {},
372
211
  * })
373
212
  */
374
- export function createV5Theme(options: CreateV5ThemeOptions = {}) {
213
+ export function createV5Theme<
214
+ Children extends Record<string, ChildTheme> = typeof defaultChildrenThemes,
215
+ GrandChildren extends Record<
216
+ string,
217
+ GrandChildrenThemeDefinition
218
+ > = typeof defaultGrandChildrenThemes,
219
+ >(
220
+ options: CreateV5ThemeOptions<Children, GrandChildren> = {} as CreateV5ThemeOptions<
221
+ Children,
222
+ GrandChildren
223
+ >
224
+ ) {
375
225
  const {
376
226
  darkPalette: customDarkPalette = darkPalette,
377
227
  lightPalette: customLightPalette = lightPalette,
378
- colors = {},
379
- includeDefaultColors = true,
380
- grandChildrenThemes: customGrandChildrenThemes = {},
228
+ childrenThemes = defaultChildrenThemes as unknown as Children,
229
+ grandChildrenThemes = defaultGrandChildrenThemes as unknown as GrandChildren,
230
+ componentThemes: customComponentThemes = defaultComponentThemes,
381
231
  } = options
382
232
 
383
- // Build childrenThemes from default colors + custom colors
384
- const childrenThemes: Record<string, ColorTheme> = {
233
+ // Generate black/white named colors from palettes
234
+ const blackColors = paletteToNamedColors('black', customDarkPalette)
235
+ const whiteColors = paletteToNamedColors('white', customLightPalette)
236
+
237
+ // Build extra colors - spread children color objects directly (types flow naturally)
238
+ // Note: opacity/interpolation colors (color01, background01, etc.) are computed by getTheme
239
+ const extraBase = {
240
+ ...blackColors,
241
+ ...whiteColors,
242
+ ...whiteBlack,
243
+ }
244
+ const lightExtraBase = {
245
+ ...extraBase,
246
+ ...lightShadows,
247
+ shadowColor: lightShadows.shadow1,
248
+ }
249
+ const darkExtraBase = { ...extraBase, ...darkShadows, shadowColor: darkShadows.shadow1 }
250
+
251
+ // Spread all children colors into extra - types flow from Children generic
252
+ type LightExtra = BaseExtraLight & MergedChildrenColors<Children, 'light'>
253
+ type DarkExtra = BaseExtraDark & MergedChildrenColors<Children, 'dark'>
254
+
255
+ const lightExtra = { ...lightExtraBase } as LightExtra
256
+ const darkExtra = { ...darkExtraBase } as DarkExtra
257
+
258
+ for (const theme of Object.values(childrenThemes)) {
259
+ if (theme.light) Object.assign(lightExtra, theme.light)
260
+ if (theme.dark) Object.assign(darkExtra, theme.dark)
261
+ }
262
+
263
+ // Convert children to palette format for createThemes, adding black/white internally
264
+ type PaletteEntry = { palette: { light: string[]; dark: string[] } }
265
+ type ChildrenWithPalettes = { [K in keyof Children | 'black' | 'white']: PaletteEntry }
266
+
267
+ const childrenWithPalettes = {
268
+ // Always include black/white for theme generation
385
269
  black: {
386
- palette: {
387
- dark: Object.values(blackColors),
388
- light: Object.values(blackColors),
389
- },
270
+ palette: { dark: Object.values(blackColors), light: Object.values(blackColors) },
390
271
  },
391
272
  white: {
392
- palette: {
393
- dark: Object.values(whiteColors),
394
- light: Object.values(whiteColors),
395
- },
273
+ palette: { dark: Object.values(whiteColors), light: Object.values(whiteColors) },
396
274
  },
397
- }
398
-
399
- if (includeDefaultColors) {
400
- Object.assign(childrenThemes, {
401
- gray: {
402
- palette: {
403
- dark: Object.values(grayDark),
404
- light: Object.values(gray),
405
- },
406
- },
407
- blue: {
408
- palette: {
409
- dark: Object.values(blueDark),
410
- light: Object.values(blue),
411
- },
412
- },
413
- red: {
414
- palette: {
415
- dark: Object.values(redDark),
416
- light: Object.values(red),
417
- },
418
- },
419
- yellow: {
420
- palette: {
421
- dark: Object.values(yellowDark),
422
- light: Object.values(yellow),
423
- },
424
- },
425
- green: {
426
- palette: {
427
- dark: Object.values(greenDark),
428
- light: Object.values(green),
429
- },
430
- },
431
- teal: {
432
- palette: {
433
- dark: Object.values(tealDark),
434
- light: Object.values(teal),
435
- },
436
- },
437
- orange: {
438
- palette: {
439
- dark: Object.values(orangeDark),
440
- light: Object.values(orange),
275
+ ...(Object.fromEntries(
276
+ Object.entries(childrenThemes).map(([name, theme]) => [
277
+ name,
278
+ {
279
+ palette: {
280
+ light: Object.values(theme.light),
281
+ dark: Object.values(theme.dark),
282
+ },
441
283
  },
442
- },
443
- pink: {
444
- palette: {
445
- dark: Object.values(pinkDark),
446
- light: Object.values(pink),
447
- },
448
- },
449
- purple: {
450
- palette: {
451
- dark: Object.values(purpleDark),
452
- light: Object.values(purple),
453
- },
454
- },
455
- neutral: {
456
- palette: {
457
- dark: neutralPalette,
458
- light: neutralPalette,
459
- },
460
- },
461
- tan: {
462
- palette: {
463
- dark: tanDarkPalette,
464
- light: tanLightPalette,
465
- },
466
- },
467
- })
468
- }
469
-
470
- // Add custom colors (can override default ones)
471
- for (const [name, palette] of Object.entries(colors)) {
472
- childrenThemes[name] = {
473
- palette: {
474
- dark: palette.dark,
475
- light: palette.light,
476
- },
477
- }
478
- }
284
+ ])
285
+ ) as { [K in keyof Children]: PaletteEntry }),
286
+ } as ChildrenWithPalettes
479
287
 
480
288
  return createThemes({
481
- componentThemes: defaultComponentThemes,
289
+ // componentThemes: false disables them, undefined/truthy values enable them
290
+ componentThemes: customComponentThemes,
482
291
 
483
292
  base: {
484
293
  palette: {
@@ -487,44 +296,8 @@ export function createV5Theme(options: CreateV5ThemeOptions = {}) {
487
296
  },
488
297
 
489
298
  extra: {
490
- light: {
491
- ...blackColors,
492
- ...blue,
493
- ...gray,
494
- ...green,
495
- ...lightShadows,
496
- ...neutral,
497
- ...orange,
498
- ...pink,
499
- ...purple,
500
- ...red,
501
- ...tan,
502
- ...teal,
503
- ...whiteColors,
504
- ...yellow,
505
- shadowColor: lightShadows.shadow1,
506
- ...whiteBlack,
507
- ...extraColorsLight,
508
- },
509
- dark: {
510
- ...blackColors,
511
- ...blueDark,
512
- ...darkShadows,
513
- ...grayDark,
514
- ...greenDark,
515
- ...neutral, // same for both light and dark
516
- ...orangeDark,
517
- ...pinkDark,
518
- ...purpleDark,
519
- ...redDark,
520
- ...tanDark,
521
- ...tealDark,
522
- ...whiteColors,
523
- ...yellowDark,
524
- shadowColor: darkShadows.shadow1,
525
- ...whiteBlack,
526
- ...extraColorsDark,
527
- },
299
+ light: lightExtra,
300
+ dark: darkExtra,
528
301
  },
529
302
  },
530
303
 
@@ -535,21 +308,48 @@ export function createV5Theme(options: CreateV5ThemeOptions = {}) {
535
308
  },
536
309
  },
537
310
 
538
- childrenThemes,
539
-
540
- grandChildrenThemes: {
541
- accent: {
542
- template: 'inverse',
543
- },
544
- alt1: { template: 'alt1' },
545
- alt2: { template: 'alt2' },
546
- surface1: { template: 'surface1' },
547
- surface2: { template: 'surface2' },
548
- surface3: { template: 'surface3' },
549
- ...customGrandChildrenThemes,
311
+ childrenThemes: childrenWithPalettes,
312
+
313
+ grandChildrenThemes,
314
+
315
+ // Add computed colors to ALL themes based on each theme's palette
316
+ getTheme: ({ palette }) => {
317
+ if (!palette || palette.length < 3) {
318
+ throw new Error(`invalid palette: ${JSON.stringify(palette)}`)
319
+ }
320
+
321
+ // palette[1] is background-ish, palette[length-2] is foreground-ish
322
+ const bgColor = palette[1]!
323
+ const fgColor = palette[palette.length - 2]!
324
+
325
+ return {
326
+ // In-between shades
327
+ color0pt5: interpolateColor(bgColor, palette[2]!, 0.5),
328
+ color1pt5: interpolateColor(palette[1]!, palette[2]!, 0.5),
329
+ color2pt5: interpolateColor(palette[2]!, palette[3]!, 0.5),
330
+ // Opacity variants of foreground color
331
+ color01: opacify(fgColor, 0.1),
332
+ color0075: opacify(fgColor, 0.075),
333
+ color005: opacify(fgColor, 0.05),
334
+ color0025: opacify(fgColor, 0.025),
335
+ color002: opacify(fgColor, 0.02),
336
+ color001: opacify(fgColor, 0.01),
337
+ // Opacity variants of background color
338
+ background01: opacify(bgColor, 0.1),
339
+ background0075: opacify(bgColor, 0.075),
340
+ background005: opacify(bgColor, 0.05),
341
+ background0025: opacify(bgColor, 0.025),
342
+ background002: opacify(bgColor, 0.02),
343
+ background001: opacify(bgColor, 0.01),
344
+ }
550
345
  },
551
346
  })
552
347
  }
553
348
 
554
349
  // Default themes using the createV5Theme function
555
350
  export const themes = createV5Theme()
351
+
352
+ // type sanity checks - these should not cause type errors:
353
+ themes.dark.background0075
354
+ themes.dark.background
355
+ themes.dark.accent1