@expokit/cli 0.0.0-stage → 0.3.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 (312) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +46 -2
  3. package/bin/expo-kit.js +1266 -0
  4. package/bin/lib.mjs +343 -0
  5. package/bin/mcp.mjs +345 -0
  6. package/package.json +49 -4
  7. package/registry/_docs/accordion.json +115 -0
  8. package/registry/_docs/action-sheet.json +138 -0
  9. package/registry/_docs/alert.json +155 -0
  10. package/registry/_docs/avatar.json +227 -0
  11. package/registry/_docs/badge.json +148 -0
  12. package/registry/_docs/bottom-nav.json +143 -0
  13. package/registry/_docs/bottom-sheet.json +186 -0
  14. package/registry/_docs/bullet.json +27 -0
  15. package/registry/_docs/button-3d.json +138 -0
  16. package/registry/_docs/button.json +131 -0
  17. package/registry/_docs/calendar.json +198 -0
  18. package/registry/_docs/card-stack.json +124 -0
  19. package/registry/_docs/card.json +131 -0
  20. package/registry/_docs/carousel.json +122 -0
  21. package/registry/_docs/chat.json +184 -0
  22. package/registry/_docs/checkbox.json +109 -0
  23. package/registry/_docs/checkout.json +105 -0
  24. package/registry/_docs/chip-group.json +132 -0
  25. package/registry/_docs/color-swatch.json +82 -0
  26. package/registry/_docs/confetti.json +102 -0
  27. package/registry/_docs/coupon.json +192 -0
  28. package/registry/_docs/cube-pager.json +100 -0
  29. package/registry/_docs/dashboard.json +123 -0
  30. package/registry/_docs/date-picker.json +269 -0
  31. package/registry/_docs/dialog.json +186 -0
  32. package/registry/_docs/drawer.json +199 -0
  33. package/registry/_docs/fab.json +144 -0
  34. package/registry/_docs/flip-card.json +127 -0
  35. package/registry/_docs/floating-input.json +211 -0
  36. package/registry/_docs/gift-card.json +124 -0
  37. package/registry/_docs/glass-button.json +142 -0
  38. package/registry/_docs/glass-nav-bar.json +102 -0
  39. package/registry/_docs/glass-search-bar.json +114 -0
  40. package/registry/_docs/glass-segmented.json +94 -0
  41. package/registry/_docs/glass-sheet.json +136 -0
  42. package/registry/_docs/glass-tab-bar.json +100 -0
  43. package/registry/_docs/glass.json +94 -0
  44. package/registry/_docs/inbox.json +75 -0
  45. package/registry/_docs/isometric-bar.json +75 -0
  46. package/registry/_docs/kit.json +14 -0
  47. package/registry/_docs/kpi-card.json +14 -0
  48. package/registry/_docs/like-button.json +92 -0
  49. package/registry/_docs/liquidglass-tab.json +30 -0
  50. package/registry/_docs/list-item.json +216 -0
  51. package/registry/_docs/loyalty-card.json +124 -0
  52. package/registry/_docs/morph-button.json +128 -0
  53. package/registry/_docs/number-ticker.json +112 -0
  54. package/registry/_docs/offers.json +81 -0
  55. package/registry/_docs/onboarding.json +69 -0
  56. package/registry/_docs/otp-input.json +158 -0
  57. package/registry/_docs/pagination-dots.json +108 -0
  58. package/registry/_docs/parallax-layers.json +104 -0
  59. package/registry/_docs/parallax-scroll.json +210 -0
  60. package/registry/_docs/password-input.json +112 -0
  61. package/registry/_docs/payment-card.json +169 -0
  62. package/registry/_docs/pixel-bar.json +69 -0
  63. package/registry/_docs/polar-area.json +81 -0
  64. package/registry/_docs/profile.json +87 -0
  65. package/registry/_docs/progress-steps.json +116 -0
  66. package/registry/_docs/progress.json +152 -0
  67. package/registry/_docs/radio-group.json +112 -0
  68. package/registry/_docs/rating.json +114 -0
  69. package/registry/_docs/ring-carousel.json +122 -0
  70. package/registry/_docs/scratch-card.json +110 -0
  71. package/registry/_docs/search-bar.json +160 -0
  72. package/registry/_docs/search-results.json +123 -0
  73. package/registry/_docs/segmented-control.json +123 -0
  74. package/registry/_docs/select.json +277 -0
  75. package/registry/_docs/settings.json +75 -0
  76. package/registry/_docs/shimmer-button.json +98 -0
  77. package/registry/_docs/sign-in.json +201 -0
  78. package/registry/_docs/skeleton.json +162 -0
  79. package/registry/_docs/slide-to-confirm.json +108 -0
  80. package/registry/_docs/slider.json +177 -0
  81. package/registry/_docs/spin-wheel.json +138 -0
  82. package/registry/_docs/spinner.json +121 -0
  83. package/registry/_docs/stateful-button.json +184 -0
  84. package/registry/_docs/stepper.json +184 -0
  85. package/registry/_docs/swipe-deck.json +118 -0
  86. package/registry/_docs/swipeable-row.json +95 -0
  87. package/registry/_docs/switch.json +124 -0
  88. package/registry/_docs/tabs.json +129 -0
  89. package/registry/_docs/tag-input.json +148 -0
  90. package/registry/_docs/text-animation.json +141 -0
  91. package/registry/_docs/textarea.json +191 -0
  92. package/registry/_docs/ticket-card.json +194 -0
  93. package/registry/_docs/ticket-shape.json +14 -0
  94. package/registry/_docs/tilt-card.json +157 -0
  95. package/registry/_docs/time-picker.json +269 -0
  96. package/registry/_docs/timeline-chart.json +33 -0
  97. package/registry/_docs/timeline.json +172 -0
  98. package/registry/_docs/toast.json +185 -0
  99. package/registry/_docs/treemap.json +33 -0
  100. package/registry/_docs/waterfall.json +39 -0
  101. package/registry/_docs/wheel-list.json +122 -0
  102. package/registry/accordion/files/src/components/ui/accordion.tsx +233 -0
  103. package/registry/accordion/files/src/lib/kit.tsx +219 -0
  104. package/registry/accordion.json +17 -0
  105. package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +580 -0
  106. package/registry/action-sheet/files/src/lib/kit.tsx +219 -0
  107. package/registry/action-sheet.json +18 -0
  108. package/registry/alert/files/src/components/ui/alert.tsx +654 -0
  109. package/registry/alert/files/src/components/ui/button.tsx +274 -0
  110. package/registry/alert/files/src/lib/kit.tsx +219 -0
  111. package/registry/alert.json +20 -0
  112. package/registry/avatar/files/src/components/ui/avatar.tsx +790 -0
  113. package/registry/avatar/files/src/lib/kit.tsx +219 -0
  114. package/registry/avatar.json +18 -0
  115. package/registry/badge/files/src/components/ui/badge.tsx +626 -0
  116. package/registry/badge/files/src/lib/kit.tsx +219 -0
  117. package/registry/badge.json +17 -0
  118. package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +1050 -0
  119. package/registry/bottom-nav/files/src/lib/kit.tsx +219 -0
  120. package/registry/bottom-nav.json +18 -0
  121. package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +585 -0
  122. package/registry/bottom-sheet/files/src/lib/kit.tsx +219 -0
  123. package/registry/bottom-sheet.json +17 -0
  124. package/registry/bullet.json +14 -0
  125. package/registry/button/files/src/components/ui/button.tsx +274 -0
  126. package/registry/button/files/src/lib/kit.tsx +219 -0
  127. package/registry/button-3d/files/src/components/ui/button-3d.tsx +212 -0
  128. package/registry/button-3d/files/src/lib/kit.tsx +219 -0
  129. package/registry/button-3d.json +18 -0
  130. package/registry/button.json +18 -0
  131. package/registry/calendar/files/src/components/ui/calendar.tsx +822 -0
  132. package/registry/calendar/files/src/lib/kit.tsx +219 -0
  133. package/registry/calendar.json +17 -0
  134. package/registry/card/files/src/components/ui/card.tsx +300 -0
  135. package/registry/card/files/src/lib/kit.tsx +219 -0
  136. package/registry/card-stack.json +15 -0
  137. package/registry/card.json +17 -0
  138. package/registry/carousel.json +15 -0
  139. package/registry/chat.json +18 -0
  140. package/registry/checkbox/files/src/components/ui/checkbox.tsx +150 -0
  141. package/registry/checkbox/files/src/lib/kit.tsx +219 -0
  142. package/registry/checkbox.json +17 -0
  143. package/registry/checkout.json +26 -0
  144. package/registry/chip-group/files/src/components/ui/chip-group.tsx +269 -0
  145. package/registry/chip-group/files/src/lib/kit.tsx +219 -0
  146. package/registry/chip-group.json +17 -0
  147. package/registry/color-swatch.json +17 -0
  148. package/registry/confetti/files/src/components/ui/confetti.tsx +222 -0
  149. package/registry/confetti/files/src/lib/kit.tsx +219 -0
  150. package/registry/confetti.json +15 -0
  151. package/registry/coupon/files/src/components/ui/coupon.tsx +698 -0
  152. package/registry/coupon/files/src/components/ui/flip-card.tsx +205 -0
  153. package/registry/coupon/files/src/components/ui/tilt-card.tsx +635 -0
  154. package/registry/coupon/files/src/lib/kit.tsx +219 -0
  155. package/registry/coupon/files/src/lib/ticket-shape.tsx +543 -0
  156. package/registry/coupon.json +23 -0
  157. package/registry/cube-pager.json +15 -0
  158. package/registry/dashboard.json +32 -0
  159. package/registry/date-picker/files/src/components/ui/calendar.tsx +822 -0
  160. package/registry/date-picker/files/src/components/ui/date-picker.tsx +796 -0
  161. package/registry/date-picker/files/src/lib/kit.tsx +219 -0
  162. package/registry/date-picker.json +19 -0
  163. package/registry/dialog/files/src/components/ui/dialog.tsx +759 -0
  164. package/registry/dialog/files/src/lib/kit.tsx +219 -0
  165. package/registry/dialog.json +18 -0
  166. package/registry/drawer/files/src/components/ui/drawer.tsx +1135 -0
  167. package/registry/drawer/files/src/lib/kit.tsx +219 -0
  168. package/registry/drawer.json +18 -0
  169. package/registry/fab.json +18 -0
  170. package/registry/flip-card/files/src/components/ui/flip-card.tsx +205 -0
  171. package/registry/flip-card/files/src/lib/kit.tsx +219 -0
  172. package/registry/flip-card.json +15 -0
  173. package/registry/floating-input/files/src/components/ui/floating-input.tsx +528 -0
  174. package/registry/floating-input/files/src/lib/kit.tsx +219 -0
  175. package/registry/floating-input.json +17 -0
  176. package/registry/gift-card.json +21 -0
  177. package/registry/glass-button.json +22 -0
  178. package/registry/glass-nav-bar.json +21 -0
  179. package/registry/glass-search-bar.json +21 -0
  180. package/registry/glass-segmented.json +22 -0
  181. package/registry/glass-sheet.json +22 -0
  182. package/registry/glass-tab-bar.json +22 -0
  183. package/registry/glass.json +19 -0
  184. package/registry/inbox.json +30 -0
  185. package/registry/isometric-bar.json +14 -0
  186. package/registry/kit/files/src/lib/kit.tsx +219 -0
  187. package/registry/kit.json +11 -0
  188. package/registry/kpi-card/files/src/components/charts/kpi-card.tsx +45 -0
  189. package/registry/kpi-card.json +14 -0
  190. package/registry/like-button.json +18 -0
  191. package/registry/liquidglass-tab/files/src/app/(tabs)/_layout.tsx +97 -0
  192. package/registry/liquidglass-tab/files/src/app/(tabs)/alerts.tsx +5 -0
  193. package/registry/liquidglass-tab/files/src/app/(tabs)/explore.tsx +5 -0
  194. package/registry/liquidglass-tab/files/src/app/(tabs)/index.tsx +5 -0
  195. package/registry/liquidglass-tab/files/src/app/(tabs)/profile.tsx +5 -0
  196. package/registry/liquidglass-tab/files/src/app/_layout.tsx +33 -0
  197. package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +259 -0
  198. package/registry/liquidglass-tab/files/src/components/TabScreen.tsx +122 -0
  199. package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +61 -0
  200. package/registry/liquidglass-tab/files/src/lib/tab-icons.ts +130 -0
  201. package/registry/liquidglass-tab/files/src/lib/tabs.ts +108 -0
  202. package/registry/liquidglass-tab.json +23 -0
  203. package/registry/list-item/files/src/components/ui/list-item.tsx +508 -0
  204. package/registry/list-item/files/src/lib/kit.tsx +219 -0
  205. package/registry/list-item.json +17 -0
  206. package/registry/loyalty-card.json +23 -0
  207. package/registry/morph-button.json +18 -0
  208. package/registry/number-ticker.json +15 -0
  209. package/registry/offers.json +24 -0
  210. package/registry/onboarding.json +17 -0
  211. package/registry/otp-input/files/src/components/ui/otp-input.tsx +380 -0
  212. package/registry/otp-input/files/src/lib/kit.tsx +219 -0
  213. package/registry/otp-input.json +17 -0
  214. package/registry/pagination-dots/files/src/components/ui/pagination-dots.tsx +262 -0
  215. package/registry/pagination-dots/files/src/lib/kit.tsx +219 -0
  216. package/registry/pagination-dots.json +15 -0
  217. package/registry/parallax-layers.json +15 -0
  218. package/registry/parallax-scroll.json +17 -0
  219. package/registry/password-input/files/src/components/ui/floating-input.tsx +528 -0
  220. package/registry/password-input/files/src/components/ui/password-input.tsx +271 -0
  221. package/registry/password-input/files/src/lib/kit.tsx +219 -0
  222. package/registry/password-input.json +19 -0
  223. package/registry/payment-card/files/src/components/ui/flip-card.tsx +205 -0
  224. package/registry/payment-card/files/src/components/ui/payment-card.tsx +385 -0
  225. package/registry/payment-card/files/src/components/ui/tilt-card.tsx +635 -0
  226. package/registry/payment-card/files/src/lib/kit.tsx +219 -0
  227. package/registry/payment-card.json +21 -0
  228. package/registry/pixel-bar.json +14 -0
  229. package/registry/polar-area.json +14 -0
  230. package/registry/profile.json +17 -0
  231. package/registry/progress/files/src/components/ui/progress.tsx +883 -0
  232. package/registry/progress/files/src/lib/kit.tsx +219 -0
  233. package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +559 -0
  234. package/registry/progress-steps/files/src/lib/kit.tsx +219 -0
  235. package/registry/progress-steps.json +18 -0
  236. package/registry/progress.json +17 -0
  237. package/registry/radio-group/files/src/components/ui/radio-group.tsx +327 -0
  238. package/registry/radio-group/files/src/lib/kit.tsx +219 -0
  239. package/registry/radio-group.json +18 -0
  240. package/registry/rating.json +18 -0
  241. package/registry/ring-carousel.json +15 -0
  242. package/registry/scratch-card.json +18 -0
  243. package/registry/search-bar/files/src/components/ui/search-bar.tsx +530 -0
  244. package/registry/search-bar/files/src/lib/kit.tsx +219 -0
  245. package/registry/search-bar.json +17 -0
  246. package/registry/search-results.json +30 -0
  247. package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +341 -0
  248. package/registry/segmented-control/files/src/lib/kit.tsx +219 -0
  249. package/registry/segmented-control.json +18 -0
  250. package/registry/select/files/src/components/ui/select.tsx +1398 -0
  251. package/registry/select/files/src/lib/kit.tsx +219 -0
  252. package/registry/select.json +17 -0
  253. package/registry/settings.json +26 -0
  254. package/registry/shimmer-button.json +17 -0
  255. package/registry/sign-in.json +26 -0
  256. package/registry/skeleton/files/src/components/ui/skeleton.tsx +799 -0
  257. package/registry/skeleton/files/src/lib/kit.tsx +219 -0
  258. package/registry/skeleton.json +17 -0
  259. package/registry/slide-to-confirm.json +18 -0
  260. package/registry/slider/files/src/components/ui/slider.tsx +716 -0
  261. package/registry/slider/files/src/lib/kit.tsx +219 -0
  262. package/registry/slider.json +17 -0
  263. package/registry/spin-wheel.json +18 -0
  264. package/registry/spinner/files/src/components/ui/spinner.tsx +748 -0
  265. package/registry/spinner/files/src/lib/kit.tsx +219 -0
  266. package/registry/spinner.json +17 -0
  267. package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +532 -0
  268. package/registry/stateful-button/files/src/lib/kit.tsx +219 -0
  269. package/registry/stateful-button.json +18 -0
  270. package/registry/stepper/files/src/components/ui/stepper.tsx +646 -0
  271. package/registry/stepper/files/src/lib/kit.tsx +219 -0
  272. package/registry/stepper.json +17 -0
  273. package/registry/swipe-deck.json +15 -0
  274. package/registry/swipeable-row.json +15 -0
  275. package/registry/switch/files/src/components/ui/switch.tsx +344 -0
  276. package/registry/switch/files/src/lib/kit.tsx +219 -0
  277. package/registry/switch.json +18 -0
  278. package/registry/tabs/files/src/components/ui/tabs.tsx +539 -0
  279. package/registry/tabs/files/src/lib/kit.tsx +219 -0
  280. package/registry/tabs.json +17 -0
  281. package/registry/tag-input/files/src/components/ui/tag-input.tsx +720 -0
  282. package/registry/tag-input/files/src/lib/kit.tsx +219 -0
  283. package/registry/tag-input.json +17 -0
  284. package/registry/text-animation/files/src/components/ui/text-animation.tsx +650 -0
  285. package/registry/text-animation/files/src/lib/kit.tsx +219 -0
  286. package/registry/text-animation.json +15 -0
  287. package/registry/textarea/files/src/components/ui/textarea.tsx +553 -0
  288. package/registry/textarea/files/src/lib/kit.tsx +219 -0
  289. package/registry/textarea.json +17 -0
  290. package/registry/ticket-card.json +24 -0
  291. package/registry/ticket-shape/files/src/lib/kit.tsx +219 -0
  292. package/registry/ticket-shape/files/src/lib/ticket-shape.tsx +543 -0
  293. package/registry/ticket-shape.json +17 -0
  294. package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +635 -0
  295. package/registry/tilt-card/files/src/lib/kit.tsx +219 -0
  296. package/registry/tilt-card.json +17 -0
  297. package/registry/time-picker/files/src/components/ui/time-picker.tsx +1159 -0
  298. package/registry/time-picker/files/src/lib/kit.tsx +219 -0
  299. package/registry/time-picker.json +17 -0
  300. package/registry/timeline/files/src/components/ui/avatar.tsx +790 -0
  301. package/registry/timeline/files/src/components/ui/timeline.tsx +1059 -0
  302. package/registry/timeline/files/src/lib/kit.tsx +219 -0
  303. package/registry/timeline-chart.json +14 -0
  304. package/registry/timeline.json +20 -0
  305. package/registry/toast/files/src/components/ui/button.tsx +274 -0
  306. package/registry/toast/files/src/components/ui/toast.tsx +1284 -0
  307. package/registry/toast/files/src/lib/kit.tsx +219 -0
  308. package/registry/toast.json +20 -0
  309. package/registry/treemap.json +14 -0
  310. package/registry/waterfall.json +14 -0
  311. package/registry/wheel-list.json +17 -0
  312. package/schema/components.schema.json +13 -0
@@ -0,0 +1,129 @@
1
+ {
2
+ "name": "tabs",
3
+ "category": "Navigation",
4
+ "aliases": [],
5
+ "exportName": "Tabs",
6
+ "files": [
7
+ "src/components/ui/tabs.tsx"
8
+ ],
9
+ "summary": "Underline tabs whose indicator moves like liquid, leading edge first.",
10
+ "props": [
11
+ [
12
+ "items",
13
+ "{ value, label, icon?, badge?, content?, disabled?, accessibilityLabel? }[]",
14
+ "—",
15
+ "Tabs and (optionally) their content panes. `icon` is a node or `({ active, color }) => node`."
16
+ ],
17
+ [
18
+ "value / defaultValue",
19
+ "string",
20
+ "first enabled tab",
21
+ "Active tab (controlled / uncontrolled)."
22
+ ],
23
+ [
24
+ "onValueChange",
25
+ "(value: string) => void",
26
+ "—",
27
+ "Called when a tab is pressed or swiped to."
28
+ ],
29
+ [
30
+ "size",
31
+ "\"sm\" | \"md\"",
32
+ "\"md\"",
33
+ "36 / 44pt tall (sm keeps a 44pt hit area)."
34
+ ],
35
+ [
36
+ "scrollable",
37
+ "boolean",
38
+ "auto",
39
+ "`true` sizes tabs to labels and scrolls; `false` gives equal widths. Default fills the width and scrolls only when tabs overflow, keeping the active tab centred."
40
+ ],
41
+ [
42
+ "swipeable",
43
+ "boolean",
44
+ "true",
45
+ "Drag the content sideways to change tab; the underline tracks the drag."
46
+ ],
47
+ [
48
+ "transition",
49
+ "\"slide\" | \"fade\" | \"none\"",
50
+ "\"slide\"",
51
+ "How content changes on a tap."
52
+ ],
53
+ [
54
+ "haptics",
55
+ "boolean",
56
+ "true",
57
+ "Selection tick on change."
58
+ ],
59
+ [
60
+ "accent",
61
+ "string",
62
+ "theme accent",
63
+ "Underline and active badge colour."
64
+ ],
65
+ [
66
+ "variant",
67
+ "\"underline\"",
68
+ "\"underline\"",
69
+ "Deprecated: `pill` and `soft` render as `underline` — use SegmentedControl for a pill switch."
70
+ ],
71
+ [
72
+ "contentStyle",
73
+ "StyleProp<ViewStyle>",
74
+ "—",
75
+ "Style of each content pane."
76
+ ],
77
+ [
78
+ "style",
79
+ "StyleProp<ViewStyle>",
80
+ "—",
81
+ "Style for the root view wrapping the tab bar and content."
82
+ ]
83
+ ],
84
+ "behavior": [
85
+ [
86
+ "Touch",
87
+ "Tap a tab or drag the content panes sideways; the underline follows your finger, with rubber-band resistance at the ends."
88
+ ],
89
+ [
90
+ "Motion",
91
+ "The underline is measured to each label and springs with the leading edge first. Badges pop, and an overflowing bar scrolls to centre the active tab."
92
+ ],
93
+ [
94
+ "Reduced motion",
95
+ "Springs become short timings, the bar scrolls without animating and badges change without the pop."
96
+ ],
97
+ [
98
+ "Haptics",
99
+ "A selection tick on every change; turn it off with haptics={false}."
100
+ ],
101
+ [
102
+ "Screen readers",
103
+ "The bar is a tablist of tabs with selected and disabled state; badges are read with the label, and inactive panes are hidden."
104
+ ]
105
+ ],
106
+ "usage": "import { Tabs } from '@/components/ui/tabs';\n\n<Tabs\n items={[\n { value: 'overview', label: 'Overview', content: <Overview /> },\n { value: 'reviews', label: 'Reviews', badge: 128, content: <Reviews /> },\n ]}\n/>",
107
+ "examples": [
108
+ {
109
+ "name": "Underline",
110
+ "title": "Underline",
111
+ "code": "import { Pressable, Text, View } from 'react-native';\nimport { Tabs } from '@/components/ui/tabs';\nimport { alpha, useKit } from '@/lib/kit';\n\nfunction Panel({ title, body }: { title: string; body: string }) {\n const { colors } = useKit();\n return (\n <View style={{ gap: 4 }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground }}>{title}</Text>\n <Text style={{ fontSize: 14, fontWeight: '400', lineHeight: 20, color: colors.mutedForeground }}>{body}</Text>\n </View>\n );\n}\n\nexport default function Underline() {\n return (\n <Tabs\n items={[\n { value: 'overview', label: 'Overview', content: <Panel title=\"Revenue up 12%\" body=\"Annual plans drove most of the growth this week. Churn held at 1.8%.\" /> },\n { value: 'activity', label: 'Activity', badge: 3, content: <Panel title=\"3 new comments\" body=\"Priya and Leo left notes on the Q4 roadmap since yesterday.\" /> },\n { value: 'settings', label: 'Settings', content: <Panel title=\"Project settings\" body=\"Notifications, members and integrations for Mobile app 2.0.\" /> },\n ]}\n />\n );\n}\n"
112
+ },
113
+ {
114
+ "name": "Profile",
115
+ "title": "Profile",
116
+ "code": "import { Pressable, Text, View } from 'react-native';\nimport { Tabs } from '@/components/ui/tabs';\nimport { alpha, useKit } from '@/lib/kit';\n\ntype Post = [who: string, text: string, meta: string];\n\nfunction Feed({ posts }: { posts: Post[] }) {\n const { colors, radius, dark } = useKit();\n return (\n <View style={{ gap: 14 }}>\n {posts.map(([who, text, meta]) => (\n <View key={text} style={{ flexDirection: 'row', gap: 10 }}>\n <View\n style={{\n width: 34,\n height: 34,\n borderRadius: radius.capsule(34),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{who.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1, gap: 2 }}>\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>\n <Text style={{ color: colors.foreground }}>{who}</Text> · {meta}\n </Text>\n <Text style={{ fontSize: 14.5, fontWeight: '400', lineHeight: 20, color: colors.foreground }}>{text}</Text>\n </View>\n </View>\n ))}\n </View>\n );\n}\n\nfunction Grid() {\n const { colors, radius, dark } = useKit();\n const tones = [0.1, 0.18, 0.26, 0.14, 0.22, 0.3];\n return (\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 6 }}>\n {tones.map((a, i) => (\n <View\n key={i}\n style={{ width: '32%', aspectRatio: 1, borderRadius: radius.scale(10), backgroundColor: alpha(colors.accent, dark ? a + 0.08 : a) }}\n />\n ))}\n </View>\n );\n}\n\nexport default function Profile() {\n return (\n <Tabs\n items={[\n {\n value: 'posts',\n label: 'Posts',\n badge: 128,\n content: (\n <Feed\n posts={[\n ['Maya Chen', 'Shipped the new onboarding today. Three screens instead of seven, and it finally feels calm.', '2h'],\n ['Maya Chen', 'Hot take: every tab bar needs a haptic tick.', '1d'],\n ]}\n />\n ),\n },\n {\n value: 'replies',\n label: 'Replies',\n content: <Feed posts={[['Maya Chen', 'Agreed, the spring on that indicator is perfect. Ship it.', '5h']]} />,\n },\n { value: 'media', label: 'Media', badge: 24, content: <Grid /> },\n {\n value: 'likes',\n label: 'Likes',\n content: (\n <Feed\n posts={[\n ['Leo Park', 'Offsite is booked: June 12–14 in Lisbon. Bring sunscreen.', '3h'],\n ['Ana Ruiz', 'Pricing page v3 is live. Conversion up 0.6 points so far.', '1d'],\n ]}\n />\n ),\n },\n ]}\n />\n );\n}\n"
117
+ },
118
+ {
119
+ "name": "Scrollable",
120
+ "title": "Scrollable",
121
+ "code": "import { Pressable, Text, View } from 'react-native';\nimport { Tabs } from '@/components/ui/tabs';\nimport { alpha, useKit } from '@/lib/kit';\n\nfunction Panel({ title, body }: { title: string; body: string }) {\n const { colors } = useKit();\n return (\n <View style={{ gap: 4 }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground }}>{title}</Text>\n <Text style={{ fontSize: 14, fontWeight: '400', lineHeight: 20, color: colors.mutedForeground }}>{body}</Text>\n </View>\n );\n}\n\nexport default function Scrollable() {\n const categories = ['For you', 'Trending', 'Design', 'Engineering', 'Product', 'Marketing', 'Finance', 'Remote'];\n return (\n <Tabs\n items={categories.map((c, i) => ({\n value: c,\n label: c,\n badge: i === 1 ? 'New' : undefined,\n content: <Panel title={c} body={`Hand-picked ${c.toLowerCase()} stories from people you follow, updated every hour.`} />,\n }))}\n />\n );\n}\n"
122
+ },
123
+ {
124
+ "name": "WithIcons",
125
+ "title": "With Icons",
126
+ "code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { BottomNavIcon } from '@/components/ui/bottom-nav';\nimport { Tabs } from '@/components/ui/tabs';\nimport { alpha, useKit } from '@/lib/kit';\n\nfunction Panel({ title, body }: { title: string; body: string }) {\n const { colors } = useKit();\n return (\n <View style={{ gap: 4 }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground }}>{title}</Text>\n <Text style={{ fontSize: 14, fontWeight: '400', lineHeight: 20, color: colors.mutedForeground }}>{body}</Text>\n </View>\n );\n}\n\ntype Post = [who: string, text: string, meta: string];\n\nfunction Feed({ posts }: { posts: Post[] }) {\n const { colors, radius, dark } = useKit();\n return (\n <View style={{ gap: 14 }}>\n {posts.map(([who, text, meta]) => (\n <View key={text} style={{ flexDirection: 'row', gap: 10 }}>\n <View\n style={{\n width: 34,\n height: 34,\n borderRadius: radius.capsule(34),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{who.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1, gap: 2 }}>\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>\n <Text style={{ color: colors.foreground }}>{who}</Text> · {meta}\n </Text>\n <Text style={{ fontSize: 14.5, fontWeight: '400', lineHeight: 20, color: colors.foreground }}>{text}</Text>\n </View>\n </View>\n ))}\n </View>\n );\n}\n\nexport default function WithIcons() {\n const { colors } = useKit();\n const [unread, setUnread] = useState(4);\n const [mentions, setMentions] = useState(2);\n const clear = (\n <Pressable\n accessibilityRole=\"button\"\n onPress={() => {\n setUnread(0);\n setMentions(0);\n }}\n hitSlop={10}\n style={{ alignSelf: 'flex-start', marginTop: 12 }}\n >\n <Text style={{ fontSize: 14, fontWeight: '400', color: colors.accent }}>Mark all as read</Text>\n </Pressable>\n );\n return (\n <Tabs\n size=\"sm\"\n items={[\n {\n value: 'inbox',\n label: 'Inbox',\n icon: ({ active, color }) => <BottomNavIcon name=\"inbox\" color={color} size={17} active={active} />,\n badge: unread || undefined,\n content: (\n <View>\n <Feed posts={[['Priya Nair', 'Can you review the pricing page before 3pm?', '9:41']]} />\n {clear}\n </View>\n ),\n },\n {\n value: 'mentions',\n label: 'Mentions',\n icon: ({ active, color }) => <BottomNavIcon name=\"message\" color={color} size={17} active={active} />,\n badge: mentions || undefined,\n content: <Feed posts={[['Leo Park', '@karan the offsite deck is ready for a look.', '9:12']]} />,\n },\n { value: 'activity', label: 'Activity', icon: ({ active, color }) => <BottomNavIcon name=\"activity\" color={color} size={17} active={active} />, content: <Panel title=\"All quiet\" body=\"Nothing new since you last checked.\" /> },\n ]}\n />\n );\n}\n"
127
+ }
128
+ ]
129
+ }
@@ -0,0 +1,148 @@
1
+ {
2
+ "name": "tag-input",
3
+ "category": "Inputs",
4
+ "aliases": [],
5
+ "exportName": "TagInput",
6
+ "files": [
7
+ "src/components/ui/tag-input.tsx"
8
+ ],
9
+ "summary": "Chips that spring in on Enter or comma and shrink away as neighbours slide over.",
10
+ "props": [
11
+ [
12
+ "value / defaultValue",
13
+ "string[]",
14
+ "[]",
15
+ "Controlled or uncontrolled tags."
16
+ ],
17
+ [
18
+ "onChange",
19
+ "(tags) => void",
20
+ "—",
21
+ "Called when a tag is added or removed (the removed chip then shrinks out)."
22
+ ],
23
+ [
24
+ "variant",
25
+ "\"outlined\" | \"filled\"",
26
+ "\"outlined\"",
27
+ "Field style."
28
+ ],
29
+ [
30
+ "chipVariant",
31
+ "\"soft\" | \"solid\" | \"outline\"",
32
+ "\"soft\"",
33
+ "Tinted accent chips, filled accent chips, or bordered chips with an accent dot."
34
+ ],
35
+ [
36
+ "colorize",
37
+ "boolean",
38
+ "false",
39
+ "Tint each chip from a hash of its label."
40
+ ],
41
+ [
42
+ "prefix",
43
+ "string",
44
+ "—",
45
+ "Shown before every chip label (e.g. \"#\"); typed copies are stripped."
46
+ ],
47
+ [
48
+ "transform",
49
+ "(tag) => string",
50
+ "—",
51
+ "Normalise a tag before validation (lowercase, strip spaces…)."
52
+ ],
53
+ [
54
+ "maxTags",
55
+ "number",
56
+ "—",
57
+ "Limit; shows a filling ring and \"3/5\" counter, and locks the input when reached."
58
+ ],
59
+ [
60
+ "suggestions",
61
+ "string[]",
62
+ "—",
63
+ "Pills under the empty field; while typing, a list with the match highlighted and an \"Add …\" row."
64
+ ],
65
+ [
66
+ "separators",
67
+ "string[]",
68
+ "[\",\"]",
69
+ "Characters that commit a tag (Enter always does)."
70
+ ],
71
+ [
72
+ "validate",
73
+ "(tag, tags) => boolean | string",
74
+ "—",
75
+ "Reject a tag (the field shakes); return a string to show it as the error."
76
+ ],
77
+ [
78
+ "error / helper",
79
+ "string",
80
+ "—",
81
+ "Message under the field; errors shake once."
82
+ ],
83
+ [
84
+ "accent",
85
+ "string",
86
+ "theme accent",
87
+ "Focus and chip colour."
88
+ ],
89
+ [
90
+ "style",
91
+ "StyleProp<ViewStyle>",
92
+ "—",
93
+ "Style for the outer wrapper around the field, suggestions and message (the one that shakes)."
94
+ ],
95
+ [
96
+ "…TextInputProps",
97
+ "",
98
+ "",
99
+ "placeholder, keyboardType, …"
100
+ ]
101
+ ],
102
+ "behavior": [
103
+ [
104
+ "Keyboard",
105
+ "Enter or a separator commits the text as a chip. Backspace in an empty field highlights the last chip, and a second press removes it."
106
+ ],
107
+ [
108
+ "Motion",
109
+ "Chips pop in on a spring and shrink out on removal; duplicates bump and a rejected tag shakes the field."
110
+ ],
111
+ [
112
+ "Screen readers",
113
+ "Each chip has a 'Remove' button and each suggestion an 'Add' button; error messages land in a polite live region and the counter reads '3 of 5 tags'."
114
+ ],
115
+ [
116
+ "Web",
117
+ "The browser focus outline on the input is removed; the field draws its own focus state."
118
+ ]
119
+ ],
120
+ "usage": "import { useState } from 'react';\nimport { TagInput } from '@/components/ui/tag-input';\n\nconst [skills, setSkills] = useState(['TypeScript']);\n<TagInput value={skills} onChange={setSkills} placeholder=\"Add a skill…\" suggestions={['React', 'Swift', 'Kotlin']} />",
121
+ "examples": [
122
+ {
123
+ "name": "Skills",
124
+ "title": "Skills",
125
+ "code": "import React, { useState } from 'react';\nimport { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { TagInput } from '@/components/ui/tag-input';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n s.card,\n { borderRadius: radius.xl, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -6 },\n});\n\nexport default function Skills() {\n return (\n <Card>\n <Ink style={s.title}>Your skills</Ink>\n <TagInput\n placeholder=\"Add a skill…\"\n defaultValue={['TypeScript', 'React Native']}\n suggestions={['React', 'Swift', 'SwiftUI', 'Kotlin', 'GraphQL', 'Figma', 'Node.js', 'Rust', 'PostgreSQL']}\n helper=\"Type to search, or tap a suggestion.\"\n />\n </Card>\n );\n}\n"
126
+ },
127
+ {
128
+ "name": "InviteByEmail",
129
+ "title": "Invite By Email",
130
+ "code": "import React, { useState } from 'react';\nimport { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { TagInput } from '@/components/ui/tag-input';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n s.card,\n { borderRadius: radius.xl, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -6 },\n});\n\nexport default function InviteByEmail() {\n const [emails, setEmails] = useState(['maya@studio.co', 'leo@studio.co']);\n return (\n <Card>\n <Ink style={s.title}>Invite teammates</Ink>\n <TagInput\n value={emails}\n onChange={setEmails}\n validate={(tag) => /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/.test(tag) || `“${tag}” isn't a valid email address`}\n transform={(tag) => tag.toLowerCase()}\n chipVariant=\"outline\"\n keyboardType=\"email-address\"\n placeholder=\"name@company.com\"\n helper=\"Separate addresses with a comma or space.\"\n separators={[',', ' ']}\n />\n </Card>\n );\n}\n"
131
+ },
132
+ {
133
+ "name": "Topics",
134
+ "title": "Topics",
135
+ "code": "import { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { TagInput } from '@/components/ui/tag-input';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n s.card,\n { borderRadius: radius.xl, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -6 },\n});\n\nexport default function Topics() {\n return (\n <Card>\n <Ink style={s.title}>Follow topics</Ink>\n <TagInput\n colorize\n maxTags={5}\n defaultValue={['Design', 'Climate', 'Startups']}\n suggestions={['Space', 'AI', 'Music', 'Cooking', 'Photography', 'Travel']}\n placeholder=\"Add a topic…\"\n helper=\"Pick up to five for your feed.\"\n />\n </Card>\n );\n}\n"
136
+ },
137
+ {
138
+ "name": "Hashtags",
139
+ "title": "Hashtags",
140
+ "code": "import { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { TagInput } from '@/components/ui/tag-input';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n s.card,\n { borderRadius: radius.xl, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -6 },\n});\n\nexport default function Hashtags() {\n return (\n <Card>\n <Ink style={s.title}>New post</Ink>\n <TagInput\n variant=\"filled\"\n chipVariant=\"solid\"\n prefix=\"#\"\n transform={(tag) => tag.replace(/\\s+/g, '').toLowerCase()}\n separators={[',', ' ']}\n defaultValue={['sunset', 'lisbon']}\n suggestions={['travel', 'goldenhour', 'streetphotography', 'portugal']}\n placeholder=\"#hashtag\"\n helper=\"Backspace once to highlight the last tag, twice to remove it.\"\n />\n </Card>\n );\n}\n"
141
+ },
142
+ {
143
+ "name": "ChipStyles",
144
+ "title": "Chip Styles",
145
+ "code": "import { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { TagInput } from '@/components/ui/tag-input';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n s.card,\n { borderRadius: radius.xl, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -6 },\n});\n\nexport default function ChipStyles() {\n return (\n <Card>\n <Ink tone=\"muted\" style={s.label}>Soft</Ink>\n <TagInput defaultValue={['Vegan', 'Gluten-free']} placeholder=\"Dietary needs\" />\n <Ink tone=\"muted\" style={s.label}>Solid</Ink>\n <TagInput chipVariant=\"solid\" defaultValue={['Urgent', 'Bug']} placeholder=\"Labels\" />\n <Ink tone=\"muted\" style={s.label}>Outline</Ink>\n <TagInput chipVariant=\"outline\" defaultValue={['Remote', 'Full-time']} placeholder=\"Job filters\" />\n <Ink tone=\"muted\" style={s.label}>Disabled</Ink>\n <TagInput editable={false} defaultValue={['Read-only']} />\n </Card>\n );\n}\n"
146
+ }
147
+ ]
148
+ }
@@ -0,0 +1,141 @@
1
+ {
2
+ "name": "text-animation",
3
+ "category": "Motion",
4
+ "aliases": [
5
+ "text-effect",
6
+ "animated-text",
7
+ "typewriter",
8
+ "shimmer-text",
9
+ "scramble-text"
10
+ ],
11
+ "exportName": "AnimatedText",
12
+ "files": [
13
+ "src/components/ui/text-animation.tsx"
14
+ ],
15
+ "summary": "Reveal, shimmer, cascade, scramble and typewriter text effects.",
16
+ "props": [
17
+ [
18
+ "children",
19
+ "string",
20
+ "—",
21
+ "The text to animate."
22
+ ],
23
+ [
24
+ "variant",
25
+ "\"reveal\" | \"shimmer\" | \"cascade\" | \"scramble\" | \"typewriter\"",
26
+ "\"reveal\"",
27
+ "Which effect to play."
28
+ ],
29
+ [
30
+ "trigger",
31
+ "\"mount\" | \"change\"",
32
+ "\"change\" for cascade / typewriter, else \"mount\"",
33
+ "Animate once on mount, or also every time the text changes. A typewriter keeps typing when the new text extends the old (streaming)."
34
+ ],
35
+ [
36
+ "delay",
37
+ "number",
38
+ "0",
39
+ "ms before the first animation."
40
+ ],
41
+ [
42
+ "stagger",
43
+ "number",
44
+ "70 reveal · 80 shimmer · 28 cascade · 45 scramble · 32 typewriter",
45
+ "ms between words (reveal) or letters. Shimmer: ms of sweep per letter."
46
+ ],
47
+ [
48
+ "speed",
49
+ "number",
50
+ "1",
51
+ "Speed multiplier: 2 plays twice as fast."
52
+ ],
53
+ [
54
+ "color",
55
+ "string",
56
+ "theme foreground",
57
+ "Text colour."
58
+ ],
59
+ [
60
+ "caret",
61
+ "boolean",
62
+ "true",
63
+ "Blinking caret for the typewriter."
64
+ ],
65
+ [
66
+ "replayKey",
67
+ "string | number",
68
+ "—",
69
+ "Change it to replay the animation."
70
+ ],
71
+ [
72
+ "onComplete",
73
+ "() => void",
74
+ "—",
75
+ "Called when an animation finishes (not for the looping shimmer)."
76
+ ],
77
+ [
78
+ "textStyle",
79
+ "StyleProp<TextStyle>",
80
+ "—",
81
+ "fontSize, fontWeight, lineHeight, letterSpacing, textAlign… for every letter."
82
+ ],
83
+ [
84
+ "style",
85
+ "StyleProp<ViewStyle>",
86
+ "—",
87
+ "Style for the root view."
88
+ ]
89
+ ],
90
+ "behavior": [
91
+ [
92
+ "Motion",
93
+ "Reveal, shimmer and cascade run one native-driver clock per animation with spring curves pre-sampled into interpolations; scramble and typewriter use a light JS timer. Text wraps by word."
94
+ ],
95
+ [
96
+ "Reduced motion",
97
+ "Shows the final text immediately; shimmer stays static and the typewriter caret is hidden."
98
+ ],
99
+ [
100
+ "Screen readers",
101
+ "The whole string is one text element; the per-letter views are hidden. Cascade and typewriter announce new text when it changes (typewriter once streaming settles)."
102
+ ],
103
+ [
104
+ "Platforms",
105
+ "iOS, Android and web. Arabic, Hebrew, Indic, Thai and other connected scripts animate a word at a time so shaping is never broken; right-to-left text flows from the right."
106
+ ]
107
+ ],
108
+ "usage": "import { AnimatedText } from '@/components/ui/text-animation';\n\n<AnimatedText textStyle={{ fontSize: 28, fontWeight: '600' }}>\n Ship native apps that feel alive.\n</AnimatedText>\n\n<AnimatedText variant=\"shimmer\">Thinking…</AnimatedText>\n<AnimatedText variant=\"cascade\">{status}</AnimatedText>",
109
+ "examples": [
110
+ {
111
+ "name": "Reveal",
112
+ "title": "Reveal",
113
+ "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { AnimatedText } from '@/components/ui/text-animation';\nimport { useKit } from '@/lib/kit';\n\nfunction Chip({ label, onPress }: { label: string; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n accessibilityRole=\"button\"\n onPress={onPress}\n hitSlop={4}\n style={{ height: 36, paddingHorizontal: 14, borderRadius: radius.capsule(36), justifyContent: 'center', alignSelf: 'flex-start', backgroundColor: colors.muted }}\n >\n <Text style={{ color: colors.foreground, fontSize: 14 }}>{label}</Text>\n </Pressable>\n );\n}\n\nexport default function Reveal() {\n const [run, setRun] = useState(0);\n return (\n <View style={{ gap: 16 }}>\n <AnimatedText replayKey={run} textStyle={{ fontSize: 30, fontWeight: '600', letterSpacing: -0.8, lineHeight: 36 }}>\n Ship native apps that feel alive.\n </AnimatedText>\n <Chip label=\"Replay\" onPress={() => setRun((r) => r + 1)} />\n </View>\n );\n}\n"
114
+ },
115
+ {
116
+ "name": "Thinking",
117
+ "title": "Thinking",
118
+ "code": "import { Pressable, Text, View } from 'react-native';\nimport { AnimatedText } from '@/components/ui/text-animation';\nimport { useKit } from '@/lib/kit';\n\nexport default function Thinking() {\n const { colors, radius } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10, padding: 14, borderRadius: radius.lg, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border }}>\n <View style={{ width: 28, height: 28, borderRadius: radius.capsule(28), backgroundColor: colors.accent }} />\n <AnimatedText variant=\"shimmer\" textStyle={{ fontSize: 16 }}>\n Thinking…\n </AnimatedText>\n </View>\n );\n}\n"
119
+ },
120
+ {
121
+ "name": "DeployStatus",
122
+ "title": "Deploy Status",
123
+ "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { AnimatedText } from '@/components/ui/text-animation';\nimport { useKit } from '@/lib/kit';\n\nfunction Chip({ label, onPress }: { label: string; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n accessibilityRole=\"button\"\n onPress={onPress}\n hitSlop={4}\n style={{ height: 36, paddingHorizontal: 14, borderRadius: radius.capsule(36), justifyContent: 'center', alignSelf: 'flex-start', backgroundColor: colors.muted }}\n >\n <Text style={{ color: colors.foreground, fontSize: 14 }}>{label}</Text>\n </Pressable>\n );\n}\n\nconst STATUSES = ['Queued', 'Building', 'Deploying', 'Live'];\n\nexport default function DeployStatus() {\n const { colors, radius } = useKit();\n const [i, setI] = useState(0);\n const status = STATUSES[i];\n const live = status === 'Live';\n return (\n <View style={{ gap: 16 }}>\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>\n <View style={{ width: 10, height: 10, borderRadius: radius.capsule(10), backgroundColor: live ? colors.success : colors.warning }} />\n <Text style={{ fontSize: 17, color: colors.mutedForeground }}>expokit.dev</Text>\n <AnimatedText variant=\"cascade\" color={live ? colors.success : colors.foreground} textStyle={{ fontSize: 17, fontWeight: '600' }}>\n {status}\n </AnimatedText>\n </View>\n <Chip label=\"Next status\" onPress={() => setI((n) => (n + 1) % STATUSES.length)} />\n </View>\n );\n}\n"
124
+ },
125
+ {
126
+ "name": "Scramble",
127
+ "title": "Scramble",
128
+ "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { AnimatedText } from '@/components/ui/text-animation';\nimport { useKit } from '@/lib/kit';\n\nfunction Chip({ label, onPress }: { label: string; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n accessibilityRole=\"button\"\n onPress={onPress}\n hitSlop={4}\n style={{ height: 36, paddingHorizontal: 14, borderRadius: radius.capsule(36), justifyContent: 'center', alignSelf: 'flex-start', backgroundColor: colors.muted }}\n >\n <Text style={{ color: colors.foreground, fontSize: 14 }}>{label}</Text>\n </Pressable>\n );\n}\n\nexport default function Scramble() {\n const { colors, font } = useKit();\n const [run, setRun] = useState(0);\n return (\n <View style={{ gap: 16 }}>\n <View style={{ gap: 4 }}>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>Your referral code</Text>\n <AnimatedText variant=\"scramble\" replayKey={run} textStyle={{ fontSize: 28, fontWeight: '600', letterSpacing: 2, fontFamily: font.mono }}>\n SPRING-7Q2X\n </AnimatedText>\n </View>\n <Chip label=\"Generate again\" onPress={() => setRun((r) => r + 1)} />\n </View>\n );\n}\n"
129
+ },
130
+ {
131
+ "name": "Streaming",
132
+ "title": "Streaming",
133
+ "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { AnimatedText } from '@/components/ui/text-animation';\nimport { useKit } from '@/lib/kit';\n\nfunction Chip({ label, onPress }: { label: string; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n accessibilityRole=\"button\"\n onPress={onPress}\n hitSlop={4}\n style={{ height: 36, paddingHorizontal: 14, borderRadius: radius.capsule(36), justifyContent: 'center', alignSelf: 'flex-start', backgroundColor: colors.muted }}\n >\n <Text style={{ color: colors.foreground, fontSize: 14 }}>{label}</Text>\n </Pressable>\n );\n}\n\nconst ANSWER =\n 'Spring animations feel natural because they keep momentum: a flick carries its speed into the motion, and nothing stops dead.';\n\nexport default function Streaming() {\n const { colors, radius } = useKit();\n const [text, setText] = useState('');\n const [run, setRun] = useState(0);\n useEffect(() => {\n // Simulates tokens arriving from a model, a few words at a time.\n const words = ANSWER.split(' ');\n let n = 0;\n const id = setInterval(() => {\n n = Math.min(words.length, n + 1 + Math.floor(Math.random() * 3));\n setText(words.slice(0, n).join(' '));\n if (n >= words.length) clearInterval(id);\n }, 260);\n return () => clearInterval(id);\n }, [run]);\n return (\n <View style={{ gap: 16 }}>\n <View style={{ padding: 14, borderRadius: radius.lg, backgroundColor: colors.muted }}>\n <AnimatedText key={run} variant=\"typewriter\" textStyle={{ fontSize: 16, lineHeight: 23 }}>\n {text}\n </AnimatedText>\n </View>\n <Chip\n label=\"Ask again\"\n onPress={() => {\n setText('');\n setRun((r) => r + 1);\n }}\n />\n </View>\n );\n}\n"
134
+ },
135
+ {
136
+ "name": "Hero",
137
+ "title": "Hero",
138
+ "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { AnimatedText } from '@/components/ui/text-animation';\nimport { useKit } from '@/lib/kit';\n\nexport default function Hero() {\n const { colors } = useKit();\n const [run, setRun] = useState(0);\n return (\n <Pressable accessibilityRole=\"button\" accessibilityHint=\"Replays the animation\" onPress={() => setRun((r) => r + 1)} style={{ alignItems: 'center', gap: 10, paddingVertical: 12 }}>\n <AnimatedText variant=\"scramble\" replayKey={run} color={colors.accent} style={{ alignSelf: 'stretch' }} textStyle={{ fontSize: 13, fontWeight: '600', letterSpacing: 1.5, textAlign: 'center' }}>\n NEW IN EXPOKIT\n </AnimatedText>\n <AnimatedText replayKey={run} delay={250} style={{ alignSelf: 'stretch' }} textStyle={{ fontSize: 34, fontWeight: '700', letterSpacing: -1, lineHeight: 40, textAlign: 'center' }}>\n Motion your users can feel\n </AnimatedText>\n <AnimatedText variant=\"typewriter\" replayKey={run} delay={900} caret={false} style={{ alignSelf: 'stretch' }} color={colors.mutedForeground} textStyle={{ fontSize: 15, textAlign: 'center' }}>\n Tap anywhere to play it again.\n </AnimatedText>\n </Pressable>\n );\n}\n"
139
+ }
140
+ ]
141
+ }