@expokit/cli 0.0.0-stage → 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 (302) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +32 -2
  3. package/bin/expo-kit.js +1204 -0
  4. package/bin/lib.mjs +343 -0
  5. package/bin/mcp.mjs +338 -0
  6. package/package.json +44 -4
  7. package/registry/_docs/accordion.json +99 -0
  8. package/registry/_docs/action-sheet.json +110 -0
  9. package/registry/_docs/alert.json +127 -0
  10. package/registry/_docs/avatar.json +203 -0
  11. package/registry/_docs/badge.json +117 -0
  12. package/registry/_docs/bottom-nav.json +119 -0
  13. package/registry/_docs/bottom-sheet.json +158 -0
  14. package/registry/_docs/bullet.json +24 -0
  15. package/registry/_docs/button-3d.json +122 -0
  16. package/registry/_docs/button.json +115 -0
  17. package/registry/_docs/calendar.json +182 -0
  18. package/registry/_docs/card-stack.json +108 -0
  19. package/registry/_docs/card.json +111 -0
  20. package/registry/_docs/carousel.json +102 -0
  21. package/registry/_docs/checkbox.json +93 -0
  22. package/registry/_docs/checkout.json +102 -0
  23. package/registry/_docs/chip-group.json +116 -0
  24. package/registry/_docs/color-swatch.json +66 -0
  25. package/registry/_docs/confetti.json +82 -0
  26. package/registry/_docs/coupon.json +176 -0
  27. package/registry/_docs/cube-pager.json +84 -0
  28. package/registry/_docs/dashboard.json +120 -0
  29. package/registry/_docs/date-picker.json +249 -0
  30. package/registry/_docs/dialog.json +162 -0
  31. package/registry/_docs/drawer.json +171 -0
  32. package/registry/_docs/fab.json +120 -0
  33. package/registry/_docs/flip-card.json +111 -0
  34. package/registry/_docs/floating-input.json +191 -0
  35. package/registry/_docs/gift-card.json +108 -0
  36. package/registry/_docs/glass-button.json +118 -0
  37. package/registry/_docs/glass-nav-bar.json +78 -0
  38. package/registry/_docs/glass-search-bar.json +90 -0
  39. package/registry/_docs/glass-segmented.json +66 -0
  40. package/registry/_docs/glass-sheet.json +108 -0
  41. package/registry/_docs/glass-tab-bar.json +76 -0
  42. package/registry/_docs/glass.json +78 -0
  43. package/registry/_docs/inbox.json +72 -0
  44. package/registry/_docs/isometric-bar.json +72 -0
  45. package/registry/_docs/kit.json +11 -0
  46. package/registry/_docs/kpi-card.json +11 -0
  47. package/registry/_docs/like-button.json +72 -0
  48. package/registry/_docs/liquidglass-tab.json +27 -0
  49. package/registry/_docs/list-item.json +200 -0
  50. package/registry/_docs/loyalty-card.json +108 -0
  51. package/registry/_docs/morph-button.json +108 -0
  52. package/registry/_docs/number-ticker.json +96 -0
  53. package/registry/_docs/offers.json +78 -0
  54. package/registry/_docs/onboarding.json +66 -0
  55. package/registry/_docs/otp-input.json +134 -0
  56. package/registry/_docs/pagination-dots.json +88 -0
  57. package/registry/_docs/parallax-layers.json +84 -0
  58. package/registry/_docs/parallax-scroll.json +186 -0
  59. package/registry/_docs/password-input.json +92 -0
  60. package/registry/_docs/payment-card.json +153 -0
  61. package/registry/_docs/pixel-bar.json +66 -0
  62. package/registry/_docs/polar-area.json +78 -0
  63. package/registry/_docs/profile.json +84 -0
  64. package/registry/_docs/progress-steps.json +92 -0
  65. package/registry/_docs/progress.json +132 -0
  66. package/registry/_docs/radio-group.json +92 -0
  67. package/registry/_docs/rating.json +90 -0
  68. package/registry/_docs/ring-carousel.json +102 -0
  69. package/registry/_docs/scratch-card.json +90 -0
  70. package/registry/_docs/search-bar.json +140 -0
  71. package/registry/_docs/search-results.json +120 -0
  72. package/registry/_docs/segmented-control.json +103 -0
  73. package/registry/_docs/select.json +252 -0
  74. package/registry/_docs/settings.json +72 -0
  75. package/registry/_docs/shimmer-button.json +78 -0
  76. package/registry/_docs/sign-in.json +198 -0
  77. package/registry/_docs/skeleton.json +142 -0
  78. package/registry/_docs/slide-to-confirm.json +84 -0
  79. package/registry/_docs/slider.json +157 -0
  80. package/registry/_docs/spin-wheel.json +114 -0
  81. package/registry/_docs/spinner.json +91 -0
  82. package/registry/_docs/stepper.json +168 -0
  83. package/registry/_docs/swipe-deck.json +102 -0
  84. package/registry/_docs/swipeable-row.json +72 -0
  85. package/registry/_docs/switch.json +104 -0
  86. package/registry/_docs/tabs.json +105 -0
  87. package/registry/_docs/tag-input.json +128 -0
  88. package/registry/_docs/textarea.json +175 -0
  89. package/registry/_docs/ticket-card.json +174 -0
  90. package/registry/_docs/ticket-shape.json +11 -0
  91. package/registry/_docs/tilt-card.json +141 -0
  92. package/registry/_docs/time-picker.json +253 -0
  93. package/registry/_docs/timeline-chart.json +30 -0
  94. package/registry/_docs/timeline.json +156 -0
  95. package/registry/_docs/toast.json +161 -0
  96. package/registry/_docs/treemap.json +30 -0
  97. package/registry/_docs/waterfall.json +36 -0
  98. package/registry/_docs/wheel-list.json +102 -0
  99. package/registry/accordion/files/src/components/ui/accordion.tsx +233 -0
  100. package/registry/accordion/files/src/lib/kit.tsx +219 -0
  101. package/registry/accordion.json +17 -0
  102. package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +580 -0
  103. package/registry/action-sheet/files/src/lib/kit.tsx +219 -0
  104. package/registry/action-sheet.json +18 -0
  105. package/registry/alert/files/src/components/ui/alert.tsx +654 -0
  106. package/registry/alert/files/src/components/ui/button.tsx +274 -0
  107. package/registry/alert/files/src/lib/kit.tsx +219 -0
  108. package/registry/alert.json +20 -0
  109. package/registry/avatar/files/src/components/ui/avatar.tsx +790 -0
  110. package/registry/avatar/files/src/lib/kit.tsx +219 -0
  111. package/registry/avatar.json +18 -0
  112. package/registry/badge/files/src/components/ui/badge.tsx +316 -0
  113. package/registry/badge/files/src/lib/kit.tsx +219 -0
  114. package/registry/badge.json +17 -0
  115. package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +1050 -0
  116. package/registry/bottom-nav/files/src/lib/kit.tsx +219 -0
  117. package/registry/bottom-nav.json +18 -0
  118. package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +585 -0
  119. package/registry/bottom-sheet/files/src/lib/kit.tsx +219 -0
  120. package/registry/bottom-sheet.json +17 -0
  121. package/registry/bullet.json +14 -0
  122. package/registry/button/files/src/components/ui/button.tsx +274 -0
  123. package/registry/button/files/src/lib/kit.tsx +219 -0
  124. package/registry/button-3d/files/src/components/ui/button-3d.tsx +212 -0
  125. package/registry/button-3d/files/src/lib/kit.tsx +219 -0
  126. package/registry/button-3d.json +18 -0
  127. package/registry/button.json +18 -0
  128. package/registry/calendar/files/src/components/ui/calendar.tsx +822 -0
  129. package/registry/calendar/files/src/lib/kit.tsx +219 -0
  130. package/registry/calendar.json +17 -0
  131. package/registry/card/files/src/components/ui/card.tsx +300 -0
  132. package/registry/card/files/src/lib/kit.tsx +219 -0
  133. package/registry/card-stack.json +15 -0
  134. package/registry/card.json +17 -0
  135. package/registry/carousel.json +15 -0
  136. package/registry/checkbox/files/src/components/ui/checkbox.tsx +150 -0
  137. package/registry/checkbox/files/src/lib/kit.tsx +219 -0
  138. package/registry/checkbox.json +17 -0
  139. package/registry/checkout.json +26 -0
  140. package/registry/chip-group/files/src/components/ui/chip-group.tsx +269 -0
  141. package/registry/chip-group/files/src/lib/kit.tsx +219 -0
  142. package/registry/chip-group.json +17 -0
  143. package/registry/color-swatch.json +17 -0
  144. package/registry/confetti/files/src/components/ui/confetti.tsx +222 -0
  145. package/registry/confetti/files/src/lib/kit.tsx +219 -0
  146. package/registry/confetti.json +15 -0
  147. package/registry/coupon/files/src/components/ui/coupon.tsx +698 -0
  148. package/registry/coupon/files/src/components/ui/flip-card.tsx +205 -0
  149. package/registry/coupon/files/src/components/ui/tilt-card.tsx +635 -0
  150. package/registry/coupon/files/src/lib/kit.tsx +219 -0
  151. package/registry/coupon/files/src/lib/ticket-shape.tsx +543 -0
  152. package/registry/coupon.json +23 -0
  153. package/registry/cube-pager.json +15 -0
  154. package/registry/dashboard.json +32 -0
  155. package/registry/date-picker/files/src/components/ui/calendar.tsx +822 -0
  156. package/registry/date-picker/files/src/components/ui/date-picker.tsx +796 -0
  157. package/registry/date-picker/files/src/lib/kit.tsx +219 -0
  158. package/registry/date-picker.json +19 -0
  159. package/registry/dialog/files/src/components/ui/dialog.tsx +759 -0
  160. package/registry/dialog/files/src/lib/kit.tsx +219 -0
  161. package/registry/dialog.json +18 -0
  162. package/registry/drawer/files/src/components/ui/drawer.tsx +1135 -0
  163. package/registry/drawer/files/src/lib/kit.tsx +219 -0
  164. package/registry/drawer.json +18 -0
  165. package/registry/fab.json +18 -0
  166. package/registry/flip-card/files/src/components/ui/flip-card.tsx +205 -0
  167. package/registry/flip-card/files/src/lib/kit.tsx +219 -0
  168. package/registry/flip-card.json +15 -0
  169. package/registry/floating-input/files/src/components/ui/floating-input.tsx +528 -0
  170. package/registry/floating-input/files/src/lib/kit.tsx +219 -0
  171. package/registry/floating-input.json +17 -0
  172. package/registry/gift-card.json +21 -0
  173. package/registry/glass-button.json +22 -0
  174. package/registry/glass-nav-bar.json +21 -0
  175. package/registry/glass-search-bar.json +21 -0
  176. package/registry/glass-segmented.json +22 -0
  177. package/registry/glass-sheet.json +22 -0
  178. package/registry/glass-tab-bar.json +22 -0
  179. package/registry/glass.json +19 -0
  180. package/registry/inbox.json +30 -0
  181. package/registry/isometric-bar.json +14 -0
  182. package/registry/kit/files/src/lib/kit.tsx +219 -0
  183. package/registry/kit.json +11 -0
  184. package/registry/kpi-card/files/src/components/charts/kpi-card.tsx +45 -0
  185. package/registry/kpi-card.json +14 -0
  186. package/registry/like-button.json +18 -0
  187. package/registry/liquidglass-tab/files/src/app/(tabs)/_layout.tsx +97 -0
  188. package/registry/liquidglass-tab/files/src/app/(tabs)/alerts.tsx +5 -0
  189. package/registry/liquidglass-tab/files/src/app/(tabs)/explore.tsx +5 -0
  190. package/registry/liquidglass-tab/files/src/app/(tabs)/index.tsx +5 -0
  191. package/registry/liquidglass-tab/files/src/app/(tabs)/profile.tsx +5 -0
  192. package/registry/liquidglass-tab/files/src/app/_layout.tsx +33 -0
  193. package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +259 -0
  194. package/registry/liquidglass-tab/files/src/components/TabScreen.tsx +122 -0
  195. package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +61 -0
  196. package/registry/liquidglass-tab/files/src/lib/tab-icons.ts +130 -0
  197. package/registry/liquidglass-tab/files/src/lib/tabs.ts +108 -0
  198. package/registry/liquidglass-tab.json +23 -0
  199. package/registry/list-item/files/src/components/ui/list-item.tsx +508 -0
  200. package/registry/list-item/files/src/lib/kit.tsx +219 -0
  201. package/registry/list-item.json +17 -0
  202. package/registry/loyalty-card.json +23 -0
  203. package/registry/morph-button.json +18 -0
  204. package/registry/number-ticker.json +15 -0
  205. package/registry/offers.json +24 -0
  206. package/registry/onboarding.json +17 -0
  207. package/registry/otp-input/files/src/components/ui/otp-input.tsx +380 -0
  208. package/registry/otp-input/files/src/lib/kit.tsx +219 -0
  209. package/registry/otp-input.json +17 -0
  210. package/registry/pagination-dots/files/src/components/ui/pagination-dots.tsx +262 -0
  211. package/registry/pagination-dots/files/src/lib/kit.tsx +219 -0
  212. package/registry/pagination-dots.json +15 -0
  213. package/registry/parallax-layers.json +15 -0
  214. package/registry/parallax-scroll.json +17 -0
  215. package/registry/password-input/files/src/components/ui/floating-input.tsx +528 -0
  216. package/registry/password-input/files/src/components/ui/password-input.tsx +271 -0
  217. package/registry/password-input/files/src/lib/kit.tsx +219 -0
  218. package/registry/password-input.json +19 -0
  219. package/registry/payment-card/files/src/components/ui/flip-card.tsx +205 -0
  220. package/registry/payment-card/files/src/components/ui/payment-card.tsx +385 -0
  221. package/registry/payment-card/files/src/components/ui/tilt-card.tsx +635 -0
  222. package/registry/payment-card/files/src/lib/kit.tsx +219 -0
  223. package/registry/payment-card.json +21 -0
  224. package/registry/pixel-bar.json +14 -0
  225. package/registry/polar-area.json +14 -0
  226. package/registry/profile.json +17 -0
  227. package/registry/progress/files/src/components/ui/progress.tsx +883 -0
  228. package/registry/progress/files/src/lib/kit.tsx +219 -0
  229. package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +559 -0
  230. package/registry/progress-steps/files/src/lib/kit.tsx +219 -0
  231. package/registry/progress-steps.json +18 -0
  232. package/registry/progress.json +17 -0
  233. package/registry/radio-group/files/src/components/ui/radio-group.tsx +327 -0
  234. package/registry/radio-group/files/src/lib/kit.tsx +219 -0
  235. package/registry/radio-group.json +18 -0
  236. package/registry/rating.json +18 -0
  237. package/registry/ring-carousel.json +15 -0
  238. package/registry/scratch-card.json +18 -0
  239. package/registry/search-bar/files/src/components/ui/search-bar.tsx +530 -0
  240. package/registry/search-bar/files/src/lib/kit.tsx +219 -0
  241. package/registry/search-bar.json +17 -0
  242. package/registry/search-results.json +30 -0
  243. package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +341 -0
  244. package/registry/segmented-control/files/src/lib/kit.tsx +219 -0
  245. package/registry/segmented-control.json +18 -0
  246. package/registry/select/files/src/components/ui/select.tsx +1398 -0
  247. package/registry/select/files/src/lib/kit.tsx +219 -0
  248. package/registry/select.json +17 -0
  249. package/registry/settings.json +26 -0
  250. package/registry/shimmer-button.json +17 -0
  251. package/registry/sign-in.json +26 -0
  252. package/registry/skeleton/files/src/components/ui/skeleton.tsx +799 -0
  253. package/registry/skeleton/files/src/lib/kit.tsx +219 -0
  254. package/registry/skeleton.json +17 -0
  255. package/registry/slide-to-confirm.json +18 -0
  256. package/registry/slider/files/src/components/ui/slider.tsx +716 -0
  257. package/registry/slider/files/src/lib/kit.tsx +219 -0
  258. package/registry/slider.json +17 -0
  259. package/registry/spin-wheel.json +18 -0
  260. package/registry/spinner/files/src/components/ui/spinner.tsx +335 -0
  261. package/registry/spinner/files/src/lib/kit.tsx +219 -0
  262. package/registry/spinner.json +17 -0
  263. package/registry/stepper/files/src/components/ui/stepper.tsx +646 -0
  264. package/registry/stepper/files/src/lib/kit.tsx +219 -0
  265. package/registry/stepper.json +17 -0
  266. package/registry/swipe-deck.json +15 -0
  267. package/registry/swipeable-row.json +15 -0
  268. package/registry/switch/files/src/components/ui/switch.tsx +344 -0
  269. package/registry/switch/files/src/lib/kit.tsx +219 -0
  270. package/registry/switch.json +18 -0
  271. package/registry/tabs/files/src/components/ui/tabs.tsx +539 -0
  272. package/registry/tabs/files/src/lib/kit.tsx +219 -0
  273. package/registry/tabs.json +17 -0
  274. package/registry/tag-input/files/src/components/ui/tag-input.tsx +720 -0
  275. package/registry/tag-input/files/src/lib/kit.tsx +219 -0
  276. package/registry/tag-input.json +17 -0
  277. package/registry/textarea/files/src/components/ui/textarea.tsx +553 -0
  278. package/registry/textarea/files/src/lib/kit.tsx +219 -0
  279. package/registry/textarea.json +17 -0
  280. package/registry/ticket-card.json +24 -0
  281. package/registry/ticket-shape/files/src/lib/kit.tsx +219 -0
  282. package/registry/ticket-shape/files/src/lib/ticket-shape.tsx +543 -0
  283. package/registry/ticket-shape.json +17 -0
  284. package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +635 -0
  285. package/registry/tilt-card/files/src/lib/kit.tsx +219 -0
  286. package/registry/tilt-card.json +17 -0
  287. package/registry/time-picker/files/src/components/ui/time-picker.tsx +1159 -0
  288. package/registry/time-picker/files/src/lib/kit.tsx +219 -0
  289. package/registry/time-picker.json +17 -0
  290. package/registry/timeline/files/src/components/ui/avatar.tsx +790 -0
  291. package/registry/timeline/files/src/components/ui/timeline.tsx +1059 -0
  292. package/registry/timeline/files/src/lib/kit.tsx +219 -0
  293. package/registry/timeline-chart.json +14 -0
  294. package/registry/timeline.json +20 -0
  295. package/registry/toast/files/src/components/ui/button.tsx +274 -0
  296. package/registry/toast/files/src/components/ui/toast.tsx +1284 -0
  297. package/registry/toast/files/src/lib/kit.tsx +219 -0
  298. package/registry/toast.json +20 -0
  299. package/registry/treemap.json +14 -0
  300. package/registry/waterfall.json +14 -0
  301. package/registry/wheel-list.json +17 -0
  302. package/schema/components.schema.json +13 -0
@@ -0,0 +1,111 @@
1
+ {
2
+ "name": "flip-card",
3
+ "category": "3D",
4
+ "aliases": [],
5
+ "exportName": "FlipCard",
6
+ "files": [
7
+ "src/components/ui/flip-card.tsx"
8
+ ],
9
+ "props": [
10
+ [
11
+ "front",
12
+ "ReactNode",
13
+ "—",
14
+ "First face. Sets the card's size."
15
+ ],
16
+ [
17
+ "back",
18
+ "ReactNode",
19
+ "—",
20
+ "Second face, filling the same box."
21
+ ],
22
+ [
23
+ "flipped",
24
+ "boolean",
25
+ "—",
26
+ "Controlled side — true shows the back."
27
+ ],
28
+ [
29
+ "defaultFlipped",
30
+ "boolean",
31
+ "false",
32
+ "Uncontrolled initial side."
33
+ ],
34
+ [
35
+ "onFlip",
36
+ "(flipped: boolean) => void",
37
+ "—",
38
+ "Fires when tapped."
39
+ ],
40
+ [
41
+ "direction",
42
+ "\"horizontal\" | \"vertical\"",
43
+ "\"horizontal\"",
44
+ "Turn around the vertical or the horizontal axis."
45
+ ],
46
+ [
47
+ "trigger",
48
+ "\"press\" | \"none\"",
49
+ "\"press\"",
50
+ "Flip on tap, or only through `flipped`."
51
+ ],
52
+ [
53
+ "perspective",
54
+ "number",
55
+ "1000",
56
+ "Depth of the effect — smaller is more dramatic."
57
+ ],
58
+ [
59
+ "radius",
60
+ "number",
61
+ "20",
62
+ "Corner radius of both faces."
63
+ ],
64
+ [
65
+ "shadow",
66
+ "boolean",
67
+ "true",
68
+ "Soft shadow that narrows as the card turns edge-on."
69
+ ],
70
+ [
71
+ "accessibilityLabel",
72
+ "string",
73
+ "—",
74
+ "Label for screen readers."
75
+ ],
76
+ [
77
+ "labels",
78
+ "{ toBack?: string; toFront?: string }",
79
+ "English hints",
80
+ "Screen-reader hints for each side."
81
+ ],
82
+ [
83
+ "style",
84
+ "StyleProp<ViewStyle>",
85
+ "—",
86
+ "Style for the root (width, margins)."
87
+ ]
88
+ ],
89
+ "examples": [
90
+ {
91
+ "name": "Polaroid",
92
+ "title": "Polaroid",
93
+ "code": "import React, { useState } from 'react';\nimport { Image, Platform, Pressable, Text, View } from 'react-native';\nimport { FlipCard } from '@/components/ui/flip-card';\nimport { useKit } from '@/lib/kit';\n\n/** Any photo works (`require()` or a URL). */\nconst photo = (id: number, w: number, h: number) => ({ uri: `https://picsum.photos/id/${id}/${w}/${h}` });\n\nconst hand = Platform.select({ ios: 'Noteworthy', android: 'casual', default: '\"Bradley Hand\", \"Segoe Print\", cursive' });\n\n/** A screen title, as it sits above the card in your app. */\nfunction Header({ title, sub, right }: { title: string; sub?: string; right?: React.ReactNode }) {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 22, fontWeight: '700', letterSpacing: -0.4 }}>{title}</Text>\n {sub ? <Text style={{ color: colors.mutedForeground, fontSize: 13, marginTop: 2 }}>{sub}</Text> : null}\n </View>\n {right}\n </View>\n );\n}\n\nexport default function Polaroid() {\n const { colors } = useKit();\n const paper = { width: 240, padding: 12, paddingBottom: 0, backgroundColor: '#f7f5f0' } as const;\n return (\n <View style={{ gap: 26 }}>\n <Header title=\"Memories\" sub=\"Lisbon · June 2025 · 24 photos\" />\n <FlipCard\n radius={6}\n accessibilityLabel=\"Polaroid of a Lisbon tram\"\n style={{ alignSelf: 'center', transform: [{ rotate: '-2deg' }] }}\n front={\n <View style={paper}>\n <Image alt=\"\" source={photo(419, 480, 480)} style={{ width: 216, height: 216, backgroundColor: '#ddd' }} />\n <Text style={{ fontFamily: hand, fontSize: 19, color: '#2b2b2b', textAlign: 'center', paddingVertical: 14 }}>Lisbon, June ’25</Text>\n </View>\n }\n back={\n <View style={[paper, { flex: 1, padding: 22, backgroundColor: '#efe9dc', justifyContent: 'space-between' }]}>\n <Text style={{ fontFamily: hand, fontSize: 18, lineHeight: 27, color: '#2f2a22' }}>\n Tram 28 all the way up to Graça. Pastéis de nata still warm. Best trip yet — come back next year?\n </Text>\n <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-end' }}>\n <Text style={{ fontFamily: hand, fontSize: 16, color: '#5b5245' }}>— M.</Text>\n <View style={{ paddingHorizontal: 8, paddingVertical: 4, borderWidth: 1.5, borderColor: '#b04a3a', borderRadius: 4, transform: [{ rotate: '6deg' }] }}>\n <Text style={{ color: '#b04a3a', fontSize: 10, fontWeight: '800', letterSpacing: 1 }}>LISBOA · 14 JUN</Text>\n </View>\n </View>\n </View>\n }\n />\n <Text style={{ color: colors.mutedForeground, fontSize: 13, textAlign: 'center' }}>Tap the photo to read the note on the back</Text>\n </View>\n );\n}\n"
94
+ },
95
+ {
96
+ "name": "Recipe",
97
+ "title": "Recipe",
98
+ "code": "import React, { useState } from 'react';\nimport { Image, Platform, Pressable, Text, View } from 'react-native';\nimport { FlipCard } from '@/components/ui/flip-card';\nimport { useKit } from '@/lib/kit';\n\n/** Any photo works (`require()` or a URL). */\nconst photo = (id: number, w: number, h: number) => ({ uri: `https://picsum.photos/id/${id}/${w}/${h}` });\n\n/** Smooth darkening toward the bottom of a photo, made from thin bands. */\nfunction Scrim({ height = '55%', max = 0.75 }: { height?: `${number}%` | number; max?: number }) {\n const steps = 40; // fine steps: coarse ones read as faint lines on iOS\n return (\n <View pointerEvents=\"none\" style={{ position: 'absolute', left: 0, right: 0, bottom: 0, height }}>\n {Array.from({ length: steps }, (_, i) => (\n <View key={i} style={{ flex: 1, backgroundColor: `rgba(0,0,0,${(max * ((i + 0.5) / steps) ** 1.6).toFixed(3)})` }} />\n ))}\n </View>\n );\n}\n\n/** A screen title, as it sits above the card in your app. */\nfunction Header({ title, sub, right }: { title: string; sub?: string; right?: React.ReactNode }) {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 22, fontWeight: '700', letterSpacing: -0.4 }}>{title}</Text>\n {sub ? <Text style={{ color: colors.mutedForeground, fontSize: 13, marginTop: 2 }}>{sub}</Text> : null}\n </View>\n {right}\n </View>\n );\n}\n\nexport default function Recipe() {\n const { colors } = useKit();\n const items = ['3 parsnips, halved', '2 red onions, quartered', '4 carrots', 'Parsley & dill', 'Olive oil, salt, cracked pepper'];\n return (\n <View style={{ gap: 16 }}>\n <Header title=\"Tonight’s dinner\" sub=\"Picked for you · vegetarian\" />\n <FlipCard\n radius={22}\n accessibilityLabel=\"Roasted root vegetables recipe\"\n front={\n <View style={{ height: 260, backgroundColor: '#222' }}>\n <Image alt=\"\" source={photo(292, 720, 520)} style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }} />\n <Scrim />\n <View style={{ flex: 1, justifyContent: 'flex-end', padding: 18, gap: 2 }}>\n <Text style={{ color: 'rgba(255,255,255,0.8)', fontSize: 11, fontWeight: '700', letterSpacing: 1 }}>45 MIN · SERVES 4</Text>\n <Text style={{ color: '#fff', fontSize: 26, fontWeight: '800', letterSpacing: -0.5 }}>Roasted roots</Text>\n <Text style={{ color: 'rgba(255,255,255,0.75)', fontSize: 12 }}>Tap for ingredients</Text>\n </View>\n </View>\n }\n back={\n <View style={{ flex: 1, padding: 20, backgroundColor: '#f6f1e7', gap: 10 }}>\n <Text style={{ color: '#3b3226', fontSize: 12, fontWeight: '800', letterSpacing: 1.2 }}>INGREDIENTS</Text>\n {items.map((it) => (\n <View key={it} style={{ flexDirection: 'row', alignItems: 'center', gap: 10, paddingBottom: 8, borderBottomWidth: 1, borderBottomColor: 'rgba(59,50,38,0.12)' }}>\n <View style={{ width: 6, height: 6, borderRadius: 3, backgroundColor: '#c9a227' }} />\n <Text style={{ color: '#3b3226', fontSize: 15 }}>{it}</Text>\n </View>\n ))}\n </View>\n }\n />\n <View style={{ flexDirection: 'row', gap: 8 }}>\n {['45 min', 'Serves 4', '520 kcal'].map((t) => (\n <View key={t} style={{ flex: 1, alignItems: 'center', paddingVertical: 9, borderRadius: 12, backgroundColor: colors.muted }}>\n <Text style={{ color: colors.foreground, fontSize: 13, fontWeight: '600' }}>{t}</Text>\n </View>\n ))}\n </View>\n <Pressable accessibilityRole=\"button\" style={{ alignItems: 'center', paddingVertical: 14, borderRadius: 14, backgroundColor: colors.primary }}>\n <Text style={{ color: colors.primaryForeground, fontSize: 15, fontWeight: '600' }}>Start cooking</Text>\n </Pressable>\n </View>\n );\n}\n"
99
+ },
100
+ {
101
+ "name": "Profile",
102
+ "title": "Profile",
103
+ "code": "import React, { useState } from 'react';\nimport { Image, Platform, Pressable, Text, View } from 'react-native';\nimport { FlipCard } from '@/components/ui/flip-card';\nimport { useKit } from '@/lib/kit';\n\n/** Any photo works (`require()` or a URL). */\nconst photo = (id: number, w: number, h: number) => ({ uri: `https://picsum.photos/id/${id}/${w}/${h}` });\n\n/** Smooth darkening toward the bottom of a photo, made from thin bands. */\nfunction Scrim({ height = '55%', max = 0.75 }: { height?: `${number}%` | number; max?: number }) {\n const steps = 40; // fine steps: coarse ones read as faint lines on iOS\n return (\n <View pointerEvents=\"none\" style={{ position: 'absolute', left: 0, right: 0, bottom: 0, height }}>\n {Array.from({ length: steps }, (_, i) => (\n <View key={i} style={{ flex: 1, backgroundColor: `rgba(0,0,0,${(max * ((i + 0.5) / steps) ** 1.6).toFixed(3)})` }} />\n ))}\n </View>\n );\n}\n\n/** A screen title, as it sits above the card in your app. */\nfunction Header({ title, sub, right }: { title: string; sub?: string; right?: React.ReactNode }) {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 22, fontWeight: '700', letterSpacing: -0.4 }}>{title}</Text>\n {sub ? <Text style={{ color: colors.mutedForeground, fontSize: 13, marginTop: 2 }}>{sub}</Text> : null}\n </View>\n {right}\n </View>\n );\n}\n\nexport default function Profile() {\n const { colors } = useKit();\n return (\n <View style={{ gap: 20 }}>\n <Header title=\"Design team\" sub=\"Tap a card to see their year\" />\n <FlipCard\n radius={24}\n accessibilityLabel=\"Ana Lima, lead designer\"\n style={{ alignSelf: 'center', width: 250 }}\n front={\n <View style={{ height: 320, backgroundColor: '#222' }}>\n <Image alt=\"\" source={photo(1027, 500, 640)} style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }} />\n <Scrim />\n <View style={{ flex: 1, justifyContent: 'flex-end', padding: 18 }}>\n <Text style={{ color: '#fff', fontSize: 24, fontWeight: '800', letterSpacing: -0.4 }}>Ana Lima</Text>\n <Text style={{ color: 'rgba(255,255,255,0.8)', fontSize: 13 }}>Lead designer · Lisbon</Text>\n </View>\n </View>\n }\n back={\n <View style={{ flex: 1, padding: 22, backgroundColor: '#141416', justifyContent: 'space-between' }}>\n <Text style={{ color: 'rgba(255,255,255,0.55)', fontSize: 11, fontWeight: '700', letterSpacing: 1.2 }}>THIS YEAR</Text>\n {[\n ['Projects shipped', '24'],\n ['Design reviews', '138'],\n ['Mentees', '6'],\n ].map(([k, v]) => (\n <View key={k}>\n <Text style={{ color: '#fff', fontSize: 34, fontWeight: '800', letterSpacing: -1 }}>{v}</Text>\n <Text style={{ color: 'rgba(255,255,255,0.6)', fontSize: 13 }}>{k}</Text>\n </View>\n ))}\n </View>\n }\n />\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable accessibilityRole=\"button\" style={{ flex: 1, alignItems: 'center', paddingVertical: 14, borderRadius: 14, backgroundColor: colors.primary }}>\n <Text style={{ color: colors.primaryForeground, fontSize: 15, fontWeight: '600' }}>Message Ana</Text>\n </Pressable>\n <Pressable accessibilityRole=\"button\" style={{ flex: 1, alignItems: 'center', paddingVertical: 14, borderRadius: 14, backgroundColor: colors.muted }}>\n <Text style={{ color: colors.foreground, fontSize: 15, fontWeight: '600' }}>View work</Text>\n </Pressable>\n </View>\n </View>\n );\n}\n"
104
+ },
105
+ {
106
+ "name": "Flashcard",
107
+ "title": "Flashcard",
108
+ "code": "import React, { useState } from 'react';\nimport { Image, Platform, Pressable, Text, View } from 'react-native';\nimport { FlipCard } from '@/components/ui/flip-card';\nimport { useKit } from '@/lib/kit';\n\n/** Any photo works (`require()` or a URL). */\nconst photo = (id: number, w: number, h: number) => ({ uri: `https://picsum.photos/id/${id}/${w}/${h}` });\n\n/** Smooth darkening toward the bottom of a photo, made from thin bands. */\nfunction Scrim({ height = '55%', max = 0.75 }: { height?: `${number}%` | number; max?: number }) {\n const steps = 40; // fine steps: coarse ones read as faint lines on iOS\n return (\n <View pointerEvents=\"none\" style={{ position: 'absolute', left: 0, right: 0, bottom: 0, height }}>\n {Array.from({ length: steps }, (_, i) => (\n <View key={i} style={{ flex: 1, backgroundColor: `rgba(0,0,0,${(max * ((i + 0.5) / steps) ** 1.6).toFixed(3)})` }} />\n ))}\n </View>\n );\n}\n\n/** A screen title, as it sits above the card in your app. */\nfunction Header({ title, sub, right }: { title: string; sub?: string; right?: React.ReactNode }) {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 22, fontWeight: '700', letterSpacing: -0.4 }}>{title}</Text>\n {sub ? <Text style={{ color: colors.mutedForeground, fontSize: 13, marginTop: 2 }}>{sub}</Text> : null}\n </View>\n {right}\n </View>\n );\n}\n\nexport default function Flashcard() {\n const { colors } = useKit();\n const [flipped, setFlipped] = useState(false);\n return (\n <View style={{ gap: 14 }}>\n <Header\n title=\"Spanish · Fruit\"\n sub=\"12 cards left today\"\n right={<Text style={{ color: colors.mutedForeground, fontSize: 13, fontWeight: '600' }}>8 / 20</Text>}\n />\n <View style={{ height: 6, borderRadius: 3, backgroundColor: colors.muted, marginBottom: 6 }}>\n <View style={{ width: '40%', height: 6, borderRadius: 3, backgroundColor: colors.success }} />\n </View>\n <FlipCard\n radius={22}\n flipped={flipped}\n onFlip={setFlipped}\n accessibilityLabel=\"Flashcard\"\n front={\n <View style={{ height: 240, backgroundColor: '#3a0d10' }}>\n <Image alt=\"\" source={photo(1080, 720, 400)} style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }} />\n <Scrim max={0.8} />\n <View style={{ flex: 1, justifyContent: 'flex-end', padding: 18 }}>\n <Text style={{ color: 'rgba(255,255,255,0.75)', fontSize: 11, fontWeight: '700', letterSpacing: 1 }}>SPANISH</Text>\n <Text style={{ color: '#fff', fontSize: 30, fontWeight: '800' }}>la fresa</Text>\n </View>\n </View>\n }\n back={\n <View style={{ flex: 1, padding: 20, backgroundColor: colors.card, justifyContent: 'center', gap: 6 }}>\n <Text style={{ color: colors.mutedForeground, fontSize: 11, fontWeight: '700', letterSpacing: 1 }}>ENGLISH</Text>\n <Text style={{ color: colors.foreground, fontSize: 30, fontWeight: '800' }}>the strawberry</Text>\n <Text style={{ color: colors.mutedForeground, fontSize: 14 }}>“Me encantan las fresas con nata.”</Text>\n </View>\n }\n />\n <View style={{ flexDirection: 'row', gap: 10 }}>\n {['Again', 'Good', 'Easy'].map((label) => (\n <Pressable\n key={label}\n onPress={() => setFlipped((f) => !f)}\n accessibilityRole=\"button\"\n style={{ flex: 1, alignItems: 'center', paddingVertical: 12, borderRadius: 12, backgroundColor: colors.muted }}\n >\n <Text style={{ color: colors.foreground, fontSize: 14, fontWeight: '600' }}>{label}</Text>\n </Pressable>\n ))}\n </View>\n </View>\n );\n}\n"
109
+ }
110
+ ]
111
+ }
@@ -0,0 +1,191 @@
1
+ {
2
+ "name": "floating-input",
3
+ "category": "Inputs",
4
+ "aliases": [],
5
+ "exportName": "FloatingInput",
6
+ "files": [
7
+ "src/components/ui/floating-input.tsx"
8
+ ],
9
+ "props": [
10
+ [
11
+ "label",
12
+ "string",
13
+ "—",
14
+ "Placeholder that floats onto the border."
15
+ ],
16
+ [
17
+ "floatingLabel",
18
+ "boolean",
19
+ "true",
20
+ "Set to false to put the label above the field as static text (no notch) and show placeholder inside; filled and underline work too."
21
+ ],
22
+ [
23
+ "variant",
24
+ "\"outlined\" | \"filled\" | \"underline\"",
25
+ "\"outlined\"",
26
+ "Notched outline, rounded filled, or bare underline (filled/underline bars grow from the centre on focus)."
27
+ ],
28
+ [
29
+ "size",
30
+ "\"sm\" | \"md\" | \"lg\"",
31
+ "\"md\"",
32
+ "44 / 54 / 64pt — font, padding, radius and icons scale too."
33
+ ],
34
+ [
35
+ "error",
36
+ "string",
37
+ "—",
38
+ "Red state, icon + message that slides in, one shake."
39
+ ],
40
+ [
41
+ "success",
42
+ "boolean | string",
43
+ "—",
44
+ "Green border + animated check; a string is shown as the message."
45
+ ],
46
+ [
47
+ "helper",
48
+ "string",
49
+ "—",
50
+ "Hint under the field."
51
+ ],
52
+ [
53
+ "prefix / suffix",
54
+ "string",
55
+ "—",
56
+ "Inline text like \"₹\", \"+91\", \"INR\" — fades in when the label floats."
57
+ ],
58
+ [
59
+ "counter",
60
+ "boolean",
61
+ "false",
62
+ "Shows length/maxLength under the field (needs maxLength)."
63
+ ],
64
+ [
65
+ "left / right",
66
+ "ReactNode",
67
+ "—",
68
+ "Icons or adornments inside the field."
69
+ ],
70
+ [
71
+ "clearable",
72
+ "boolean",
73
+ "false",
74
+ "Animated SVG clear button while there's text."
75
+ ],
76
+ [
77
+ "accent",
78
+ "string",
79
+ "theme accent",
80
+ "Focus border, halo and label colour."
81
+ ],
82
+ [
83
+ "surface",
84
+ "string",
85
+ "theme background",
86
+ "Colour behind the field (paints the notch)."
87
+ ],
88
+ [
89
+ "style",
90
+ "StyleProp<ViewStyle>",
91
+ "—",
92
+ "Style for the outer wrapper around the field and message line (the one that shakes)."
93
+ ],
94
+ [
95
+ "fieldStyle",
96
+ "StyleProp<ViewStyle>",
97
+ "—",
98
+ "The bordered box: background, border width / colour, radius, height, padding. Wins over the computed style; a borderColor replaces the resting border only (focus, error and success colours still show). A numeric height / borderRadius re-centres the label and resizes the halo."
99
+ ],
100
+ [
101
+ "inputStyle",
102
+ "StyleProp<TextStyle>",
103
+ "—",
104
+ "The typed text — fontSize, fontWeight, color, letterSpacing, fontFamily."
105
+ ],
106
+ [
107
+ "labelStyle",
108
+ "StyleProp<TextStyle>",
109
+ "—",
110
+ "Label in both modes (floating and above). color is the resting colour — focus (accent), error and success still take over; fontSize sets the resting size, it floats at the same ratio and the notch follows its width."
111
+ ],
112
+ [
113
+ "messageStyle",
114
+ "StyleProp<TextStyle>",
115
+ "—",
116
+ "Helper / error / success text. color applies to the helper; error and success keep their tone colour."
117
+ ],
118
+ [
119
+ "prefixStyle / suffixStyle",
120
+ "StyleProp<TextStyle>",
121
+ "—",
122
+ "Prefix / suffix text (still fades in with the label)."
123
+ ],
124
+ [
125
+ "counterStyle",
126
+ "StyleProp<TextStyle>",
127
+ "—",
128
+ "The length/maxLength counter; turns danger at the limit regardless."
129
+ ],
130
+ [
131
+ "placeholderTextColor",
132
+ "string",
133
+ "theme mutedForeground",
134
+ "Placeholder colour (floatingLabel={false})."
135
+ ],
136
+ [
137
+ "iconColor",
138
+ "string",
139
+ "theme mutedForeground",
140
+ "Clear button colour at rest."
141
+ ],
142
+ [
143
+ "…TextInputProps",
144
+ "",
145
+ "",
146
+ "Everything TextInput accepts."
147
+ ]
148
+ ],
149
+ "examples": [
150
+ {
151
+ "name": "SignUp",
152
+ "title": "Sign Up",
153
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { FloatingInput } from '@/components/ui/floating-input';\nimport { readableOn, useKit } from '@/lib/kit';\n\nfunction Card({ title, subtitle, children }: { title: string; subtitle?: string; children: React.ReactNode }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={{\n padding: 16,\n gap: 12,\n borderRadius: radius.xl,\n borderWidth: StyleSheet.hairlineWidth,\n borderColor: colors.border,\n backgroundColor: colors.card,\n }}\n >\n <View style={{ gap: 2, marginBottom: 2 }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground }}>{title}</Text>\n {subtitle ? <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{subtitle}</Text> : null}\n </View>\n {children}\n </View>\n );\n}\n\nconst isEmail = (v: string) => /^\\S+@\\S+\\.\\S{2,}$/.test(v);\n\nexport default function SignUp() {\n const { colors } = useKit();\n const [name, setName] = useState('Priya Sharma');\n const [email, setEmail] = useState('priya@');\n const [phone, setPhone] = useState('');\n const emailError = email.length > 0 && !isEmail(email) ? 'Enter a valid email address' : undefined;\n return (\n <Card title=\"Create your account\" subtitle=\"It takes less than a minute.\">\n <FloatingInput label=\"Full name\" value={name} onChangeText={setName} autoComplete=\"name\" clearable />\n <FloatingInput\n label=\"Email\"\n value={email}\n onChangeText={setEmail}\n keyboardType=\"email-address\"\n autoCapitalize=\"none\"\n autoCorrect={false}\n error={emailError}\n success={isEmail(email) ? 'Looks good' : undefined}\n />\n <FloatingInput\n label=\"Mobile number\"\n prefix=\"+91\"\n value={phone}\n onChangeText={(v) => setPhone(v.replace(/\\D/g, ''))}\n keyboardType=\"phone-pad\"\n maxLength={10}\n success={phone.length === 10}\n helper=\"We'll text you a 6-digit code\"\n />\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>\n Already have an account? <Text style={{ fontWeight: '400', color: colors.accent }}>Sign in</Text>\n </Text>\n </Card>\n );\n}\n"
154
+ },
155
+ {
156
+ "name": "Payment",
157
+ "title": "Payment",
158
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { FloatingInput } from '@/components/ui/floating-input';\nimport { readableOn, useKit } from '@/lib/kit';\n\nfunction Card({ title, subtitle, children }: { title: string; subtitle?: string; children: React.ReactNode }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={{\n padding: 16,\n gap: 12,\n borderRadius: radius.xl,\n borderWidth: StyleSheet.hairlineWidth,\n borderColor: colors.border,\n backgroundColor: colors.card,\n }}\n >\n <View style={{ gap: 2, marginBottom: 2 }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground }}>{title}</Text>\n {subtitle ? <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{subtitle}</Text> : null}\n </View>\n {children}\n </View>\n );\n}\n\nconst formatCard = (v: string) =>\n v\n .replace(/\\D/g, '')\n .slice(0, 16)\n .replace(/(\\d{4})(?=\\d)/g, '$1 ');\n\nconst formatExpiry = (v: string) => {\n const d = v.replace(/\\D/g, '').slice(0, 4);\n return d.length > 2 ? `${d.slice(0, 2)}/${d.slice(2)}` : d;\n};\n\nconst cardBrand = (v: string) => {\n const d = v.replace(/\\D/g, '');\n if (/^4/.test(d)) return 'VISA';\n if (/^(5[1-5]|2[2-7])/.test(d)) return 'MC';\n if (/^3[47]/.test(d)) return 'AMEX';\n return undefined;\n};\n\n/** '' = not finished yet, otherwise an error message or null when valid. */\nconst expiryError = (v: string): string | null | '' => {\n if (v.length < 5) return '';\n const month = Number(v.slice(0, 2));\n const year = 2000 + Number(v.slice(3));\n if (month < 1 || month > 12) return 'Month must be 01–12';\n const now = new Date();\n if (year < now.getFullYear() || (year === now.getFullYear() && month < now.getMonth() + 1)) return 'This card has expired';\n return null;\n};\n\nconst rupees = (n: number) => {\n const [int, frac] = n.toFixed(2).split('.');\n return `₹${int.replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}${frac === '00' ? '' : `.${frac}`}`;\n};\n\nexport default function Payment() {\n const { colors, radius } = useKit();\n const [amount, setAmount] = useState('1299');\n const [card, setCard] = useState('4242 4242 4242 4242');\n const [expiry, setExpiry] = useState('');\n const [cvc, setCvc] = useState('');\n const [paid, setPaid] = useState(false);\n const brand = cardBrand(card);\n const cvcLen = brand === 'AMEX' ? 4 : 3;\n const expErr = expiryError(expiry);\n const ready = Number(amount) > 0 && card.length >= 18 && expErr === null && cvc.length === cvcLen;\n return (\n <Card title=\"Add funds\" subtitle=\"Charged securely — you can cancel anytime.\">\n <FloatingInput label=\"Amount\" prefix=\"₹\" suffix=\"INR\" keyboardType=\"decimal-pad\" value={amount} onChangeText={setAmount} />\n <FloatingInput\n label=\"Card number\"\n keyboardType=\"number-pad\"\n autoComplete=\"cc-number\"\n value={card}\n onChangeText={(v) => setCard(formatCard(v))}\n maxLength={19}\n suffix={brand}\n success={card.length === 19}\n />\n <View style={{ flexDirection: 'row', gap: 10, alignItems: 'flex-start' }}>\n <FloatingInput\n label=\"MM/YY\"\n keyboardType=\"number-pad\"\n autoComplete=\"cc-exp\"\n value={expiry}\n onChangeText={(v) => setExpiry(formatExpiry(v))}\n maxLength={5}\n error={expErr || undefined}\n success={expErr === null}\n style={{ flex: 1 }}\n />\n <FloatingInput\n label=\"CVC\"\n keyboardType=\"number-pad\"\n autoComplete=\"cc-csc\"\n secureTextEntry\n value={cvc}\n onChangeText={(v) => setCvc(v.replace(/\\D/g, '').slice(0, cvcLen))}\n maxLength={cvcLen}\n helper={`${cvcLen} digits on the ${brand === 'AMEX' ? 'front' : 'back'}`}\n style={{ flex: 1 }}\n />\n </View>\n <Pressable\n disabled={!ready}\n onPress={() => setPaid(true)}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled: !ready }}\n style={({ pressed }) => ({\n height: 50,\n marginTop: 4,\n borderRadius: radius.md,\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: ready ? colors.accent : colors.muted,\n opacity: pressed ? 0.85 : 1,\n })}\n >\n <Text style={{ fontSize: 16, fontWeight: '400', color: ready ? readableOn(colors.accent) : colors.mutedForeground }}>\n {paid ? 'Paid ✓' : `Pay ${rupees(Number(amount) || 0)}`}\n </Text>\n </Pressable>\n </Card>\n );\n}\n"
159
+ },
160
+ {
161
+ "name": "Variants",
162
+ "title": "Variants",
163
+ "code": "import { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { FloatingInput } from '@/components/ui/floating-input';\n\nexport default function Variants() {\n return (\n <View style={{ gap: 18 }}>\n <FloatingInput label=\"Outlined\" defaultValue=\"Notch cuts the border\" helper=\"Tap to see the focus halo\" />\n <FloatingInput variant=\"filled\" label=\"Filled\" defaultValue=\"Bar grows from the centre\" />\n <FloatingInput variant=\"underline\" label=\"Underline\" defaultValue=\"Minimal, for dense forms\" />\n </View>\n );\n}\n"
164
+ },
165
+ {
166
+ "name": "States",
167
+ "title": "States",
168
+ "code": "import { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { FloatingInput } from '@/components/ui/floating-input';\n\nexport default function States() {\n return (\n <View style={{ gap: 16 }}>\n <FloatingInput label=\"Username\" defaultValue=\"admin\" error=\"That username is already taken\" />\n <FloatingInput label=\"Username\" defaultValue=\"priya.codes\" success=\"Nice — it's available\" />\n <FloatingInput label=\"Workspace\" defaultValue=\"Acme Inc.\" editable={false} helper=\"Only owners can rename the workspace\" />\n </View>\n );\n}\n"
169
+ },
170
+ {
171
+ "name": "Website",
172
+ "title": "Website",
173
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { FloatingInput } from '@/components/ui/floating-input';\nimport { readableOn, useKit } from '@/lib/kit';\n\nfunction Card({ title, subtitle, children }: { title: string; subtitle?: string; children: React.ReactNode }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={{\n padding: 16,\n gap: 12,\n borderRadius: radius.xl,\n borderWidth: StyleSheet.hairlineWidth,\n borderColor: colors.border,\n backgroundColor: colors.card,\n }}\n >\n <View style={{ gap: 2, marginBottom: 2 }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground }}>{title}</Text>\n {subtitle ? <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{subtitle}</Text> : null}\n </View>\n {children}\n </View>\n );\n}\n\nexport default function Website() {\n const [slug, setSlug] = useState('');\n return (\n <Card title=\"Your public page\">\n <FloatingInput\n label=\"Website\"\n prefix=\"https://\"\n keyboardType=\"url\"\n autoCapitalize=\"none\"\n autoCorrect={false}\n clearable\n />\n <FloatingInput\n label=\"Handle\"\n prefix=\"@\"\n suffix=\".expo.page\"\n value={slug}\n onChangeText={(v) => setSlug(v.toLowerCase().replace(/[^a-z0-9-]/g, ''))}\n autoCapitalize=\"none\"\n maxLength={20}\n counter\n helper=\"Letters, numbers and dashes\"\n />\n </Card>\n );\n}\n"
174
+ },
175
+ {
176
+ "name": "Sizes",
177
+ "title": "Sizes",
178
+ "code": "import { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { FloatingInput } from '@/components/ui/floating-input';\n\nexport default function Sizes() {\n return (\n <View style={{ gap: 14 }}>\n <FloatingInput size=\"sm\" label=\"Small · 44pt\" defaultValue=\"Compact filters and toolbars\" clearable />\n <FloatingInput size=\"md\" label=\"Medium · 54pt\" defaultValue=\"The default for forms\" clearable />\n <FloatingInput size=\"lg\" label=\"Large · 64pt\" defaultValue=\"Hero fields\" clearable />\n </View>\n );\n}\n"
179
+ },
180
+ {
181
+ "name": "LabelAbove",
182
+ "title": "Label Above",
183
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { FloatingInput } from '@/components/ui/floating-input';\nimport { readableOn, useKit } from '@/lib/kit';\n\nfunction Card({ title, subtitle, children }: { title: string; subtitle?: string; children: React.ReactNode }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={{\n padding: 16,\n gap: 12,\n borderRadius: radius.xl,\n borderWidth: StyleSheet.hairlineWidth,\n borderColor: colors.border,\n backgroundColor: colors.card,\n }}\n >\n <View style={{ gap: 2, marginBottom: 2 }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground }}>{title}</Text>\n {subtitle ? <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{subtitle}</Text> : null}\n </View>\n {children}\n </View>\n );\n}\n\nconst isEmail = (v: string) => /^\\S+@\\S+\\.\\S{2,}$/.test(v);\n\nexport default function LabelAbove() {\n const [name, setName] = useState('');\n const [email, setEmail] = useState('');\n const [bio, setBio] = useState('');\n return (\n <Card title=\"Edit profile\" subtitle=\"This is how others will see you.\">\n <FloatingInput floatingLabel={false} label=\"Full name\" placeholder=\"Priya Sharma\" value={name} onChangeText={setName} autoComplete=\"name\" clearable />\n <FloatingInput\n floatingLabel={false}\n label=\"Email\"\n placeholder=\"you@example.com\"\n value={email}\n onChangeText={setEmail}\n keyboardType=\"email-address\"\n autoCapitalize=\"none\"\n autoCorrect={false}\n error={email.length > 0 && !isEmail(email) ? 'Enter a valid email address' : undefined}\n helper=\"We'll never share it.\"\n />\n <FloatingInput\n floatingLabel={false}\n label=\"Bio\"\n placeholder=\"A line about you\"\n value={bio}\n onChangeText={setBio}\n maxLength={80}\n counter\n />\n </Card>\n );\n}\n"
184
+ },
185
+ {
186
+ "name": "CustomStyles",
187
+ "title": "Custom Styles",
188
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { FloatingInput } from '@/components/ui/floating-input';\nimport { readableOn, useKit } from '@/lib/kit';\n\nfunction Card({ title, subtitle, children }: { title: string; subtitle?: string; children: React.ReactNode }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={{\n padding: 16,\n gap: 12,\n borderRadius: radius.xl,\n borderWidth: StyleSheet.hairlineWidth,\n borderColor: colors.border,\n backgroundColor: colors.card,\n }}\n >\n <View style={{ gap: 2, marginBottom: 2 }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground }}>{title}</Text>\n {subtitle ? <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{subtitle}</Text> : null}\n </View>\n {children}\n </View>\n );\n}\n\nexport default function CustomStyles() {\n const { colors, radius } = useKit();\n const [handle, setHandle] = useState('priya.codes');\n return (\n <Card title=\"Custom styles\" subtitle=\"fieldStyle, labelStyle, inputStyle, messageStyle.\">\n <FloatingInput\n label=\"Username\"\n prefix=\"@\"\n value={handle}\n onChangeText={setHandle}\n autoCapitalize=\"none\"\n autoCorrect={false}\n helper=\"Letters, numbers and dots.\"\n fieldStyle={{ borderWidth: 2, borderColor: colors.border, borderRadius: radius.lg, backgroundColor: colors.card }}\n surface={colors.card}\n labelStyle={{ fontSize: 15, fontWeight: '400', letterSpacing: 0.2 }}\n inputStyle={{ fontSize: 17, fontWeight: '400', letterSpacing: 0.3 }}\n prefixStyle={{ color: colors.accent, fontWeight: '400' }}\n messageStyle={{ fontStyle: 'italic' }}\n />\n </Card>\n );\n}\n"
189
+ }
190
+ ]
191
+ }
@@ -0,0 +1,108 @@
1
+ {
2
+ "name": "gift-card",
3
+ "category": "3D",
4
+ "aliases": [],
5
+ "exportName": "GiftCard",
6
+ "files": [
7
+ "src/components/ui/gift-card.tsx"
8
+ ],
9
+ "props": [
10
+ [
11
+ "amount",
12
+ "string",
13
+ "—",
14
+ "Formatted amount, e.g. \"₹2,000\"."
15
+ ],
16
+ [
17
+ "brand",
18
+ "string",
19
+ "—",
20
+ "Store / brand, top-left."
21
+ ],
22
+ [
23
+ "message",
24
+ "string",
25
+ "—",
26
+ "Short note on the front."
27
+ ],
28
+ [
29
+ "from / to",
30
+ "string",
31
+ "—",
32
+ "Who sent it and who it's for."
33
+ ],
34
+ [
35
+ "code",
36
+ "string",
37
+ "—",
38
+ "Redeem code, revealed on the back (grouped in fours)."
39
+ ],
40
+ [
41
+ "validity",
42
+ "string",
43
+ "—",
44
+ "Expiry line on the back."
45
+ ],
46
+ [
47
+ "onCopy",
48
+ "(code: string) => void",
49
+ "—",
50
+ "Shows a Copy button — wire it to expo-clipboard."
51
+ ],
52
+ [
53
+ "image",
54
+ "ImageSourcePropType",
55
+ "deep plum",
56
+ "Card art — a photo or texture (require() or { uri })."
57
+ ],
58
+ [
59
+ "tone",
60
+ "\"light\" | \"dark\"",
61
+ "\"light\"",
62
+ "Light text for dark art, dark text for pale art."
63
+ ],
64
+ [
65
+ "redeemed",
66
+ "boolean",
67
+ "false",
68
+ "Dim the card with a Redeemed label."
69
+ ],
70
+ [
71
+ "flipped / defaultFlipped / onFlip",
72
+ "boolean / boolean / (flipped) => void",
73
+ "— / false / —",
74
+ "Controlled or uncontrolled side."
75
+ ],
76
+ [
77
+ "tilt",
78
+ "boolean",
79
+ "true",
80
+ "Tilt toward the finger."
81
+ ],
82
+ [
83
+ "radius",
84
+ "number",
85
+ "18",
86
+ "Corner radius."
87
+ ],
88
+ [
89
+ "background",
90
+ "string",
91
+ "\"#2a1630\"",
92
+ "Card colour when there is no image."
93
+ ],
94
+ [
95
+ "labels",
96
+ "{ tag?, for?, from?, redeemed?, code?, back?, copy?, copied? }",
97
+ "English",
98
+ "Every built-in string, for translation."
99
+ ],
100
+ [
101
+ "style",
102
+ "StyleProp<ViewStyle>",
103
+ "—",
104
+ "Style for the root."
105
+ ]
106
+ ],
107
+ "examples": []
108
+ }
@@ -0,0 +1,118 @@
1
+ {
2
+ "name": "glass-button",
3
+ "category": "Glass",
4
+ "aliases": [
5
+ "liquid-glass-button",
6
+ "glass-toolbar",
7
+ "floating-controls"
8
+ ],
9
+ "exportName": "GlassButton",
10
+ "files": [
11
+ "src/components/ui/glass-button.tsx"
12
+ ],
13
+ "props": [
14
+ [
15
+ "label",
16
+ "string",
17
+ "—",
18
+ "Button text. Leave it out (with an icon) for a circular icon button."
19
+ ],
20
+ [
21
+ "icon",
22
+ "ReactNode",
23
+ "—",
24
+ "Icon before the label. `useGlassButtonInk({ variant, tint, scheme })` returns the colour to draw it in."
25
+ ],
26
+ [
27
+ "size",
28
+ "'sm' | 'md' | 'lg'",
29
+ "'md'",
30
+ "Height 36 / 44 / 52; icon-only buttons are circles of that size. `GLASS_ICON_SIZE[size]` is the matching icon size."
31
+ ],
32
+ [
33
+ "variant",
34
+ "'glass' | 'tinted'",
35
+ "'glass'",
36
+ "Plain monochrome glass, or glass washed with `tint` — the label switches to black or white to stay readable."
37
+ ],
38
+ [
39
+ "tint",
40
+ "string",
41
+ "kit accent",
42
+ "Wash colour for the tinted variant."
43
+ ],
44
+ [
45
+ "scheme",
46
+ "'light' | 'dark'",
47
+ "kit theme",
48
+ "Force light or dark glass, e.g. 'dark' over photos."
49
+ ],
50
+ [
51
+ "onPress / disabled",
52
+ "() => void / boolean",
53
+ "— / false",
54
+ "Press handler; disabled dims the button and blocks presses."
55
+ ],
56
+ [
57
+ "haptics",
58
+ "boolean",
59
+ "true",
60
+ "Light impact on press."
61
+ ],
62
+ [
63
+ "accessibilityLabel",
64
+ "string",
65
+ "label",
66
+ "Required for icon-only buttons."
67
+ ],
68
+ [
69
+ "style",
70
+ "StyleProp<ViewStyle>",
71
+ "—",
72
+ "Outer style: margins, position, width or flex (buttons hug their content by default)."
73
+ ],
74
+ [
75
+ "GlassToolbar items",
76
+ "{ key, icon, label, onPress?, active?, disabled? }[]",
77
+ "—",
78
+ "Icon buttons. `icon` is a node or `({ color, size, active }) => node`; `label` is the accessibility label; `active` draws a soft bubble in the accent colour."
79
+ ],
80
+ [
81
+ "GlassToolbar size",
82
+ "'sm' | 'md' | 'lg'",
83
+ "'md'",
84
+ "Item size, 36 / 44 / 52."
85
+ ],
86
+ [
87
+ "GlassToolbar floating",
88
+ "boolean",
89
+ "false",
90
+ "A cluster of separate glass circles (melting together on iOS 26) instead of one capsule."
91
+ ],
92
+ [
93
+ "GlassToolbar direction",
94
+ "'row' | 'column'",
95
+ "'row'",
96
+ "Lay items out horizontally or vertically (map-style controls)."
97
+ ],
98
+ [
99
+ "GlassToolbar separators / gap",
100
+ "boolean / number",
101
+ "true / 8",
102
+ "Hairlines between capsule items; space between floating circles."
103
+ ],
104
+ [
105
+ "GlassToolbar accent",
106
+ "string",
107
+ "kit accent (glass ink)",
108
+ "Colour of active items."
109
+ ],
110
+ [
111
+ "GlassToolbar scheme / haptics / accessibilityLabel / style",
112
+ "—",
113
+ "kit theme / true / — / —",
114
+ "As on GlassButton; accessibilityLabel names the toolbar."
115
+ ]
116
+ ],
117
+ "examples": []
118
+ }
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "glass-nav-bar",
3
+ "category": "Glass",
4
+ "aliases": [],
5
+ "exportName": "GlassNavBar",
6
+ "files": [
7
+ "src/components/ui/glass-nav-bar.tsx"
8
+ ],
9
+ "props": [
10
+ [
11
+ "title",
12
+ "string",
13
+ "—",
14
+ "Centred bar title (and the large title when largeTitle is on)."
15
+ ],
16
+ [
17
+ "largeTitle",
18
+ "boolean",
19
+ "false",
20
+ "Big title under the bar that scrolls away while the small centred title fades in. Needs scrollY."
21
+ ],
22
+ [
23
+ "scrollY",
24
+ "Animated.Value",
25
+ "—",
26
+ "Your scroll view's offset (useGlassNavScroll). The glass fades in over ~40pt as content passes under the bar; without it the bar is solid glass."
27
+ ],
28
+ [
29
+ "onBack",
30
+ "() => void",
31
+ "—",
32
+ "Shows a round glass back button with a chevron."
33
+ ],
34
+ [
35
+ "backLabel",
36
+ "string",
37
+ "—",
38
+ "Text beside the chevron (the button becomes a pill) and its accessibility label; \"Back\" otherwise."
39
+ ],
40
+ [
41
+ "actions",
42
+ "{ key, icon, onPress, accessibilityLabel }[]",
43
+ "[]",
44
+ "Round glass buttons on the right, grouped so they merge on iOS 26."
45
+ ],
46
+ [
47
+ "scheme",
48
+ "'light' | 'dark'",
49
+ "kit theme",
50
+ "Force light or dark glass and ink, e.g. over a photo."
51
+ ],
52
+ [
53
+ "topInset",
54
+ "number",
55
+ "0",
56
+ "Space above the bar for the status bar — pass your safe-area top inset."
57
+ ],
58
+ [
59
+ "style",
60
+ "StyleProp<ViewStyle>",
61
+ "—",
62
+ "Style for the bar container, which pins to the top of its parent."
63
+ ],
64
+ [
65
+ "useGlassNavScroll(listener?)",
66
+ "hook → { scrollY, onScroll }",
67
+ "—",
68
+ "Native-driver Animated.event for an Animated.ScrollView / FlatList (scrollEventThrottle 16)."
69
+ ],
70
+ [
71
+ "GLASS_NAV_HEIGHT / glassNavInset({ largeTitle, topInset })",
72
+ "number / function",
73
+ "54",
74
+ "Bar row height, and the full height the bar covers — use it as your content's paddingTop."
75
+ ]
76
+ ],
77
+ "examples": []
78
+ }