react-native-terra-ui 0.1.1 → 0.2.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 (262) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/lib/module/components/avatar/Avatar.js +118 -35
  3. package/lib/module/components/avatar/Avatar.js.map +1 -1
  4. package/lib/module/components/box/Box.js +15 -2
  5. package/lib/module/components/box/Box.js.map +1 -1
  6. package/lib/module/components/box/utils.js +4 -1
  7. package/lib/module/components/box/utils.js.map +1 -1
  8. package/lib/module/components/button/Button.js +7 -14
  9. package/lib/module/components/button/Button.js.map +1 -1
  10. package/lib/module/components/chip/Chip.js +372 -0
  11. package/lib/module/components/chip/Chip.js.map +1 -0
  12. package/lib/module/components/chip/index.js +4 -0
  13. package/lib/module/components/chip/index.js.map +1 -0
  14. package/lib/module/components/chip/types.js +2 -0
  15. package/lib/module/components/chip/types.js.map +1 -0
  16. package/lib/module/components/chip/utils.js +36 -0
  17. package/lib/module/components/chip/utils.js.map +1 -0
  18. package/lib/module/components/empty-state/EmptyState.js +200 -0
  19. package/lib/module/components/empty-state/EmptyState.js.map +1 -0
  20. package/lib/module/components/empty-state/index.js +4 -0
  21. package/lib/module/components/empty-state/index.js.map +1 -0
  22. package/lib/module/components/empty-state/types.js +2 -0
  23. package/lib/module/components/empty-state/types.js.map +1 -0
  24. package/lib/module/components/header/header-buttons.js +31 -36
  25. package/lib/module/components/header/header-buttons.js.map +1 -1
  26. package/lib/module/components/header/index.js +1 -1
  27. package/lib/module/components/header/index.js.map +1 -1
  28. package/lib/module/components/header/parts/HeaderGradientOverlay.js +38 -0
  29. package/lib/module/components/header/parts/HeaderGradientOverlay.js.map +1 -0
  30. package/lib/module/components/header/variants/LargeTitleHeader/index.js +69 -99
  31. package/lib/module/components/header/variants/LargeTitleHeader/index.js.map +1 -1
  32. package/lib/module/components/header/variants/TitleHeader/index.js +32 -63
  33. package/lib/module/components/header/variants/TitleHeader/index.js.map +1 -1
  34. package/lib/module/components/icon/Icon.js +20 -8
  35. package/lib/module/components/icon/Icon.js.map +1 -1
  36. package/lib/module/components/image/Image.js +54 -0
  37. package/lib/module/components/image/Image.js.map +1 -0
  38. package/lib/module/components/image/index.js +4 -0
  39. package/lib/module/components/image/index.js.map +1 -0
  40. package/lib/module/components/image/types.js +4 -0
  41. package/lib/module/components/image/types.js.map +1 -0
  42. package/lib/module/components/index.js +4 -0
  43. package/lib/module/components/index.js.map +1 -1
  44. package/lib/module/components/portal/Portal.js +42 -0
  45. package/lib/module/components/portal/Portal.js.map +1 -0
  46. package/lib/module/components/portal/PortalContext.js +1 -16
  47. package/lib/module/components/portal/PortalContext.js.map +1 -1
  48. package/lib/module/components/portal/PortalHost.js +16 -14
  49. package/lib/module/components/portal/PortalHost.js.map +1 -1
  50. package/lib/module/components/portal/PortalProvider.js +51 -19
  51. package/lib/module/components/portal/PortalProvider.js.map +1 -1
  52. package/lib/module/components/portal/index.js +1 -1
  53. package/lib/module/components/portal/index.js.map +1 -1
  54. package/lib/module/components/screen/Screen.js +25 -28
  55. package/lib/module/components/screen/Screen.js.map +1 -1
  56. package/lib/module/components/screen/ScreenContext.js +7 -5
  57. package/lib/module/components/screen/ScreenContext.js.map +1 -1
  58. package/lib/module/components/screen/ScreenFlashList.js +144 -0
  59. package/lib/module/components/screen/ScreenFlashList.js.map +1 -0
  60. package/lib/module/components/screen/ScreenFlatList.js +88 -27
  61. package/lib/module/components/screen/ScreenFlatList.js.map +1 -1
  62. package/lib/module/components/screen/ScreenScrollView.js +101 -34
  63. package/lib/module/components/screen/ScreenScrollView.js.map +1 -1
  64. package/lib/module/components/screen/index.js +1 -0
  65. package/lib/module/components/screen/index.js.map +1 -1
  66. package/lib/module/components/screen/parts/BottomSafeArea.js +62 -0
  67. package/lib/module/components/screen/parts/BottomSafeArea.js.map +1 -0
  68. package/lib/module/components/screen/parts/ScreenPlaceholder.js +50 -0
  69. package/lib/module/components/screen/parts/ScreenPlaceholder.js.map +1 -0
  70. package/lib/module/components/screen/types.js +4 -0
  71. package/lib/module/components/screen/types.js.map +1 -0
  72. package/lib/module/components/screen/utils.js +12 -9
  73. package/lib/module/components/screen/utils.js.map +1 -1
  74. package/lib/module/components/surface/Surface.js +16 -9
  75. package/lib/module/components/surface/Surface.js.map +1 -1
  76. package/lib/module/components/text/Text.js +23 -5
  77. package/lib/module/components/text/Text.js.map +1 -1
  78. package/lib/module/components/toast/Toast.js +8 -8
  79. package/lib/module/components/toast/Toast.js.map +1 -1
  80. package/lib/module/components/toolbar/Toolbar.js +268 -0
  81. package/lib/module/components/toolbar/Toolbar.js.map +1 -0
  82. package/lib/module/components/toolbar/index.js +4 -0
  83. package/lib/module/components/toolbar/index.js.map +1 -0
  84. package/lib/module/context/ThemeProvider.js +5 -2
  85. package/lib/module/context/ThemeProvider.js.map +1 -1
  86. package/lib/module/{components/icon/utils.js → theme/defaults/icons.js} +1 -1
  87. package/lib/module/theme/defaults/icons.js.map +1 -0
  88. package/lib/module/theme/defaults/image.js +30 -0
  89. package/lib/module/theme/defaults/image.js.map +1 -0
  90. package/lib/module/theme/index.js +1 -1
  91. package/lib/module/theme/{configure.js → registry.js} +21 -28
  92. package/lib/module/theme/registry.js.map +1 -0
  93. package/lib/module/theme/runtime.js +1 -1
  94. package/lib/module/theme/theme.js +91 -10
  95. package/lib/module/theme/theme.js.map +1 -1
  96. package/lib/module/theme/tokens/dark.js +59 -68
  97. package/lib/module/theme/tokens/dark.js.map +1 -1
  98. package/lib/module/theme/tokens/light.js +49 -67
  99. package/lib/module/theme/tokens/light.js.map +1 -1
  100. package/lib/module/theme/tokens/primitives.js +241 -20
  101. package/lib/module/theme/tokens/primitives.js.map +1 -1
  102. package/lib/module/utils/accent-utils.js +4 -4
  103. package/lib/module/utils/accent-utils.js.map +1 -1
  104. package/lib/typescript/src/components/avatar/Avatar.d.ts.map +1 -1
  105. package/lib/typescript/src/components/avatar/types.d.ts +4 -4
  106. package/lib/typescript/src/components/avatar/types.d.ts.map +1 -1
  107. package/lib/typescript/src/components/box/Box.d.ts.map +1 -1
  108. package/lib/typescript/src/components/box/utils.d.ts +6 -1
  109. package/lib/typescript/src/components/box/utils.d.ts.map +1 -1
  110. package/lib/typescript/src/components/button/Button.d.ts +1 -1
  111. package/lib/typescript/src/components/button/Button.d.ts.map +1 -1
  112. package/lib/typescript/src/components/chip/Chip.d.ts +24 -0
  113. package/lib/typescript/src/components/chip/Chip.d.ts.map +1 -0
  114. package/lib/typescript/src/components/chip/index.d.ts +4 -0
  115. package/lib/typescript/src/components/chip/index.d.ts.map +1 -0
  116. package/lib/typescript/src/components/chip/types.d.ts +8 -0
  117. package/lib/typescript/src/components/chip/types.d.ts.map +1 -0
  118. package/lib/typescript/src/components/chip/utils.d.ts +8 -0
  119. package/lib/typescript/src/components/chip/utils.d.ts.map +1 -0
  120. package/lib/typescript/src/components/empty-state/EmptyState.d.ts +45 -0
  121. package/lib/typescript/src/components/empty-state/EmptyState.d.ts.map +1 -0
  122. package/lib/typescript/src/components/empty-state/index.d.ts +4 -0
  123. package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -0
  124. package/lib/typescript/src/components/empty-state/types.d.ts +6 -0
  125. package/lib/typescript/src/components/empty-state/types.d.ts.map +1 -0
  126. package/lib/typescript/src/components/header/header-buttons.d.ts +27 -0
  127. package/lib/typescript/src/components/header/header-buttons.d.ts.map +1 -1
  128. package/lib/typescript/src/components/header/index.d.ts +1 -1
  129. package/lib/typescript/src/components/header/index.d.ts.map +1 -1
  130. package/lib/typescript/src/components/header/parts/HeaderGradientOverlay.d.ts +7 -0
  131. package/lib/typescript/src/components/header/parts/HeaderGradientOverlay.d.ts.map +1 -0
  132. package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts +1 -1
  133. package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts.map +1 -1
  134. package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts +18 -7
  135. package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts.map +1 -1
  136. package/lib/typescript/src/components/icon/Icon.d.ts.map +1 -1
  137. package/lib/typescript/src/components/image/Image.d.ts +16 -0
  138. package/lib/typescript/src/components/image/Image.d.ts.map +1 -0
  139. package/lib/typescript/src/components/image/index.d.ts +3 -0
  140. package/lib/typescript/src/components/image/index.d.ts.map +1 -0
  141. package/lib/typescript/src/components/image/types.d.ts +7 -0
  142. package/lib/typescript/src/components/image/types.d.ts.map +1 -0
  143. package/lib/typescript/src/components/index.d.ts +4 -0
  144. package/lib/typescript/src/components/index.d.ts.map +1 -1
  145. package/lib/typescript/src/components/portal/Portal.d.ts +26 -0
  146. package/lib/typescript/src/components/portal/Portal.d.ts.map +1 -0
  147. package/lib/typescript/src/components/portal/PortalContext.d.ts +11 -14
  148. package/lib/typescript/src/components/portal/PortalContext.d.ts.map +1 -1
  149. package/lib/typescript/src/components/portal/PortalHost.d.ts +9 -4
  150. package/lib/typescript/src/components/portal/PortalHost.d.ts.map +1 -1
  151. package/lib/typescript/src/components/portal/PortalProvider.d.ts +3 -3
  152. package/lib/typescript/src/components/portal/PortalProvider.d.ts.map +1 -1
  153. package/lib/typescript/src/components/portal/index.d.ts +4 -2
  154. package/lib/typescript/src/components/portal/index.d.ts.map +1 -1
  155. package/lib/typescript/src/components/screen/Screen.d.ts +18 -13
  156. package/lib/typescript/src/components/screen/Screen.d.ts.map +1 -1
  157. package/lib/typescript/src/components/screen/ScreenContext.d.ts +12 -9
  158. package/lib/typescript/src/components/screen/ScreenContext.d.ts.map +1 -1
  159. package/lib/typescript/src/components/screen/ScreenFlashList.d.ts +43 -0
  160. package/lib/typescript/src/components/screen/ScreenFlashList.d.ts.map +1 -0
  161. package/lib/typescript/src/components/screen/ScreenFlatList.d.ts +17 -7
  162. package/lib/typescript/src/components/screen/ScreenFlatList.d.ts.map +1 -1
  163. package/lib/typescript/src/components/screen/ScreenScrollView.d.ts +51 -16
  164. package/lib/typescript/src/components/screen/ScreenScrollView.d.ts.map +1 -1
  165. package/lib/typescript/src/components/screen/index.d.ts +3 -0
  166. package/lib/typescript/src/components/screen/index.d.ts.map +1 -1
  167. package/lib/typescript/src/components/screen/parts/BottomSafeArea.d.ts +30 -0
  168. package/lib/typescript/src/components/screen/parts/BottomSafeArea.d.ts.map +1 -0
  169. package/lib/typescript/src/components/screen/parts/ScreenPlaceholder.d.ts +22 -0
  170. package/lib/typescript/src/components/screen/parts/ScreenPlaceholder.d.ts.map +1 -0
  171. package/lib/typescript/src/components/screen/types.d.ts +42 -0
  172. package/lib/typescript/src/components/screen/types.d.ts.map +1 -0
  173. package/lib/typescript/src/components/screen/utils.d.ts +9 -9
  174. package/lib/typescript/src/components/screen/utils.d.ts.map +1 -1
  175. package/lib/typescript/src/components/surface/Surface.d.ts +7 -0
  176. package/lib/typescript/src/components/surface/Surface.d.ts.map +1 -1
  177. package/lib/typescript/src/components/text/Text.d.ts.map +1 -1
  178. package/lib/typescript/src/components/toolbar/Toolbar.d.ts +115 -0
  179. package/lib/typescript/src/components/toolbar/Toolbar.d.ts.map +1 -0
  180. package/lib/typescript/src/components/toolbar/index.d.ts +3 -0
  181. package/lib/typescript/src/components/toolbar/index.d.ts.map +1 -0
  182. package/lib/typescript/src/context/ThemeProvider.d.ts.map +1 -1
  183. package/lib/typescript/src/{components/icon/utils.d.ts → theme/defaults/icons.d.ts} +2 -2
  184. package/lib/typescript/src/theme/defaults/icons.d.ts.map +1 -0
  185. package/lib/typescript/src/theme/defaults/image.d.ts +4 -0
  186. package/lib/typescript/src/theme/defaults/image.d.ts.map +1 -0
  187. package/lib/typescript/src/theme/index.d.ts +1 -1
  188. package/lib/typescript/src/theme/index.d.ts.map +1 -1
  189. package/lib/typescript/src/theme/{configure.d.ts → registry.d.ts} +1 -1
  190. package/lib/typescript/src/theme/registry.d.ts.map +1 -0
  191. package/lib/typescript/src/theme/theme.d.ts +21 -1
  192. package/lib/typescript/src/theme/theme.d.ts.map +1 -1
  193. package/lib/typescript/src/theme/tokens/dark.d.ts +41 -64
  194. package/lib/typescript/src/theme/tokens/dark.d.ts.map +1 -1
  195. package/lib/typescript/src/theme/tokens/light.d.ts +44 -65
  196. package/lib/typescript/src/theme/tokens/light.d.ts.map +1 -1
  197. package/lib/typescript/src/theme/tokens/primitives.d.ts +228 -17
  198. package/lib/typescript/src/theme/tokens/primitives.d.ts.map +1 -1
  199. package/lib/typescript/src/theme/types.d.ts +65 -35
  200. package/lib/typescript/src/theme/types.d.ts.map +1 -1
  201. package/package.json +23 -9
  202. package/skills/terra-ui/SKILL.md +193 -0
  203. package/skills/terra-ui/references/recipes.md +198 -0
  204. package/skills/terra-ui/references/troubleshooting.md +112 -0
  205. package/src/components/avatar/Avatar.tsx +128 -38
  206. package/src/components/avatar/types.ts +5 -5
  207. package/src/components/box/Box.tsx +13 -1
  208. package/src/components/box/utils.ts +10 -0
  209. package/src/components/button/Button.tsx +7 -15
  210. package/src/components/chip/Chip.tsx +444 -0
  211. package/src/components/chip/index.ts +3 -0
  212. package/src/components/chip/types.ts +15 -0
  213. package/src/components/chip/utils.ts +34 -0
  214. package/src/components/empty-state/EmptyState.tsx +209 -0
  215. package/src/components/empty-state/index.ts +3 -0
  216. package/src/components/empty-state/types.ts +5 -0
  217. package/src/components/header/header-buttons.tsx +33 -45
  218. package/src/components/header/index.ts +2 -0
  219. package/src/components/header/parts/HeaderGradientOverlay.tsx +29 -0
  220. package/src/components/header/variants/LargeTitleHeader/index.tsx +68 -102
  221. package/src/components/header/variants/TitleHeader/index.tsx +47 -56
  222. package/src/components/icon/Icon.tsx +20 -15
  223. package/src/components/image/Image.tsx +48 -0
  224. package/src/components/image/index.ts +2 -0
  225. package/src/components/image/types.ts +7 -0
  226. package/src/components/index.ts +4 -0
  227. package/src/components/portal/Portal.tsx +55 -0
  228. package/src/components/portal/PortalContext.tsx +14 -22
  229. package/src/components/portal/PortalHost.tsx +17 -10
  230. package/src/components/portal/PortalProvider.tsx +58 -21
  231. package/src/components/portal/index.ts +4 -2
  232. package/src/components/screen/Screen.tsx +45 -59
  233. package/src/components/screen/ScreenContext.tsx +19 -12
  234. package/src/components/screen/ScreenFlashList.tsx +196 -0
  235. package/src/components/screen/ScreenFlatList.tsx +100 -39
  236. package/src/components/screen/ScreenScrollView.tsx +114 -53
  237. package/src/components/screen/index.ts +3 -0
  238. package/src/components/screen/parts/BottomSafeArea.tsx +60 -0
  239. package/src/components/screen/parts/ScreenPlaceholder.tsx +55 -0
  240. package/src/components/screen/types.ts +43 -0
  241. package/src/components/screen/utils.ts +29 -15
  242. package/src/components/surface/Surface.tsx +17 -20
  243. package/src/components/text/Text.tsx +23 -5
  244. package/src/components/toast/Toast.tsx +8 -8
  245. package/src/components/toolbar/Toolbar.tsx +353 -0
  246. package/src/components/toolbar/index.ts +6 -0
  247. package/src/context/ThemeProvider.tsx +5 -4
  248. package/src/{components/icon/utils.tsx → theme/defaults/icons.tsx} +1 -1
  249. package/src/theme/defaults/image.tsx +34 -0
  250. package/src/theme/index.ts +1 -1
  251. package/src/theme/{configure.ts → registry.ts} +19 -34
  252. package/src/theme/runtime.ts +1 -1
  253. package/src/theme/theme.ts +127 -13
  254. package/src/theme/tokens/dark.ts +60 -69
  255. package/src/theme/tokens/light.ts +50 -68
  256. package/src/theme/tokens/primitives.ts +248 -20
  257. package/src/theme/types.ts +67 -35
  258. package/src/utils/accent-utils.ts +4 -4
  259. package/lib/module/components/icon/utils.js.map +0 -1
  260. package/lib/module/theme/configure.js.map +0 -1
  261. package/lib/typescript/src/components/icon/utils.d.ts.map +0 -1
  262. package/lib/typescript/src/theme/configure.d.ts.map +0 -1
@@ -0,0 +1,112 @@
1
+ # Troubleshooting
2
+
3
+ Symptom → cause. Most Terra UI problems are setup problems, and most of them fail
4
+ **silently** rather than throwing.
5
+
6
+ ## Components render unstyled, or the app crashes on launch
7
+
8
+ Expo Go. Unistyles v3 requires native modules, so Expo Go cannot run it. Build a
9
+ development client:
10
+
11
+ ```sh
12
+ npx expo run:ios # or run:android
13
+ ```
14
+
15
+ ## Styles do not change when the theme changes
16
+
17
+ The Unistyles Babel plugin is not processing the library. Add it to `autoProcessImports`,
18
+ and keep the worklets plugin last:
19
+
20
+ ```js
21
+ plugins: [
22
+ ['react-native-unistyles/plugin', {
23
+ root: path.resolve(__dirname, 'src'),
24
+ autoProcessImports: ['react-native-terra-ui'],
25
+ }],
26
+ 'react-native-worklets/plugin', // MUST be last
27
+ ]
28
+ ```
29
+
30
+ Clear the cache after changing Babel config — `npx expo start --clear`.
31
+
32
+ The same symptom appears in **your own** components if you built the style object in the
33
+ component body from `useUnistyles()`. Move it into `StyleSheet.create`.
34
+
35
+ ## My theme config is ignored
36
+
37
+ Import order. Something imported `react-native-terra-ui` (the root entry) before your
38
+ `configureTerraUI()` call ran, so the auto-default won and later calls are no-ops.
39
+
40
+ - The bootstrap file must import from `react-native-terra-ui/theme` only.
41
+ - It must be the **first** import in your app entry, before `expo-router/entry` or your
42
+ `AppRegistry` registration.
43
+
44
+ There is no warning for this. Verify with `getIsConfigured()` from
45
+ `react-native-terra-ui/theme` before your call.
46
+
47
+ ## An icon renders nothing
48
+
49
+ The name is not registered. Only eight semantic names ship with the library
50
+ (`navigation.back/forward/close`, `status.info/success/warning/danger`, `person`); every
51
+ other name must be in **both** the `TerraUI.IconRegistry` declaration and the `icons` map
52
+ passed to `configureTerraUI`. A missing name logs a dev warning and renders `null` — the
53
+ layout still reserves space, so it looks like a blank gap.
54
+
55
+ ## Content sits under the header, or too far below it
56
+
57
+ Use a `Screen` scroll container (`Screen.ScrollView` / `.FlatList` / `.FlashList`) rather
58
+ than a plain `ScrollView`. They reserve the header height plus the top inset as content
59
+ padding. Do not add your own top padding on top of that.
60
+
61
+ If content lands too *low* on first paint in a native tab screen, `react-native-screens`
62
+ is also applying UIKit's automatic content inset. Opt out at the navigator — on an
63
+ expo-router native tab that is `disableAutomaticContentInsets` on the trigger.
64
+
65
+ ## `Header.LargeTitle` does not collapse
66
+
67
+ It reads scroll position from the `Screen` context, so it needs both halves:
68
+
69
+ - rendered through `Screen.Header` — `<Screen.Header as={Header.LargeTitle} title="…" />`
70
+ (or nested inside it, the older form), as a direct child of `Screen`, and
71
+ - paired with `Screen.ScrollView` / `.FlatList` / `.FlashList` in the same `Screen`.
72
+
73
+ A plain `ScrollView` sibling will not drive it.
74
+
75
+ ## `useScreen()` / toast hooks throw
76
+
77
+ They require their provider. `useScreen()` must be under a `Screen`; `useToast()` and
78
+ `Toast.show()` need `ToastProvider` mounted (it is **not** included in `TerraUIProvider`).
79
+
80
+ ## A token prop does not work
81
+
82
+ Token style props (`p`, `gap`, `bg`, `radius`, `row`, …) exist on `Box` and `Surface`
83
+ only. Every other component takes explicit props — read its types.
84
+
85
+ ## A color token has no effect
86
+
87
+ Colors are flat dotted keys, not nested objects:
88
+
89
+ ```ts
90
+ theme.color['surface.default'] // ✅
91
+ theme.color.surface.default // ✗ undefined
92
+ theme.color.background // ✅ the one single-segment token
93
+ ```
94
+
95
+ An unknown key yields `undefined`, which React Native ignores silently.
96
+
97
+ ## My `Surface` has a radius or shadow I did not ask for
98
+
99
+ `Surface` reads `radius` and `elevation` from `configureTerraUI({ components: { surface } })`.
100
+ That is the point of it — but if you want a neutral container, use `Box`.
101
+
102
+ ## Types are missing or stale after upgrading
103
+
104
+ The package ships declarations at `lib/typescript`. Reinstall, and restart the TS server in
105
+ your editor. If you use the `"source"` export condition (monorepo/Metro), you are compiling
106
+ the library's TypeScript directly, so its own type errors surface in your build.
107
+
108
+ ## Something looks wrong and none of the above applies
109
+
110
+ Read the component's source — it ships in the package under
111
+ `node_modules/react-native-terra-ui/src/components/<name>/`. The JSDoc on each prop states
112
+ its default, and it is the version you actually have installed.
@@ -1,19 +1,13 @@
1
1
  import { useState } from 'react';
2
2
  import { Text, View } from 'react-native';
3
3
 
4
- import { useUnistyles } from 'react-native-unistyles';
4
+ import { StyleSheet, useUnistyles } from 'react-native-unistyles';
5
5
 
6
- import { getImageComponent } from '#theme/configure';
7
- import type { TerraTheme } from '#theme/types';
6
+ import type { RadiusKey } from '#theme/types';
8
7
 
9
8
  import { Icon } from '../icon';
10
- import type {
11
- AvatarColor,
12
- AvatarProps,
13
- AvatarShape,
14
- AvatarSize,
15
- AvatarVariant,
16
- } from './types';
9
+ import { Image } from '../image';
10
+ import type { AvatarProps, AvatarShape, AvatarSize } from './types';
17
11
 
18
12
  const SIZE_PX: Record<AvatarSize, number> = {
19
13
  xs: 24,
@@ -48,28 +42,14 @@ function getBorderRadius(
48
42
  return 0;
49
43
  }
50
44
 
51
- function resolveAvatarColors(
52
- color: AvatarColor,
53
- variant: AvatarVariant,
54
- theme: TerraTheme
55
- ): { bg: string; fg: string } {
56
- const c = theme.color;
57
- const get = (key: string): string =>
58
- (c as unknown as Record<string, string | undefined>)[key] ?? '';
59
- if (color === 'default') {
60
- return { bg: c['surface.sunken'], fg: c['text.muted'] };
61
- }
62
- if (color === 'accent') {
63
- return variant === 'soft'
64
- ? { bg: c['action.bg.subtle'], fg: c['action.fg.subtle'] }
65
- : { bg: c['action.bg.primary'], fg: c['action.fg.primary'] };
66
- }
67
- return variant === 'soft'
68
- ? {
69
- bg: get(`status.bg.${color}.subtle`),
70
- fg: get(`status.fg.${color}.subtle`),
71
- }
72
- : { bg: get(`status.bg.${color}`), fg: get(`status.fg.${color}`) };
45
+ // `Image` clips its own content to a `RadiusKey` theme token, independent of
46
+ // Avatar's own container radius — this keeps the two in sync per `shape`.
47
+ // `'full'` (theme.radius.full) clamps to a circle at any size, same as the
48
+ // container's computed `sizePx / 2`.
49
+ function getImageRadius(shape: AvatarShape): RadiusKey {
50
+ if (shape === 'circle') return 'full';
51
+ if (shape === 'rounded') return 'md';
52
+ return 'none';
73
53
  }
74
54
 
75
55
  export function Avatar({
@@ -79,19 +59,21 @@ export function Avatar({
79
59
  name,
80
60
  fallback,
81
61
  contentFit = 'cover',
82
- color = 'default',
83
- variant = 'default',
62
+ color = 'secondary',
63
+ variant = 'solid',
84
64
  style,
85
65
  ...rest
86
66
  }: AvatarProps) {
87
67
  const { theme } = useUnistyles();
88
68
  const [hasError, setHasError] = useState(false);
69
+ styles.useVariants({ color, variant });
89
70
 
90
71
  const sizePx = SIZE_PX[size];
91
72
  const borderRadius = getBorderRadius(shape, sizePx, theme.radius.md);
92
73
  const showFallback = !source || hasError;
93
74
 
94
- const { bg, fg } = resolveAvatarColors(color, variant, theme);
75
+ const bg = styles.base.backgroundColor ?? '';
76
+ const fg = styles.base.color ?? '';
95
77
 
96
78
  const fallbackContent =
97
79
  fallback !== undefined ? (
@@ -112,8 +94,6 @@ export function Avatar({
112
94
  <Icon name="person" size={sizePx} color={fg} />
113
95
  );
114
96
 
115
- const ImageComponent = getImageComponent();
116
-
117
97
  return (
118
98
  <View
119
99
  {...rest}
@@ -133,9 +113,10 @@ export function Avatar({
133
113
  {showFallback ? (
134
114
  fallbackContent
135
115
  ) : (
136
- <ImageComponent
116
+ <Image
137
117
  source={source}
138
118
  style={{ width: sizePx, height: sizePx }}
119
+ radius={getImageRadius(shape)}
139
120
  contentFit={contentFit}
140
121
  accessibilityLabel={name}
141
122
  onError={() => setHasError(true)}
@@ -144,3 +125,112 @@ export function Avatar({
144
125
  </View>
145
126
  );
146
127
  }
128
+
129
+ // ─── Styles ───────────────────────────────────────────────────────────────────
130
+
131
+ // One compound variant per (color, variant) cell of the spec's token table —
132
+ // `variants` below only registers the axis names/options for the type
133
+ // system; the actual bg/fg values all come from `compoundVariants`.
134
+ const styles = StyleSheet.create((theme) => {
135
+ const c = theme.color;
136
+
137
+ return {
138
+ base: {
139
+ variants: {
140
+ color: {
141
+ secondary: {},
142
+ primary: {},
143
+ success: {},
144
+ warning: {},
145
+ danger: {},
146
+ },
147
+ variant: {
148
+ solid: {},
149
+ soft: {},
150
+ },
151
+ },
152
+ compoundVariants: [
153
+ {
154
+ color: 'secondary',
155
+ variant: 'solid',
156
+ styles: {
157
+ backgroundColor: c['action.bg.secondary'],
158
+ color: c['action.fg.secondary'],
159
+ },
160
+ },
161
+ {
162
+ color: 'secondary',
163
+ variant: 'soft',
164
+ styles: {
165
+ backgroundColor: c['action.bg.secondary.subtle'],
166
+ color: c['action.fg.secondary.subtle'],
167
+ },
168
+ },
169
+ {
170
+ color: 'primary',
171
+ variant: 'solid',
172
+ styles: {
173
+ backgroundColor: c['action.bg.primary'],
174
+ color: c['action.fg.primary'],
175
+ },
176
+ },
177
+ {
178
+ color: 'primary',
179
+ variant: 'soft',
180
+ styles: {
181
+ backgroundColor: c['action.bg.primary.subtle'],
182
+ color: c['action.fg.primary.subtle'],
183
+ },
184
+ },
185
+ {
186
+ color: 'success',
187
+ variant: 'solid',
188
+ styles: {
189
+ backgroundColor: c['status.success.bg'],
190
+ color: c['status.success.fg'],
191
+ },
192
+ },
193
+ {
194
+ color: 'success',
195
+ variant: 'soft',
196
+ styles: {
197
+ backgroundColor: c['status.success.bg.subtle'],
198
+ color: c['status.success.fg.subtle'],
199
+ },
200
+ },
201
+ {
202
+ color: 'warning',
203
+ variant: 'solid',
204
+ styles: {
205
+ backgroundColor: c['status.warning.bg'],
206
+ color: c['status.warning.fg'],
207
+ },
208
+ },
209
+ {
210
+ color: 'warning',
211
+ variant: 'soft',
212
+ styles: {
213
+ backgroundColor: c['status.warning.bg.subtle'],
214
+ color: c['status.warning.fg.subtle'],
215
+ },
216
+ },
217
+ {
218
+ color: 'danger',
219
+ variant: 'solid',
220
+ styles: {
221
+ backgroundColor: c['status.danger.bg'],
222
+ color: c['status.danger.fg'],
223
+ },
224
+ },
225
+ {
226
+ color: 'danger',
227
+ variant: 'soft',
228
+ styles: {
229
+ backgroundColor: c['status.danger.bg.subtle'],
230
+ color: c['status.danger.fg.subtle'],
231
+ },
232
+ },
233
+ ],
234
+ },
235
+ };
236
+ });
@@ -7,13 +7,13 @@ export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
7
7
  export type AvatarShape = 'circle' | 'rounded' | 'square';
8
8
  /** Semantic color scheme for the fallback state (initials / placeholder icon). */
9
9
  export type AvatarColor =
10
- | 'default'
11
- | 'accent'
10
+ | 'secondary'
11
+ | 'primary'
12
12
  | 'success'
13
13
  | 'warning'
14
14
  | 'danger';
15
15
  /** Visual weight of the fallback background. */
16
- export type AvatarVariant = 'default' | 'soft';
16
+ export type AvatarVariant = 'solid' | 'soft';
17
17
 
18
18
  export interface AvatarProps extends ViewProps {
19
19
  /** Predefined size token. Defaults to `'md'`. */
@@ -28,8 +28,8 @@ export interface AvatarProps extends ViewProps {
28
28
  fallback?: ReactNode;
29
29
  /** How the image fills its container. Defaults to `'cover'`. */
30
30
  contentFit?: TerraImageContentFit;
31
- /** Fallback color theme. Defaults to `'default'`. */
31
+ /** Fallback color theme. Defaults to `'secondary'`. */
32
32
  color?: AvatarColor;
33
- /** Visual style variant. Defaults to `'default'`. */
33
+ /** Visual style variant. Defaults to `'solid'`. */
34
34
  variant?: AvatarVariant;
35
35
  }
@@ -1,7 +1,7 @@
1
1
  import { type ComponentRef, forwardRef } from 'react';
2
2
  import { View, type ViewProps } from 'react-native';
3
3
 
4
- import { useUnistyles } from 'react-native-unistyles';
4
+ import { StyleSheet, useUnistyles } from 'react-native-unistyles';
5
5
 
6
6
  import { Slot } from '../slot';
7
7
  import {
@@ -36,6 +36,7 @@ export const Box = forwardRef<ComponentRef<typeof View>, BoxProps>(function Box(
36
36
  }
37
37
 
38
38
  const mergedStyle = [
39
+ styles.root,
39
40
  resolveBoxStyle(styleProps as BoxStyleProps, theme),
40
41
  style,
41
42
  ];
@@ -57,3 +58,14 @@ export const Box = forwardRef<ComponentRef<typeof View>, BoxProps>(function Box(
57
58
  </View>
58
59
  );
59
60
  });
61
+
62
+ // ─── Styles ───────────────────────────────────────────────────────────────────
63
+ //
64
+ // Box has no static style of its own — this stylesheet exists so the
65
+ // Unistyles Babel plugin swaps the `View` imported above for its Unistyles-aware
66
+ // counterpart, which registers the ShadowNode with the native side. That is what
67
+ // keeps a Unistyles style passed in through `style` updating on theme change;
68
+ // without it the style freezes at whichever theme was active when it was
69
+ // created, and only callers that happen to subscribe to the theme themselves
70
+ // would appear to work.
71
+ const styles = StyleSheet.create(() => ({ root: {} }));
@@ -1,6 +1,7 @@
1
1
  import { StyleSheet, type ViewStyle } from 'react-native';
2
2
  import type {
3
3
  ColorToken,
4
+ ElevationKey,
4
5
  RadiusKey,
5
6
  SpacingKey,
6
7
  TerraTheme,
@@ -51,6 +52,11 @@ export interface BoxStyleProps {
51
52
  radius?: RadiusKey;
52
53
  borderWidth?: BorderWidthInput;
53
54
  borderColor?: ColorToken;
55
+ /**
56
+ * Drop-shadow depth — platform-aware shadow plus Android elevation. A shadow
57
+ * only renders over a non-transparent `bg`.
58
+ */
59
+ elevation?: ElevationKey;
54
60
  }
55
61
 
56
62
  const ALIGN_MAP: Record<Align, NonNullable<ViewStyle['alignItems']>> = {
@@ -147,6 +153,9 @@ export function resolveBoxStyle(
147
153
  const borderColor = resolveThemeColor(props.borderColor, theme);
148
154
  if (borderColor !== undefined) style.borderColor = borderColor;
149
155
  }
156
+ if (props.elevation !== undefined) {
157
+ Object.assign(style, theme.elevation[props.elevation]);
158
+ }
150
159
 
151
160
  return style;
152
161
  }
@@ -178,4 +187,5 @@ export const BOX_STYLE_PROP_KEYS: readonly (keyof BoxStyleProps)[] = [
178
187
  'radius',
179
188
  'borderWidth',
180
189
  'borderColor',
190
+ 'elevation',
181
191
  ];
@@ -11,7 +11,7 @@ import { Pressable } from 'react-native';
11
11
 
12
12
  import { StyleSheet, useUnistyles } from 'react-native-unistyles';
13
13
 
14
- import { getDefaultRadius } from '#theme/configure';
14
+ import { getDefaultRadius } from '#theme/registry';
15
15
  import type {
16
16
  ColorToken,
17
17
  FontWeightToken,
@@ -27,7 +27,6 @@ import { Text } from '../text';
27
27
  export type ButtonVariant =
28
28
  | 'primary'
29
29
  | 'secondary'
30
- | 'neutral'
31
30
  | 'outline'
32
31
  | 'ghost'
33
32
  | 'danger';
@@ -90,17 +89,10 @@ function getColors(variant: ButtonVariant, theme: TerraTheme): ButtonColors {
90
89
  };
91
90
  case 'secondary':
92
91
  return {
93
- bg: get('action.bg.subtle'),
94
- border: get('action.bg.subtle'),
92
+ bg: get('action.bg.secondary'),
93
+ border: get('action.bg.secondary'),
95
94
  borderWidth: 0,
96
- fg: get('action.fg.subtle'),
97
- };
98
- case 'neutral':
99
- return {
100
- bg: get('action.bg.neutral.hover'),
101
- border: get('action.bg.neutral.hover'),
102
- borderWidth: 0,
103
- fg: get('action.fg.neutral'),
95
+ fg: get('action.fg.secondary'),
104
96
  };
105
97
  case 'outline':
106
98
  return {
@@ -118,10 +110,10 @@ function getColors(variant: ButtonVariant, theme: TerraTheme): ButtonColors {
118
110
  };
119
111
  case 'danger':
120
112
  return {
121
- bg: get('status.bg.danger'),
122
- border: get('status.bg.danger'),
113
+ bg: get('status.danger.bg'),
114
+ border: get('status.danger.bg'),
123
115
  borderWidth: 0,
124
- fg: get('status.fg.danger'),
116
+ fg: get('status.danger.fg'),
125
117
  };
126
118
  }
127
119
  }