@titan-design/react-ui 0.9.1 → 0.9.3

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.
@@ -1 +1,13 @@
1
1
  export { Surface, type SurfaceProps } from './Surface'
2
+ export {
3
+ SurfaceContext,
4
+ useSurface,
5
+ useSurfaceMode,
6
+ useOnSurfaceColor,
7
+ onSurfaceColors,
8
+ surfaceBackground,
9
+ SURFACE_LEVEL_TOKEN,
10
+ type SurfaceLevel,
11
+ type OnSurfaceRole,
12
+ type SurfaceContextValue,
13
+ } from './SurfaceContext'
@@ -11,3 +11,16 @@
11
11
  export function vars(values: Record<string, string>): Record<string, string> {
12
12
  return values
13
13
  }
14
+
15
+ /**
16
+ * Stand-in for nativewind's `useColorScheme()`. Under the web test env there is
17
+ * no nativewind theme applied, so it reports `undefined` (the "no explicit
18
+ * scheme" signal) — matching ThemeProvider's dark fallback for `mode="system"`.
19
+ */
20
+ export function useColorScheme(): {
21
+ colorScheme: 'light' | 'dark' | undefined
22
+ setColorScheme: (scheme: 'light' | 'dark' | 'system') => void
23
+ toggleColorScheme: () => void
24
+ } {
25
+ return { colorScheme: undefined, setColorScheme: () => {}, toggleColorScheme: () => {} }
26
+ }
@@ -2,6 +2,12 @@ import { describe, it, expect } from 'vitest'
2
2
  import { render, screen } from '@testing-library/react'
3
3
  import { Text } from 'react-native'
4
4
  import { ThemeProvider } from './ThemeProvider'
5
+ import { useSurfaceMode } from '../components/ui/surface'
6
+
7
+ // Reports the surface mode ThemeProvider seeds into the on-surface context.
8
+ function ModeProbe() {
9
+ return <Text>{useSurfaceMode()}</Text>
10
+ }
5
11
 
6
12
  describe('ThemeProvider', () => {
7
13
  it('renders its children', () => {
@@ -21,4 +27,22 @@ describe('ThemeProvider', () => {
21
27
  )
22
28
  expect(screen.getByText('light')).toBeInTheDocument()
23
29
  })
30
+
31
+ it('seeds the on-surface context with an explicit mode', () => {
32
+ render(
33
+ <ThemeProvider mode="light">
34
+ <ModeProbe />
35
+ </ThemeProvider>,
36
+ )
37
+ expect(screen.getByText('light')).toBeInTheDocument()
38
+ })
39
+
40
+ it('falls back to dark for mode="system" when no scheme is applied', () => {
41
+ render(
42
+ <ThemeProvider mode="system">
43
+ <ModeProbe />
44
+ </ThemeProvider>,
45
+ )
46
+ expect(screen.getByText('dark')).toBeInTheDocument()
47
+ })
24
48
  })
@@ -13,11 +13,16 @@
13
13
  //
14
14
  // The var maps are the SAME canonical `darkThemeCSSVars` / `lightThemeCSSVars`
15
15
  // that generate `global.css`, so native and web resolve to identical values.
16
- import { vars } from 'nativewind'
16
+ //
17
+ // It also seeds the on-surface colour context (`<Surface>`'s mode), so titan's
18
+ // JS-resolved on-surface colours track the same theme as the className tokens.
19
+ import { vars, useColorScheme } from 'nativewind'
17
20
  import { View, type ViewProps } from 'react-native'
21
+ import { SurfaceContext } from '../components/ui/surface/SurfaceContext'
18
22
  import { darkThemeCSSVars, lightThemeCSSVars } from './config'
19
23
 
20
- export type ThemeProviderMode = 'dark' | 'light'
24
+ /** `'system'` follows nativewind's `useColorScheme()` (mobile light/dark flip). */
25
+ export type ThemeProviderMode = 'dark' | 'light' | 'system'
21
26
 
22
27
  // Precomputed once — `vars()` is pure, so the same style object is reused across
23
28
  // renders. Native == web because these are the maps `tokens-css` emits.
@@ -27,7 +32,11 @@ const MODE_VARS = {
27
32
  }
28
33
 
29
34
  export interface ThemeProviderProps extends ViewProps {
30
- /** Theme mode to register. Mobile is dark-only today; defaults to `dark`. */
35
+ /**
36
+ * Theme mode to register. `'system'` bridges nativewind's `useColorScheme()`
37
+ * so mobile follows the OS light/dark flip; `'dark'`/`'light'` pin it. Mobile
38
+ * is dark-only today, so this defaults to `dark`.
39
+ */
31
40
  mode?: ThemeProviderMode
32
41
  }
33
42
 
@@ -42,9 +51,17 @@ export function ThemeProvider({
42
51
  children,
43
52
  ...props
44
53
  }: ThemeProviderProps): React.JSX.Element {
54
+ // `'system'` → follow nativewind's applied scheme, falling back to dark when
55
+ // no scheme is set. `useColorScheme` runs unconditionally (rules of hooks);
56
+ // its result is only used in the `'system'` branch.
57
+ const { colorScheme } = useColorScheme()
58
+ const resolved = mode === 'system' ? (colorScheme ?? 'dark') : mode
59
+
45
60
  return (
46
- <View style={[MODE_VARS[mode], { flex: 1 }, style]} {...props}>
47
- {children}
48
- </View>
61
+ <SurfaceContext.Provider value={{ mode: resolved, level: 'base' }}>
62
+ <View style={[MODE_VARS[resolved], { flex: 1 }, style]} {...props}>
63
+ {children}
64
+ </View>
65
+ </SurfaceContext.Provider>
49
66
  )
50
67
  }