@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,219 @@
1
+ import React, { createContext, useContext, useMemo } from 'react';
2
+ import { Platform, useColorScheme, type TextStyle } from 'react-native';
3
+
4
+ /**
5
+ * ExpoKit design tokens. Every component reads them through useKit(), so one
6
+ * <KitProvider> (optional) re-skins the whole kit: light / dark, accent colour, radius.
7
+ * Without a provider, components follow the system colour scheme.
8
+ */
9
+
10
+ /**
11
+ * Bumped whenever components start using a new kit API (2: radius.scale / radius.capsule).
12
+ * The CLI won't keep an edited kit.tsx older than the component being added needs.
13
+ */
14
+ export const KIT_VERSION = 2;
15
+
16
+ export type ColorScheme = 'light' | 'dark';
17
+
18
+ export interface Colors {
19
+ background: string;
20
+ foreground: string;
21
+ /** Cards, sheets, inputs. */
22
+ card: string;
23
+ cardForeground: string;
24
+ /** Raised surfaces: popovers, toasts. */
25
+ popover: string;
26
+ muted: string;
27
+ mutedForeground: string;
28
+ border: string;
29
+ input: string;
30
+ primary: string;
31
+ primaryForeground: string;
32
+ secondary: string;
33
+ secondaryForeground: string;
34
+ accent: string;
35
+ accentForeground: string;
36
+ success: string;
37
+ warning: string;
38
+ danger: string;
39
+ dangerForeground: string;
40
+ ring: string;
41
+ overlay: string;
42
+ }
43
+
44
+ export interface Theme {
45
+ dark: boolean;
46
+ colors: Colors;
47
+ /** Corner radius scale. `radius.full` makes pills. */
48
+ radius: {
49
+ sm: number;
50
+ md: number;
51
+ lg: number;
52
+ xl: number;
53
+ full: number;
54
+ /** A corner designed at the default radius (12), scaled to the current one: `radius.scale(16)` is 16 at 12, 0 at 0. */
55
+ scale: (n: number) => number;
56
+ /** Corner for a bar or chip of height `h`: a pill at radius ≥ 12, softer below, square at 0. */
57
+ capsule: (h: number) => number;
58
+ };
59
+ /** 4pt spacing scale: space(4) === 16. */
60
+ space: (n: number) => number;
61
+ font: { family?: string; mono: string };
62
+ text: Record<'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl', TextStyle>;
63
+ }
64
+
65
+ const light: Colors = {
66
+ background: '#ffffff',
67
+ foreground: '#09090b',
68
+ card: '#ffffff',
69
+ cardForeground: '#09090b',
70
+ popover: '#ffffff',
71
+ muted: '#f4f4f5',
72
+ mutedForeground: '#71717a',
73
+ border: '#e4e4e7',
74
+ input: '#d4d4d8',
75
+ primary: '#18181b',
76
+ primaryForeground: '#fafafa',
77
+ secondary: '#f4f4f5',
78
+ secondaryForeground: '#18181b',
79
+ accent: '#18181b',
80
+ accentForeground: '#ffffff',
81
+ success: '#16a34a',
82
+ warning: '#f59e0b',
83
+ danger: '#e5484d',
84
+ dangerForeground: '#ffffff',
85
+ ring: '#18181b',
86
+ overlay: 'rgba(0,0,0,0.4)',
87
+ };
88
+
89
+ const dark: Colors = {
90
+ background: '#09090b',
91
+ foreground: '#fafafa',
92
+ card: '#111113',
93
+ cardForeground: '#fafafa',
94
+ popover: '#18181b',
95
+ muted: '#1c1c20',
96
+ mutedForeground: '#a1a1aa',
97
+ border: '#27272a',
98
+ input: '#34343a',
99
+ primary: '#fafafa',
100
+ primaryForeground: '#18181b',
101
+ secondary: '#1f1f23',
102
+ secondaryForeground: '#fafafa',
103
+ accent: '#fafafa',
104
+ accentForeground: '#09090b',
105
+ success: '#3dd68c',
106
+ warning: '#ffb224',
107
+ danger: '#ff6369',
108
+ dangerForeground: '#ffffff',
109
+ ring: '#d4d4d8',
110
+ overlay: 'rgba(0,0,0,0.6)',
111
+ };
112
+
113
+ export const palettes = { light, dark };
114
+
115
+ function build(scheme: ColorScheme, overrides?: ThemeOverrides): Theme {
116
+ const base = scheme === 'dark' ? dark : light;
117
+ const accent = overrides?.accent;
118
+ const colors: Colors = {
119
+ ...base,
120
+ ...(accent ? { accent, ring: accent, accentForeground: readableOn(accent) } : null),
121
+ ...overrides?.colors?.[scheme],
122
+ };
123
+ const r = overrides?.radius ?? 12;
124
+ return {
125
+ dark: scheme === 'dark',
126
+ colors,
127
+ radius: {
128
+ sm: Math.round(r * 0.5),
129
+ md: r,
130
+ lg: Math.round(r * 1.35),
131
+ xl: Math.round(r * 1.8),
132
+ full: 999,
133
+ scale: (n) => Math.round((n * r) / 12),
134
+ capsule: (h) => Math.round((h / 2) * Math.min(1, r / 12)),
135
+ },
136
+ space: (n) => n * 4,
137
+ font: {
138
+ family: overrides?.fontFamily,
139
+ mono: Platform.select({ ios: 'Menlo', android: 'monospace', default: 'ui-monospace, SFMono-Regular, Menlo, monospace' })!,
140
+ },
141
+ text: {
142
+ xs: { fontSize: 12, lineHeight: 16 },
143
+ sm: { fontSize: 14, lineHeight: 20 },
144
+ md: { fontSize: 16, lineHeight: 22 },
145
+ lg: { fontSize: 18, lineHeight: 24, fontWeight: '400' },
146
+ xl: { fontSize: 22, lineHeight: 28, fontWeight: '400', letterSpacing: -0.3 },
147
+ xxl: { fontSize: 30, lineHeight: 36, fontWeight: '400', letterSpacing: -0.8 },
148
+ },
149
+ };
150
+ }
151
+
152
+ export interface ThemeOverrides {
153
+ /** Brand colour used for focus rings, selected states and accent buttons. */
154
+ accent?: string;
155
+ /** Base corner radius in points (default 12). */
156
+ radius?: number;
157
+ fontFamily?: string;
158
+ /** Override any token per scheme. */
159
+ colors?: Partial<Record<ColorScheme, Partial<Colors>>>;
160
+ }
161
+
162
+ const KitContext = createContext<{ scheme?: ColorScheme; overrides?: ThemeOverrides } | null>(null);
163
+
164
+ export function KitProvider({
165
+ scheme,
166
+ children,
167
+ ...overrides
168
+ }: ThemeOverrides & { scheme?: ColorScheme | 'system'; children: React.ReactNode }) {
169
+ const value = useMemo(
170
+ () => ({ scheme: scheme === 'system' ? undefined : scheme, overrides }),
171
+ // eslint-disable-next-line react-hooks/exhaustive-deps
172
+ [scheme, overrides.accent, overrides.radius, overrides.fontFamily, overrides.colors]
173
+ );
174
+ return <KitContext.Provider value={value}>{children}</KitContext.Provider>;
175
+ }
176
+
177
+ export function useKit(): Theme {
178
+ const ctx = useContext(KitContext);
179
+ const system = useColorScheme();
180
+ const scheme: ColorScheme = ctx?.scheme ?? (system === 'dark' ? 'dark' : 'light');
181
+ return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
182
+ }
183
+
184
+ /** Shared motion presets so the whole kit moves the same way. */
185
+ export const motion = {
186
+ /** Toggles, selection indicators. */
187
+ snappy: { damping: 18, stiffness: 260, mass: 0.8 },
188
+ /** Press feedback, pops. */
189
+ bouncy: { damping: 11, stiffness: 220, mass: 0.7 },
190
+ /** Sheets, layout changes. */
191
+ gentle: { damping: 22, stiffness: 160, mass: 1 },
192
+ fast: 160,
193
+ normal: 240,
194
+ };
195
+
196
+ /** Adds alpha to a #rrggbb colour: alpha('#6366f1', 0.2). */
197
+ export function alpha(hex: string, a: number) {
198
+ if (!hex.startsWith('#') || (hex.length !== 7 && hex.length !== 4)) return hex;
199
+ const full = hex.length === 4 ? '#' + [...hex.slice(1)].map((c) => c + c).join('') : hex;
200
+ return full + Math.round(Math.max(0, Math.min(1, a)) * 255).toString(16).padStart(2, '0');
201
+ }
202
+
203
+ /** Black or white, whichever reads better on `hex`. */
204
+ export function readableOn(hex: string) {
205
+ const full = hex.length === 4 ? '#' + [...hex.slice(1)].map((c) => c + c).join('') : hex;
206
+ const n = parseInt(full.slice(1, 7), 16);
207
+ if (Number.isNaN(n)) return '#ffffff';
208
+ const [r, g, b] = [(n >> 16) & 255, (n >> 8) & 255, n & 255];
209
+ return (r * 299 + g * 587 + b * 114) / 1000 > 150 ? '#09090b' : '#ffffff';
210
+ }
211
+
212
+ /**
213
+ * True for children that must be wrapped in <Text>: strings, numbers and arrays of them —
214
+ * `{count} new` is the array [3, ' new'], and raw text outside <Text> crashes on native.
215
+ */
216
+ export function isTextNode(node: React.ReactNode): node is string | number | (string | number)[] {
217
+ if (typeof node === 'string' || typeof node === 'number') return true;
218
+ return Array.isArray(node) && node.length > 0 && node.every((n) => typeof n === 'string' || typeof n === 'number');
219
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "floating-input",
3
+ "type": "component",
4
+ "title": "Floating Input",
5
+ "description": "The label starts as the placeholder and glides onto the border when you focus — a soft accent halo blooms around the field, errors shake in with an icon, valid values pop a spring check. Prefix/suffix, clear button, counter and three variants whose indicator grows from the centre.",
6
+ "tier": "free",
7
+ "dependencies": [
8
+ "react-native-svg"
9
+ ],
10
+ "registryDependencies": [
11
+ "kit"
12
+ ],
13
+ "sharedFiles": [
14
+ "lib/kit.tsx"
15
+ ],
16
+ "filesDir": "floating-input/files"
17
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "gift-card",
3
+ "type": "component",
4
+ "title": "Gift Card",
5
+ "description": "A gift card on any photo — the amount, a note and who it's for on the front; tap to flip it and read the redeem code off a brushed-silver panel, with a copy button. Tilts toward your finger with a moving reflection; redeemed state.",
6
+ "tier": "pro",
7
+ "dependencies": [
8
+ "react-native-svg"
9
+ ],
10
+ "registryDependencies": [
11
+ "kit",
12
+ "tilt-card",
13
+ "flip-card"
14
+ ],
15
+ "sharedFiles": [
16
+ "lib/kit.tsx",
17
+ "components/ui/tilt-card.tsx",
18
+ "components/ui/flip-card.tsx"
19
+ ],
20
+ "filesDir": null
21
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "glass-button",
3
+ "type": "component",
4
+ "title": "Glass Button",
5
+ "description": "Liquid Glass buttons and toolbars. GlassButton: label, icon or both in three sizes (icon-only is a circle), plain monochrome glass or tinted glass for the primary action, a spring press and a light haptic — real interactive Liquid Glass on iOS 26, blur everywhere else. GlassToolbar: icon buttons in one glass capsule with hairline separators, or a floating cluster of glass circles that melt into each other on iOS 26, with an active state for tools and toggles.",
6
+ "tier": "pro",
7
+ "dependencies": [
8
+ "expo-glass-effect",
9
+ "expo-blur",
10
+ "react-native-svg",
11
+ "expo-haptics"
12
+ ],
13
+ "registryDependencies": [
14
+ "kit",
15
+ "glass"
16
+ ],
17
+ "sharedFiles": [
18
+ "lib/kit.tsx",
19
+ "components/ui/glass.tsx"
20
+ ],
21
+ "filesDir": null
22
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "glass-nav-bar",
3
+ "type": "component",
4
+ "title": "Glass Nav Bar",
5
+ "description": "The iOS 26 top bar in Liquid Glass: a centred title, a round glass back button and round glass actions that melt together on iOS 26. Wire it to a ScrollView with useGlassNavScroll() and the bar starts transparent, its glass fading in as content slides under it, while an optional large title shrinks and collapses into the bar — all on the native driver. Real Liquid Glass on iOS 26, blur on older iOS, Android and web.",
6
+ "tier": "pro",
7
+ "dependencies": [
8
+ "expo-glass-effect",
9
+ "expo-blur",
10
+ "react-native-svg"
11
+ ],
12
+ "registryDependencies": [
13
+ "kit",
14
+ "glass"
15
+ ],
16
+ "sharedFiles": [
17
+ "lib/kit.tsx",
18
+ "components/ui/glass.tsx"
19
+ ],
20
+ "filesDir": null
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "glass-search-bar",
3
+ "type": "component",
4
+ "title": "Glass Search Bar",
5
+ "description": "iOS 26 floating search pill on Liquid Glass. The pill swells a touch on focus, a clear button pops in with text, and an optional round glass cancel button (or a Cancel capsule) splits off beside it — grouped, so on iOS 26 the two melt together as it springs in. Leading / trailing slots fit a custom icon or a mic button. Real Liquid Glass on iOS 26, blur or frosted glass everywhere else.",
6
+ "tier": "pro",
7
+ "dependencies": [
8
+ "expo-glass-effect",
9
+ "expo-blur",
10
+ "react-native-svg"
11
+ ],
12
+ "registryDependencies": [
13
+ "kit",
14
+ "glass"
15
+ ],
16
+ "sharedFiles": [
17
+ "lib/kit.tsx",
18
+ "components/ui/glass.tsx"
19
+ ],
20
+ "filesDir": null
21
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "glass-segmented",
3
+ "type": "component",
4
+ "title": "Glass Segmented Control",
5
+ "description": "A Liquid Glass segmented control: a glass track with a brighter glass thumb that springs to the new segment, stretching in the direction of travel before it settles like a drop. Press the thumb and drag it across the segments — it follows your finger, ticks as it crosses each one and snaps to the nearest on release. Native-driver motion, Reduce Motion aware, monochrome unless you give it an accent.",
6
+ "tier": "pro",
7
+ "dependencies": [
8
+ "expo-glass-effect",
9
+ "expo-blur",
10
+ "react-native-svg",
11
+ "expo-haptics"
12
+ ],
13
+ "registryDependencies": [
14
+ "kit",
15
+ "glass"
16
+ ],
17
+ "sharedFiles": [
18
+ "lib/kit.tsx",
19
+ "components/ui/glass.tsx"
20
+ ],
21
+ "filesDir": null
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "glass-sheet",
3
+ "type": "component",
4
+ "title": "Glass Sheet",
5
+ "description": "iOS 26 floating glass sheet. Detached by default — it floats inset from the screen edges with big rounded corners — or docked edge-to-edge. It springs up over a fading dim, follows your finger from the grabber or the content, settles on the snap point projected from your release velocity (a fling down dismisses) with a haptic tick, and sizes itself to its content when no snap points are given; taller content scrolls. Real Liquid Glass on iOS 26, material blur on older iOS and web, a readable frosted surface on Android.",
6
+ "tier": "pro",
7
+ "dependencies": [
8
+ "expo-glass-effect",
9
+ "expo-blur",
10
+ "react-native-svg",
11
+ "expo-haptics"
12
+ ],
13
+ "registryDependencies": [
14
+ "kit",
15
+ "glass"
16
+ ],
17
+ "sharedFiles": [
18
+ "lib/kit.tsx",
19
+ "components/ui/glass.tsx"
20
+ ],
21
+ "filesDir": null
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "glass-tab-bar",
3
+ "type": "component",
4
+ "title": "Glass Tab Bar",
5
+ "description": "A floating Liquid Glass tab bar like iOS 26: a glass pill whose selected tab sits in a lighter bubble that springs between tabs, stretching as it travels and settling when it lands. Icons lift when selected, red count badges, optional labels and a separate circular trailing button (search) grouped with the bar so the two melt together on iOS 26. Real Liquid Glass on iOS 26, blur everywhere else; native-driver motion that respects Reduce Motion, selection haptics and a tablist for screen readers.",
6
+ "tier": "pro",
7
+ "dependencies": [
8
+ "expo-glass-effect",
9
+ "expo-blur",
10
+ "react-native-svg",
11
+ "expo-haptics"
12
+ ],
13
+ "registryDependencies": [
14
+ "kit",
15
+ "glass"
16
+ ],
17
+ "sharedFiles": [
18
+ "lib/kit.tsx",
19
+ "components/ui/glass.tsx"
20
+ ],
21
+ "filesDir": null
22
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "glass",
3
+ "type": "component",
4
+ "title": "Glass",
5
+ "description": "Liquid Glass for every device: real iOS 26 Liquid Glass (interactive, mergeable with GlassGroup), system material blur on older iOS, real blur on Android 12+ inside GlassProvider / GlassBackdrop (a frosted surface otherwise) and backdrop blur on web — with a specular rim, hairline edge and soft shadow wherever the glass is emulated. The base every glass component is built on.",
6
+ "tier": "pro",
7
+ "dependencies": [
8
+ "expo-glass-effect",
9
+ "expo-blur",
10
+ "react-native-svg"
11
+ ],
12
+ "registryDependencies": [
13
+ "kit"
14
+ ],
15
+ "sharedFiles": [
16
+ "lib/kit.tsx"
17
+ ],
18
+ "filesDir": null
19
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "inbox",
3
+ "type": "block",
4
+ "title": "Inbox",
5
+ "description": "A messaging inbox: search, All / Unread / Mentions filter chips with counts, swipeable rows to archive, delete or mark read, unread count badges and a friendly empty state.",
6
+ "tier": "pro",
7
+ "dependencies": [
8
+ "react-native-svg",
9
+ "expo-haptics"
10
+ ],
11
+ "registryDependencies": [
12
+ "kit",
13
+ "avatar",
14
+ "badge",
15
+ "button",
16
+ "chip-group",
17
+ "search-bar",
18
+ "swipeable-row"
19
+ ],
20
+ "sharedFiles": [
21
+ "lib/kit.tsx",
22
+ "components/ui/avatar.tsx",
23
+ "components/ui/badge.tsx",
24
+ "components/ui/button.tsx",
25
+ "components/ui/chip-group.tsx",
26
+ "components/ui/search-bar.tsx",
27
+ "components/ui/swipeable-row.tsx"
28
+ ],
29
+ "filesDir": null
30
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "isometric-bar",
3
+ "type": "chart",
4
+ "title": "Isometric Bars",
5
+ "description": "3D bars with depth, shaded sides and lids; the tallest bar can be highlighted. Morphs between datasets. Built on expo-charts.",
6
+ "tier": "pro",
7
+ "dependencies": [
8
+ "expo-charts",
9
+ "react-native-svg"
10
+ ],
11
+ "registryDependencies": [],
12
+ "sharedFiles": [],
13
+ "filesDir": null
14
+ }
@@ -0,0 +1,219 @@
1
+ import React, { createContext, useContext, useMemo } from 'react';
2
+ import { Platform, useColorScheme, type TextStyle } from 'react-native';
3
+
4
+ /**
5
+ * ExpoKit design tokens. Every component reads them through useKit(), so one
6
+ * <KitProvider> (optional) re-skins the whole kit: light / dark, accent colour, radius.
7
+ * Without a provider, components follow the system colour scheme.
8
+ */
9
+
10
+ /**
11
+ * Bumped whenever components start using a new kit API (2: radius.scale / radius.capsule).
12
+ * The CLI won't keep an edited kit.tsx older than the component being added needs.
13
+ */
14
+ export const KIT_VERSION = 2;
15
+
16
+ export type ColorScheme = 'light' | 'dark';
17
+
18
+ export interface Colors {
19
+ background: string;
20
+ foreground: string;
21
+ /** Cards, sheets, inputs. */
22
+ card: string;
23
+ cardForeground: string;
24
+ /** Raised surfaces: popovers, toasts. */
25
+ popover: string;
26
+ muted: string;
27
+ mutedForeground: string;
28
+ border: string;
29
+ input: string;
30
+ primary: string;
31
+ primaryForeground: string;
32
+ secondary: string;
33
+ secondaryForeground: string;
34
+ accent: string;
35
+ accentForeground: string;
36
+ success: string;
37
+ warning: string;
38
+ danger: string;
39
+ dangerForeground: string;
40
+ ring: string;
41
+ overlay: string;
42
+ }
43
+
44
+ export interface Theme {
45
+ dark: boolean;
46
+ colors: Colors;
47
+ /** Corner radius scale. `radius.full` makes pills. */
48
+ radius: {
49
+ sm: number;
50
+ md: number;
51
+ lg: number;
52
+ xl: number;
53
+ full: number;
54
+ /** A corner designed at the default radius (12), scaled to the current one: `radius.scale(16)` is 16 at 12, 0 at 0. */
55
+ scale: (n: number) => number;
56
+ /** Corner for a bar or chip of height `h`: a pill at radius ≥ 12, softer below, square at 0. */
57
+ capsule: (h: number) => number;
58
+ };
59
+ /** 4pt spacing scale: space(4) === 16. */
60
+ space: (n: number) => number;
61
+ font: { family?: string; mono: string };
62
+ text: Record<'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl', TextStyle>;
63
+ }
64
+
65
+ const light: Colors = {
66
+ background: '#ffffff',
67
+ foreground: '#09090b',
68
+ card: '#ffffff',
69
+ cardForeground: '#09090b',
70
+ popover: '#ffffff',
71
+ muted: '#f4f4f5',
72
+ mutedForeground: '#71717a',
73
+ border: '#e4e4e7',
74
+ input: '#d4d4d8',
75
+ primary: '#18181b',
76
+ primaryForeground: '#fafafa',
77
+ secondary: '#f4f4f5',
78
+ secondaryForeground: '#18181b',
79
+ accent: '#18181b',
80
+ accentForeground: '#ffffff',
81
+ success: '#16a34a',
82
+ warning: '#f59e0b',
83
+ danger: '#e5484d',
84
+ dangerForeground: '#ffffff',
85
+ ring: '#18181b',
86
+ overlay: 'rgba(0,0,0,0.4)',
87
+ };
88
+
89
+ const dark: Colors = {
90
+ background: '#09090b',
91
+ foreground: '#fafafa',
92
+ card: '#111113',
93
+ cardForeground: '#fafafa',
94
+ popover: '#18181b',
95
+ muted: '#1c1c20',
96
+ mutedForeground: '#a1a1aa',
97
+ border: '#27272a',
98
+ input: '#34343a',
99
+ primary: '#fafafa',
100
+ primaryForeground: '#18181b',
101
+ secondary: '#1f1f23',
102
+ secondaryForeground: '#fafafa',
103
+ accent: '#fafafa',
104
+ accentForeground: '#09090b',
105
+ success: '#3dd68c',
106
+ warning: '#ffb224',
107
+ danger: '#ff6369',
108
+ dangerForeground: '#ffffff',
109
+ ring: '#d4d4d8',
110
+ overlay: 'rgba(0,0,0,0.6)',
111
+ };
112
+
113
+ export const palettes = { light, dark };
114
+
115
+ function build(scheme: ColorScheme, overrides?: ThemeOverrides): Theme {
116
+ const base = scheme === 'dark' ? dark : light;
117
+ const accent = overrides?.accent;
118
+ const colors: Colors = {
119
+ ...base,
120
+ ...(accent ? { accent, ring: accent, accentForeground: readableOn(accent) } : null),
121
+ ...overrides?.colors?.[scheme],
122
+ };
123
+ const r = overrides?.radius ?? 12;
124
+ return {
125
+ dark: scheme === 'dark',
126
+ colors,
127
+ radius: {
128
+ sm: Math.round(r * 0.5),
129
+ md: r,
130
+ lg: Math.round(r * 1.35),
131
+ xl: Math.round(r * 1.8),
132
+ full: 999,
133
+ scale: (n) => Math.round((n * r) / 12),
134
+ capsule: (h) => Math.round((h / 2) * Math.min(1, r / 12)),
135
+ },
136
+ space: (n) => n * 4,
137
+ font: {
138
+ family: overrides?.fontFamily,
139
+ mono: Platform.select({ ios: 'Menlo', android: 'monospace', default: 'ui-monospace, SFMono-Regular, Menlo, monospace' })!,
140
+ },
141
+ text: {
142
+ xs: { fontSize: 12, lineHeight: 16 },
143
+ sm: { fontSize: 14, lineHeight: 20 },
144
+ md: { fontSize: 16, lineHeight: 22 },
145
+ lg: { fontSize: 18, lineHeight: 24, fontWeight: '400' },
146
+ xl: { fontSize: 22, lineHeight: 28, fontWeight: '400', letterSpacing: -0.3 },
147
+ xxl: { fontSize: 30, lineHeight: 36, fontWeight: '400', letterSpacing: -0.8 },
148
+ },
149
+ };
150
+ }
151
+
152
+ export interface ThemeOverrides {
153
+ /** Brand colour used for focus rings, selected states and accent buttons. */
154
+ accent?: string;
155
+ /** Base corner radius in points (default 12). */
156
+ radius?: number;
157
+ fontFamily?: string;
158
+ /** Override any token per scheme. */
159
+ colors?: Partial<Record<ColorScheme, Partial<Colors>>>;
160
+ }
161
+
162
+ const KitContext = createContext<{ scheme?: ColorScheme; overrides?: ThemeOverrides } | null>(null);
163
+
164
+ export function KitProvider({
165
+ scheme,
166
+ children,
167
+ ...overrides
168
+ }: ThemeOverrides & { scheme?: ColorScheme | 'system'; children: React.ReactNode }) {
169
+ const value = useMemo(
170
+ () => ({ scheme: scheme === 'system' ? undefined : scheme, overrides }),
171
+ // eslint-disable-next-line react-hooks/exhaustive-deps
172
+ [scheme, overrides.accent, overrides.radius, overrides.fontFamily, overrides.colors]
173
+ );
174
+ return <KitContext.Provider value={value}>{children}</KitContext.Provider>;
175
+ }
176
+
177
+ export function useKit(): Theme {
178
+ const ctx = useContext(KitContext);
179
+ const system = useColorScheme();
180
+ const scheme: ColorScheme = ctx?.scheme ?? (system === 'dark' ? 'dark' : 'light');
181
+ return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
182
+ }
183
+
184
+ /** Shared motion presets so the whole kit moves the same way. */
185
+ export const motion = {
186
+ /** Toggles, selection indicators. */
187
+ snappy: { damping: 18, stiffness: 260, mass: 0.8 },
188
+ /** Press feedback, pops. */
189
+ bouncy: { damping: 11, stiffness: 220, mass: 0.7 },
190
+ /** Sheets, layout changes. */
191
+ gentle: { damping: 22, stiffness: 160, mass: 1 },
192
+ fast: 160,
193
+ normal: 240,
194
+ };
195
+
196
+ /** Adds alpha to a #rrggbb colour: alpha('#6366f1', 0.2). */
197
+ export function alpha(hex: string, a: number) {
198
+ if (!hex.startsWith('#') || (hex.length !== 7 && hex.length !== 4)) return hex;
199
+ const full = hex.length === 4 ? '#' + [...hex.slice(1)].map((c) => c + c).join('') : hex;
200
+ return full + Math.round(Math.max(0, Math.min(1, a)) * 255).toString(16).padStart(2, '0');
201
+ }
202
+
203
+ /** Black or white, whichever reads better on `hex`. */
204
+ export function readableOn(hex: string) {
205
+ const full = hex.length === 4 ? '#' + [...hex.slice(1)].map((c) => c + c).join('') : hex;
206
+ const n = parseInt(full.slice(1, 7), 16);
207
+ if (Number.isNaN(n)) return '#ffffff';
208
+ const [r, g, b] = [(n >> 16) & 255, (n >> 8) & 255, n & 255];
209
+ return (r * 299 + g * 587 + b * 114) / 1000 > 150 ? '#09090b' : '#ffffff';
210
+ }
211
+
212
+ /**
213
+ * True for children that must be wrapped in <Text>: strings, numbers and arrays of them —
214
+ * `{count} new` is the array [3, ' new'], and raw text outside <Text> crashes on native.
215
+ */
216
+ export function isTextNode(node: React.ReactNode): node is string | number | (string | number)[] {
217
+ if (typeof node === 'string' || typeof node === 'number') return true;
218
+ return Array.isArray(node) && node.length > 0 && node.every((n) => typeof n === 'string' || typeof n === 'number');
219
+ }