@tamagui/web 1.72.3 → 1.73.0

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 (122) hide show
  1. package/dist/cjs/contexts/ComponentContext.js +1 -0
  2. package/dist/cjs/contexts/ComponentContext.js.map +1 -1
  3. package/dist/cjs/contexts/ComponentContext.native.js +1 -0
  4. package/dist/cjs/contexts/ComponentContext.native.js.map +1 -1
  5. package/dist/cjs/createComponent.js +21 -25
  6. package/dist/cjs/createComponent.js.map +1 -1
  7. package/dist/cjs/createComponent.native.js +20 -24
  8. package/dist/cjs/createComponent.native.js.map +1 -1
  9. package/dist/cjs/createTamagui.js +35 -32
  10. package/dist/cjs/createTamagui.js.map +1 -1
  11. package/dist/cjs/createTamagui.native.js +35 -32
  12. package/dist/cjs/createTamagui.native.js.map +1 -1
  13. package/dist/cjs/helpers/getThemeCSSRules.js +1 -1
  14. package/dist/cjs/helpers/getThemeCSSRules.js.map +1 -1
  15. package/dist/cjs/helpers/propMapper.js +15 -10
  16. package/dist/cjs/helpers/propMapper.js.map +1 -1
  17. package/dist/cjs/helpers/propMapper.native.js +15 -10
  18. package/dist/cjs/helpers/propMapper.native.js.map +1 -1
  19. package/dist/cjs/hooks/useConfiguration.js +33 -0
  20. package/dist/cjs/hooks/useConfiguration.js.map +6 -0
  21. package/dist/cjs/hooks/useConfiguration.native.js +34 -0
  22. package/dist/cjs/hooks/useConfiguration.native.js.map +6 -0
  23. package/dist/cjs/hooks/useDisableSSR.js +34 -0
  24. package/dist/cjs/hooks/useDisableSSR.js.map +6 -0
  25. package/dist/cjs/hooks/useDisableSSR.native.js +35 -0
  26. package/dist/cjs/hooks/useDisableSSR.native.js.map +6 -0
  27. package/dist/cjs/hooks/useMedia.js +7 -9
  28. package/dist/cjs/hooks/useMedia.js.map +1 -1
  29. package/dist/cjs/hooks/useMedia.native.js +7 -9
  30. package/dist/cjs/hooks/useMedia.native.js.map +1 -1
  31. package/dist/cjs/hooks/useProps.js +2 -2
  32. package/dist/cjs/hooks/useProps.js.map +1 -1
  33. package/dist/cjs/hooks/useProps.native.js +2 -2
  34. package/dist/cjs/hooks/useProps.native.js.map +1 -1
  35. package/dist/cjs/hooks/useTheme.js +26 -27
  36. package/dist/cjs/hooks/useTheme.js.map +2 -2
  37. package/dist/cjs/hooks/useTheme.native.js +26 -27
  38. package/dist/cjs/hooks/useTheme.native.js.map +2 -2
  39. package/dist/cjs/index.js +6 -6
  40. package/dist/cjs/index.js.map +1 -1
  41. package/dist/cjs/index.native.js +6 -6
  42. package/dist/cjs/index.native.js.map +1 -1
  43. package/dist/cjs/views/Configuration.js +26 -0
  44. package/dist/cjs/views/Configuration.js.map +6 -0
  45. package/dist/cjs/views/Configuration.native.js +27 -0
  46. package/dist/cjs/views/Configuration.native.js.map +6 -0
  47. package/dist/cjs/views/Theme.js +16 -11
  48. package/dist/cjs/views/Theme.js.map +1 -1
  49. package/dist/cjs/views/Theme.native.js +16 -11
  50. package/dist/cjs/views/Theme.native.js.map +1 -1
  51. package/dist/cjs/views/ThemeDebug.js +8 -8
  52. package/dist/cjs/views/ThemeDebug.js.map +1 -1
  53. package/dist/esm/contexts/ComponentContext.js +1 -0
  54. package/dist/esm/contexts/ComponentContext.js.map +1 -1
  55. package/dist/esm/createComponent.js +14 -18
  56. package/dist/esm/createComponent.js.map +1 -1
  57. package/dist/esm/createTamagui.js +35 -32
  58. package/dist/esm/createTamagui.js.map +1 -1
  59. package/dist/esm/helpers/getThemeCSSRules.js +1 -1
  60. package/dist/esm/helpers/getThemeCSSRules.js.map +1 -1
  61. package/dist/esm/helpers/propMapper.js +15 -10
  62. package/dist/esm/helpers/propMapper.js.map +1 -1
  63. package/dist/esm/hooks/useConfiguration.js +15 -0
  64. package/dist/esm/hooks/useConfiguration.js.map +6 -0
  65. package/dist/esm/hooks/useDisableSSR.js +15 -0
  66. package/dist/esm/hooks/useDisableSSR.js.map +6 -0
  67. package/dist/esm/hooks/useMedia.js +7 -7
  68. package/dist/esm/hooks/useMedia.js.map +1 -1
  69. package/dist/esm/hooks/useProps.js +1 -1
  70. package/dist/esm/hooks/useProps.js.map +1 -1
  71. package/dist/esm/hooks/useTheme.js +23 -25
  72. package/dist/esm/hooks/useTheme.js.map +2 -2
  73. package/dist/esm/index.js +3 -3
  74. package/dist/esm/index.js.map +1 -1
  75. package/dist/esm/views/Configuration.js +7 -0
  76. package/dist/esm/views/Configuration.js.map +6 -0
  77. package/dist/esm/views/Theme.js +15 -11
  78. package/dist/esm/views/Theme.js.map +1 -1
  79. package/dist/esm/views/ThemeDebug.js +2 -2
  80. package/dist/esm/views/ThemeDebug.js.map +1 -1
  81. package/package.json +9 -9
  82. package/src/contexts/ComponentContext.tsx +1 -0
  83. package/src/createComponent.tsx +16 -17
  84. package/src/createTamagui.ts +54 -51
  85. package/src/helpers/getThemeCSSRules.ts +2 -1
  86. package/src/helpers/propMapper.ts +16 -18
  87. package/src/hooks/useConfiguration.tsx +14 -0
  88. package/src/hooks/useDisableSSR.tsx +14 -0
  89. package/src/hooks/useMedia.tsx +12 -8
  90. package/src/hooks/useProps.tsx +1 -1
  91. package/src/hooks/useTheme.tsx +26 -25
  92. package/src/index.ts +3 -3
  93. package/src/types.tsx +7 -6
  94. package/src/views/Configuration.tsx +11 -0
  95. package/src/views/Theme.tsx +8 -9
  96. package/src/views/ThemeDebug.tsx +3 -3
  97. package/types/contexts/ComponentContext.d.ts.map +1 -1
  98. package/types/createComponent.d.ts.map +1 -1
  99. package/types/createTamagui.d.ts.map +1 -1
  100. package/types/helpers/getThemeCSSRules.d.ts.map +1 -1
  101. package/types/helpers/propMapper.d.ts.map +1 -1
  102. package/types/hooks/useConfiguration.d.ts +151 -0
  103. package/types/hooks/useConfiguration.d.ts.map +1 -0
  104. package/types/hooks/useDisableSSR.d.ts +4 -0
  105. package/types/hooks/useDisableSSR.d.ts.map +1 -0
  106. package/types/hooks/useMedia.d.ts +2 -3
  107. package/types/hooks/useMedia.d.ts.map +1 -1
  108. package/types/hooks/useTheme.d.ts +3 -3
  109. package/types/hooks/useTheme.d.ts.map +1 -1
  110. package/types/index.d.ts +3 -3
  111. package/types/index.d.ts.map +1 -1
  112. package/types/types.d.ts +7 -6
  113. package/types/types.d.ts.map +1 -1
  114. package/types/views/Configuration.d.ts +9 -0
  115. package/types/views/Configuration.d.ts.map +1 -0
  116. package/types/views/Theme.d.ts.map +1 -1
  117. package/src/helpers/getAnimationDriver.tsx +0 -8
  118. package/src/hooks/useAnimationDriver.tsx +0 -8
  119. package/src/views/AnimationDriverProvider.tsx +0 -16
  120. package/types/helpers/getAnimationDriver.d.ts +0 -9
  121. package/types/hooks/useAnimationDriver.d.ts +0 -4
  122. package/types/hooks/useStyle.d.ts +0 -8
@@ -43,6 +43,7 @@ import {
43
43
  DisposeFn,
44
44
  GroupState,
45
45
  LayoutEvent,
46
+ SizeTokens,
46
47
  SpaceDirection,
47
48
  SpaceValue,
48
49
  SpacerProps,
@@ -570,11 +571,7 @@ export function createComponent<
570
571
 
571
572
  if (process.env.NODE_ENV === 'development' && time) time`theme`
572
573
 
573
- const mediaState = useMedia(
574
- // @ts-ignore, we just pass a stable object so we can get it later with
575
- // should match to the one used in `setMediaShouldUpdate` below
576
- stateRef
577
- )
574
+ const mediaState = useMedia(stateRef, componentContext)
578
575
 
579
576
  if (process.env.NODE_ENV === 'development' && time) time`media`
580
577
 
@@ -603,7 +600,7 @@ export function createComponent<
603
600
  props,
604
601
  staticConfig,
605
602
  theme,
606
- themeState.state.name,
603
+ themeState?.state?.name || '',
607
604
  state,
608
605
  styleProps,
609
606
  null,
@@ -697,7 +694,7 @@ export function createComponent<
697
694
  presence,
698
695
  componentState: state,
699
696
  styleProps,
700
- theme: themeState.state.theme!,
697
+ theme: themeState.state?.theme!,
701
698
  pseudos: pseudos || null,
702
699
  hostRef,
703
700
  staticConfig,
@@ -1349,6 +1346,17 @@ export function Unspaced(props: { children?: any }) {
1349
1346
  }
1350
1347
  Unspaced['isUnspaced'] = true
1351
1348
 
1349
+ const getSpacerSize = (size: SizeTokens | number | boolean, { tokens }) => {
1350
+ size = size === true ? '$true' : size
1351
+ const sizePx = tokens.space[size as any] ?? size
1352
+ return {
1353
+ width: sizePx,
1354
+ height: sizePx,
1355
+ minWidth: sizePx,
1356
+ minHeight: sizePx,
1357
+ }
1358
+ }
1359
+
1352
1360
  // dont used styled() here to avoid circular deps
1353
1361
  // keep inline to avoid circular deps
1354
1362
  // @ts-expect-error we override
@@ -1368,16 +1376,7 @@ export const Spacer = createComponent<SpacerProps>({
1368
1376
 
1369
1377
  variants: {
1370
1378
  size: {
1371
- '...size': (size, { tokens }) => {
1372
- size = size === true ? '$true' : size
1373
- const sizePx = tokens.space[size] ?? size
1374
- return {
1375
- width: sizePx,
1376
- height: sizePx,
1377
- minWidth: sizePx,
1378
- minHeight: sizePx,
1379
- }
1380
- },
1379
+ '...': getSpacerSize,
1381
1380
  },
1382
1381
 
1383
1382
  flex: {
@@ -2,7 +2,7 @@ import { isWeb } from '@tamagui/constants'
2
2
 
3
3
  import { configListeners, setConfig, setTokens } from './config'
4
4
  import { Variable } from './createVariable'
5
- import { createVariables } from './createVariables'
5
+ import { DeepVariableObject, createVariables } from './createVariables'
6
6
  import { getThemeCSSRules } from './helpers/getThemeCSSRules'
7
7
  import {
8
8
  getAllRules,
@@ -19,6 +19,7 @@ import {
19
19
  CreateTamaguiProps,
20
20
  DedupedTheme,
21
21
  DedupedThemes,
22
+ GenericFont,
22
23
  GetCSS,
23
24
  InferTamaguiConfig,
24
25
  TamaguiInternalConfig,
@@ -38,62 +39,63 @@ export function createTamagui<Conf extends CreateTamaguiProps>(
38
39
  return configIn as any
39
40
  }
40
41
 
41
- if (process.env.NODE_ENV === 'development') {
42
- if (!configIn.tokens) {
43
- throw new Error('Must define tokens')
44
- }
45
- if (!configIn.themes) {
46
- throw new Error('Must define themes')
47
- }
48
- if (!configIn.fonts) {
49
- throw new Error('Must define fonts')
50
- }
51
- }
52
-
53
42
  // ensure variables
54
- const tokens = createVariables(configIn.tokens)
55
-
56
- // faster lookups
57
43
  const tokensParsed: TokensParsed = {} as any
58
- const tokensMerged: TokensMerged = {} as any
59
- for (const cat in tokens) {
60
- tokensParsed[cat] = {}
61
- tokensMerged[cat] = {}
62
- const tokenCat = tokens[cat]
63
- for (const key in tokenCat) {
64
- const val = tokenCat[key]
65
- const prefixedKey = `$${key}`
66
- tokensParsed[cat][prefixedKey] = val as any
67
- tokensMerged[cat][prefixedKey] = val as any
68
- tokensMerged[cat][key] = val as any
44
+ const tokens = createVariables(configIn.tokens || {})
45
+
46
+ if (configIn.tokens) {
47
+ // faster lookups
48
+ const tokensMerged: TokensMerged = {} as any
49
+ for (const cat in tokens) {
50
+ tokensParsed[cat] = {}
51
+ tokensMerged[cat] = {}
52
+ const tokenCat = tokens[cat]
53
+ for (const key in tokenCat) {
54
+ const val = tokenCat[key]
55
+ const prefixedKey = `$${key}`
56
+ tokensParsed[cat][prefixedKey] = val as any
57
+ tokensMerged[cat][prefixedKey] = val as any
58
+ tokensMerged[cat][key] = val as any
59
+ }
69
60
  }
61
+ setTokens(tokensMerged)
70
62
  }
71
- setTokens(tokensMerged)
72
63
 
73
- const noThemes = Object.keys(configIn.themes).length === 0
74
- const foundThemes = scanAllSheets(noThemes, tokensParsed)
75
- listenForSheetChanges()
64
+ let foundThemes: DedupedThemes | undefined
65
+ if (configIn.themes) {
66
+ const noThemes = Object.keys(configIn.themes).length === 0
67
+ foundThemes = scanAllSheets(noThemes, tokensParsed)
68
+ }
76
69
 
77
- const fontTokens = Object.fromEntries(
78
- Object.entries(configIn.fonts!).map(([k, v]) => {
79
- return [k, createVariables(v, 'f', true)]
80
- })
81
- )
70
+ listenForSheetChanges()
82
71
 
83
72
  let fontSizeTokens: Set<string> | null = null
84
-
85
- const fontsParsed = (() => {
86
- const res = {} as typeof fontTokens
87
- for (const familyName in fontTokens) {
88
- const font = fontTokens[familyName]
89
- const fontParsed = parseFont(font)
90
- res[`$${familyName}`] = fontParsed
91
- if (!fontSizeTokens && fontParsed.size) {
92
- fontSizeTokens = new Set(Object.keys(fontParsed.size))
73
+ let fontsParsed:
74
+ | {
75
+ [k: string]: DeepVariableObject<GenericFont<string>>
93
76
  }
94
- }
95
- return res!
96
- })()
77
+ | undefined
78
+
79
+ if (configIn.fonts) {
80
+ const fontTokens = Object.fromEntries(
81
+ Object.entries(configIn.fonts).map(([k, v]) => {
82
+ return [k, createVariables(v, 'f', true)]
83
+ })
84
+ )
85
+
86
+ fontsParsed = (() => {
87
+ const res = {} as typeof fontTokens
88
+ for (const familyName in fontTokens) {
89
+ const font = fontTokens[familyName]
90
+ const fontParsed = parseFont(font)
91
+ res[`$${familyName}`] = fontParsed
92
+ if (!fontSizeTokens && fontParsed.size) {
93
+ fontSizeTokens = new Set(Object.keys(fontParsed.size))
94
+ }
95
+ }
96
+ return res!
97
+ })()
98
+ }
97
99
 
98
100
  const specificTokens = {}
99
101
 
@@ -238,14 +240,14 @@ ${runtimeStyles}`
238
240
  let defaultFontName =
239
241
  configIn.defaultFont ||
240
242
  // uses font named "body" if present for compat
241
- ('body' in configIn.fonts ? 'body' : '')
243
+ (configIn.fonts && ('body' in configIn.fonts ? 'body' : ''))
242
244
 
243
245
  if (!defaultFontName && configIn.fonts) {
244
246
  // defaults to the first font to make life easier
245
247
  defaultFontName = Object.keys(configIn.fonts)[0]
246
248
  }
247
249
 
248
- if (defaultFontName[0] === '$') {
250
+ if (defaultFontName?.[0] === '$') {
249
251
  defaultFontName = defaultFontName.slice(1)
250
252
  }
251
253
 
@@ -253,6 +255,7 @@ ${runtimeStyles}`
253
255
  const defaultFont = `$${defaultFontName}`
254
256
 
255
257
  const config: TamaguiInternalConfig = {
258
+ fonts: {},
256
259
  groupNames: [],
257
260
  settings: {},
258
261
  onlyAllowShorthands: false,
@@ -267,7 +270,7 @@ ${runtimeStyles}`
267
270
  ? Object.fromEntries(Object.entries(shorthands).map(([k, v]) => [v, k]))
268
271
  : {},
269
272
  themes: themeConfig.themes as any,
270
- fontsParsed,
273
+ fontsParsed: fontsParsed || {},
271
274
  themeConfig,
272
275
  tokensParsed: tokensParsed as any,
273
276
  parsed: true,
@@ -23,7 +23,8 @@ export function getThemeCSSRules(props: {
23
23
  const { config, themeName, theme, names } = props
24
24
 
25
25
  // special case for SSR
26
- const hasDarkLight = 'light' in config.themes || 'dark' in config.themes
26
+ const hasDarkLight =
27
+ config.themes && ('light' in config.themes || 'dark' in config.themes)
27
28
  const CNP = `.${THEME_CLASSNAME_PREFIX}`
28
29
  let vars = ''
29
30
 
@@ -101,7 +101,7 @@ const resolveVariants: StyleResolver = (
101
101
  const { variants } = staticConfig
102
102
  if (!variants) return
103
103
 
104
- let variantValue = getVariantDefinition(variants[key], key, value, conf)
104
+ let variantValue = getVariantDefinition(variants[key], value, conf)
105
105
 
106
106
  if (process.env.NODE_ENV === 'development' && debug === 'verbose') {
107
107
  console.groupCollapsed(`♦️♦️♦️ resolve variant ${key}`)
@@ -316,30 +316,28 @@ const tokenCats = ['size', 'color', 'radius', 'space', 'zIndex'].map((name) => (
316
316
  }))
317
317
 
318
318
  // goes through specificity finding best matching variant function
319
- function getVariantDefinition(
320
- variant: any,
321
- key: string,
322
- value: any,
323
- conf: TamaguiInternalConfig
324
- ) {
319
+ function getVariantDefinition(variant: any, value: any, conf: TamaguiInternalConfig) {
325
320
  if (typeof variant === 'function') {
326
321
  return variant
327
322
  }
328
- if (variant[value]) {
329
- return variant[value]
323
+ const exact = variant[value]
324
+ if (exact) {
325
+ return exact
330
326
  }
331
- const { tokensParsed } = conf
332
- for (const { name, spreadName } of tokenCats) {
333
- if (spreadName in variant && value in tokensParsed[name]) {
334
- return variant[spreadName]
327
+ if (value != null) {
328
+ const { tokensParsed } = conf
329
+ for (const { name, spreadName } of tokenCats) {
330
+ if (spreadName in variant && value in tokensParsed[name]) {
331
+ return variant[spreadName]
332
+ }
333
+ }
334
+ const fontSizeVariant = variant['...fontSize']
335
+ if (fontSizeVariant && conf.fontSizeTokens.has(value)) {
336
+ return fontSizeVariant
335
337
  }
336
- }
337
- const fontSizeVariant = variant['...fontSize']
338
- if (fontSizeVariant && conf.fontSizeTokens.has(value)) {
339
- return fontSizeVariant
340
338
  }
341
339
  // fallback to catch all | size
342
- return variant[`:${typeof value}`] || variant['...'] || variant['...size']
340
+ return variant[`:${typeof value}`] || variant['...']
343
341
  }
344
342
 
345
343
  const fontShorthand = {
@@ -0,0 +1,14 @@
1
+ import { useContext } from 'react'
2
+
3
+ import { getConfig } from '../config'
4
+ import { ComponentContext } from '../contexts/ComponentContext'
5
+
6
+ export const useConfiguration = () => {
7
+ const { groups, animationDriver, ...restComponentConfig } = useContext(ComponentContext)
8
+ const { animations, ...restConfig } = getConfig()
9
+ return {
10
+ ...restConfig,
11
+ ...restComponentConfig,
12
+ animationDriver: animationDriver ?? getConfig().animations,
13
+ }
14
+ }
@@ -0,0 +1,14 @@
1
+ import { useContext } from 'react'
2
+
3
+ import { getConfig } from '../config'
4
+ import { ComponentContext } from '../contexts/ComponentContext'
5
+ import { ComponentContextI } from '../types'
6
+
7
+ export function useDisableSSR() {
8
+ const componentContext = useContext(ComponentContext)
9
+ return getDisableSSR(componentContext)
10
+ }
11
+
12
+ export function getDisableSSR(componentContext: ComponentContextI) {
13
+ return componentContext.disableSSR ?? getConfig().disableSSR
14
+ }
@@ -1,11 +1,13 @@
1
1
  import { useIsomorphicLayoutEffect } from '@tamagui/constants'
2
- import { useRef, useSyncExternalStore } from 'react'
2
+ import { useContext, useRef, useSyncExternalStore } from 'react'
3
3
 
4
4
  import { getConfig } from '../config'
5
+ import { ComponentContext } from '../contexts/ComponentContext'
5
6
  import { createProxy } from '../helpers/createProxy'
6
7
  import { matchMedia } from '../helpers/matchMedia'
7
8
  import { pseudoDescriptors } from '../helpers/pseudoDescriptors'
8
9
  import type {
10
+ ComponentContextI,
9
11
  MediaQueries,
10
12
  MediaQueryKey,
11
13
  MediaQueryObject,
@@ -13,6 +15,8 @@ import type {
13
15
  TamaguiInternalConfig,
14
16
  UseMediaState,
15
17
  } from '../types'
18
+ import { useConfiguration } from './useConfiguration'
19
+ import { getDisableSSR, useDisableSSR } from './useDisableSSR'
16
20
 
17
21
  export let mediaState: MediaQueryState =
18
22
  // development only safeguard
@@ -50,9 +54,6 @@ export const isMediaKey = (key: string) =>
50
54
 
51
55
  // for SSR capture it at time of startup
52
56
  let initState: MediaQueryState
53
- export const getInitialMediaState = () => {
54
- return (getConfig().disableSSR ? mediaState : initState) || {}
55
- }
56
57
 
57
58
  // media always above pseudos
58
59
  const defaultMediaImportance = Object.keys(pseudoDescriptors).length
@@ -191,14 +192,17 @@ function subscribe(subscriber: any) {
191
192
  return () => listeners.delete(subscriber)
192
193
  }
193
194
 
194
- export function useMedia(uid?: any): UseMediaState {
195
+ export function useMedia(uid?: any, componentContext?: ComponentContextI): UseMediaState {
195
196
  const internal = useRef<UseMediaInternalState | undefined>()
197
+ // performance boost to avoid using context twice
198
+ const disableSSR = componentContext ? getDisableSSR(componentContext) : useDisableSSR()
199
+ const initialState = (disableSSR ? mediaState : initState) || {}
196
200
 
197
201
  const state = useSyncExternalStore<MediaQueryState>(
198
202
  subscribe,
199
203
  () => {
200
204
  if (!internal.current) {
201
- return initState
205
+ return initialState
202
206
  }
203
207
 
204
208
  const { touched, prev } = internal.current
@@ -223,13 +227,13 @@ export function useMedia(uid?: any): UseMediaState {
223
227
 
224
228
  return mediaState
225
229
  },
226
- () => initState
230
+ () => initialState
227
231
  )
228
232
 
229
233
  return new Proxy(state, {
230
234
  get(_, key) {
231
235
  if (typeof key === 'string') {
232
- internal.current ||= { prev: initState }
236
+ internal.current ||= { prev: initialState }
233
237
  internal.current.touched ||= new Set()
234
238
  internal.current.touched.add(key)
235
239
  }
@@ -76,7 +76,7 @@ export function usePropsAndStyle<A extends Object>(
76
76
  props,
77
77
  staticConfig,
78
78
  theme,
79
- themeState.state.name,
79
+ themeState.state?.name || '',
80
80
  defaultComponentStateMounted,
81
81
  {
82
82
  isAnimated: false,
@@ -21,8 +21,8 @@ import type {
21
21
  import { GetThemeUnwrapped } from './getThemeUnwrapped'
22
22
 
23
23
  export type ChangedThemeResponse = {
24
- state: ThemeManagerState
25
- themeManager: ThemeManager
24
+ state?: ThemeManagerState
25
+ themeManager?: ThemeManager | null
26
26
  isNewTheme: boolean
27
27
  mounted?: boolean
28
28
  }
@@ -90,22 +90,23 @@ export const useThemeWithState = (
90
90
  )
91
91
 
92
92
  const { themeManager, state } = changedThemeState
93
- const { theme, name, className } = state
94
93
 
95
- if (!theme) {
94
+ if (!state?.theme) {
96
95
  if (process.env.NODE_ENV === 'development') {
97
- throw new Error(
98
- `No theme found given props ${JSON.stringify(
99
- props
100
- )}. Themes given to tamagui are: ${Object.keys(getConfig().themes)}`
101
- )
96
+ if (process.env.TAMAGUI_DISABLE_NO_THEME_WARNING !== '1') {
97
+ console.warn(
98
+ `[tamagui] No theme found, this could be due to an invalid theme name (given theme props ${JSON.stringify(
99
+ props
100
+ )}).\n\nIf this is intended and you are using Tamagui without any themes, you can disable this warning by setting the environment variable TAMAGUI_DISABLE_NO_THEME_WARNING=1`
101
+ )
102
+ }
102
103
  }
103
- throw `❌ 1`
104
104
  }
105
105
 
106
106
  const themeProxied = useMemo(() => {
107
- return getThemeProxied(theme, themeManager, keys.current, props.debug)
108
- }, [theme, name, className, themeManager])
107
+ if (!themeManager || !state?.theme) return {}
108
+ return getThemeProxied(state.theme, themeManager, keys.current, props.debug)
109
+ }, [state, themeManager])
109
110
 
110
111
  if (process.env.NODE_ENV === 'development' && props.debug === 'verbose') {
111
112
  console.groupCollapsed(' 🔹 useTheme =>', name)
@@ -184,7 +185,7 @@ export const activeThemeManagers = new Set<ThemeManager>()
184
185
 
185
186
  export const useChangeThemeEffect = (
186
187
  props: ThemeProps,
187
- root = false,
188
+ isRoot = false,
188
189
  keys?: string[],
189
190
  shouldUpdate?: () => boolean | undefined
190
191
  ): ChangedThemeResponse => {
@@ -195,11 +196,10 @@ export const useChangeThemeEffect = (
195
196
 
196
197
  const parentManager = useContext(ThemeManagerContext)
197
198
 
198
- if (disable) {
199
- if (!parentManager) throw `❌ 2`
199
+ if ((!isRoot && !parentManager) || disable) {
200
200
  return {
201
201
  isNewTheme: false,
202
- state: parentManager.state,
202
+ state: parentManager?.state,
203
203
  themeManager: parentManager,
204
204
  }
205
205
  }
@@ -224,11 +224,11 @@ export const useChangeThemeEffect = (
224
224
  function getShouldUpdateTheme(
225
225
  manager = themeManager,
226
226
  nextState?: ThemeManagerState | null,
227
- prevState: ThemeManagerState = state,
227
+ prevState: ThemeManagerState | undefined = state,
228
228
  forceShouldChange = false
229
229
  ) {
230
230
  const forceUpdate = shouldUpdate?.()
231
- if (!forceShouldChange && forceUpdate === false) return
231
+ if (!manager || (!forceShouldChange && forceUpdate === false)) return
232
232
  const next = nextState || manager.getState(props, parentManager)
233
233
  if (forceShouldChange) return next
234
234
  if (!next) return
@@ -241,6 +241,8 @@ export const useChangeThemeEffect = (
241
241
  if (!isServer) {
242
242
  // listen for parent change + notify children change
243
243
  useLayoutEffect(() => {
244
+ if (!themeManager) return
245
+
244
246
  // SSR safe inverse (because server can't know prefers scheme)
245
247
  // could be done through fancy selectors like how we do prefers-media
246
248
  // but may be a bit of explosion of selectors
@@ -269,7 +271,6 @@ export const useChangeThemeEffect = (
269
271
  const doUpdate = force ?? Boolean(keys?.length || isNewTheme)
270
272
 
271
273
  if (process.env.NODE_ENV === 'development' && props.debug) {
272
-
273
274
  // biome-ignore lint/suspicious/noConsoleLog: <explanation>
274
275
  console.log(` 🔸 onChange`, themeManager.id, {
275
276
  force,
@@ -278,7 +279,7 @@ export const useChangeThemeEffect = (
278
279
  name,
279
280
  manager,
280
281
  keys,
281
- });
282
+ })
282
283
  }
283
284
  if (doUpdate) {
284
285
  setThemeState(createState)
@@ -313,12 +314,12 @@ export const useChangeThemeEffect = (
313
314
  }
314
315
 
315
316
  if (isInversingOnMount) {
316
- if (!parentManager) throw '❌ 3'
317
317
  return {
318
318
  isNewTheme: false,
319
319
  themeManager: parentManager,
320
320
  state: {
321
- ...parentManager.state,
321
+ name: '',
322
+ ...parentManager?.state,
322
323
  className: '',
323
324
  },
324
325
  }
@@ -342,7 +343,7 @@ export const useChangeThemeEffect = (
342
343
 
343
344
  if (hasThemeUpdatingProps) {
344
345
  const getNewThemeManager = () => {
345
- return new ThemeManager(props, root ? 'root' : parentManager)
346
+ return new ThemeManager(props, isRoot ? 'root' : parentManager)
346
347
  }
347
348
 
348
349
  if (prev?.themeManager) {
@@ -388,7 +389,7 @@ export const useChangeThemeEffect = (
388
389
  const isNewTheme = Boolean(themeManager !== parentManager || props.inverse)
389
390
 
390
391
  // only inverse relies on this for ssr
391
- const mounted = !props.inverse ? true : root || prev?.mounted
392
+ const mounted = !props.inverse ? true : isRoot || prev?.mounted
392
393
 
393
394
  if (!state) {
394
395
  if (isNewTheme) {
@@ -399,7 +400,7 @@ export const useChangeThemeEffect = (
399
400
  }
400
401
  }
401
402
 
402
- if (!force && state.name === prev?.state.name) {
403
+ if (!force && state.name === prev?.state?.name) {
403
404
  return prev
404
405
  }
405
406
 
package/src/index.ts CHANGED
@@ -31,7 +31,6 @@ export * from './contexts/ComponentContext'
31
31
  export * from './helpers/createStyledContext'
32
32
  export * from './helpers/expandStyles'
33
33
  export * from './helpers/propMapper'
34
- export * from './helpers/getAnimationDriver'
35
34
  export * from './helpers/getExpandedShorthands'
36
35
  export * from './helpers/getSplitStyles'
37
36
  export * from './helpers/getStylesAtomic'
@@ -60,9 +59,10 @@ export {
60
59
  } from './hooks/useMedia'
61
60
  export * from './hooks/useTheme'
62
61
  export * from './hooks/useThemeName'
63
- export * from './hooks/useAnimationDriver'
62
+ export * from './hooks/useConfiguration'
64
63
  export * from './hooks/useIsTouchDevice'
65
64
  export * from './hooks/useProps'
65
+ export * from './hooks/useConfiguration'
66
66
 
67
67
  export * from './views/Slot'
68
68
  export * from './views/Stack'
@@ -72,7 +72,7 @@ export * from './views/Theme'
72
72
  export * from './views/ThemeProvider'
73
73
  export * from './views/FontLanguage'
74
74
  export * from './views/TamaguiProvider'
75
- export * from './views/AnimationDriverProvider'
75
+ export * from './views/Configuration'
76
76
 
77
77
  export * from '@tamagui/use-did-finish-ssr'
78
78
  export * from '@tamagui/use-event'
package/src/types.tsx CHANGED
@@ -180,6 +180,7 @@ export type ReactComponentWithRef<Props, Ref> = ForwardRefExoticComponent<
180
180
  >
181
181
 
182
182
  export type ComponentContextI = {
183
+ disableSSR?: boolean
183
184
  inText: boolean
184
185
  language: LanguageContextType | null
185
186
  animationDriver: AnimationDriver | null
@@ -413,12 +414,12 @@ type ConfProps<
413
414
  H extends DefaultFontSetting = DefaultFontSetting,
414
415
  I extends GenericTamaguiSettings = GenericTamaguiSettings
415
416
  > = {
416
- tokens: A
417
- themes: B
417
+ tokens?: A
418
+ themes?: B
418
419
  shorthands?: C
419
420
  media?: D
420
421
  animations?: AnimationDriver<E>
421
- fonts: F
422
+ fonts?: F
422
423
  onlyAllowShorthands?: G
423
424
  defaultFont?: H
424
425
  settings?: I
@@ -605,9 +606,9 @@ export type CreateTamaguiProps = {
605
606
  shorthands?: CreateShorthands
606
607
  media?: GenericTamaguiConfig['media']
607
608
  animations?: AnimationDriver<any>
608
- fonts: GenericTamaguiConfig['fonts']
609
- tokens: GenericTamaguiConfig['tokens']
610
- themes: {
609
+ fonts?: GenericTamaguiConfig['fonts']
610
+ tokens?: GenericTamaguiConfig['tokens']
611
+ themes?: {
611
612
  [key: string]: {
612
613
  [key: string]: string | number | Variable
613
614
  }
@@ -0,0 +1,11 @@
1
+ import { ComponentContext } from '../contexts/ComponentContext'
2
+ import type { AnimationDriver } from '../types'
3
+
4
+ interface ConfigurationProps {
5
+ animationDriver?: AnimationDriver | null
6
+ children: React.ReactNode
7
+ }
8
+
9
+ export const Configuration = (props: ConfigurationProps) => {
10
+ return <ComponentContext.Provider {...props} />
11
+ }