@expokit/cli 0.2.0 → 0.4.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 (232) hide show
  1. package/README.md +20 -2
  2. package/bin/expo-kit.js +219 -44
  3. package/bin/lib.mjs +110 -1
  4. package/bin/mcp.mjs +48 -19
  5. package/package.json +9 -4
  6. package/registry/_docs/accordion.json +16 -0
  7. package/registry/_docs/action-sheet.json +28 -0
  8. package/registry/_docs/alert.json +28 -0
  9. package/registry/_docs/assistant-app.json +47 -0
  10. package/registry/_docs/avatar.json +25 -1
  11. package/registry/_docs/badge.json +36 -5
  12. package/registry/_docs/bottom-nav.json +30 -6
  13. package/registry/_docs/bottom-sheet.json +28 -0
  14. package/registry/_docs/bullet.json +3 -0
  15. package/registry/_docs/button-3d.json +18 -2
  16. package/registry/_docs/button.json +16 -0
  17. package/registry/_docs/calendar.json +21 -5
  18. package/registry/_docs/card-stack.json +16 -0
  19. package/registry/_docs/card.json +20 -0
  20. package/registry/_docs/carousel.json +20 -0
  21. package/registry/_docs/chat.json +184 -0
  22. package/registry/_docs/checkbox.json +16 -0
  23. package/registry/_docs/checkout.json +10 -1
  24. package/registry/_docs/chip-group.json +16 -0
  25. package/registry/_docs/confetti.json +23 -3
  26. package/registry/_docs/coupon.json +16 -0
  27. package/registry/_docs/cube-pager.json +16 -0
  28. package/registry/_docs/dashboard.json +3 -0
  29. package/registry/_docs/date-picker.json +24 -4
  30. package/registry/_docs/dialog.json +24 -0
  31. package/registry/_docs/donut-shop.json +88 -0
  32. package/registry/_docs/drawer.json +32 -4
  33. package/registry/_docs/fab.json +24 -0
  34. package/registry/_docs/fintech-app.json +49 -0
  35. package/registry/_docs/flip-card.json +16 -0
  36. package/registry/_docs/floating-input.json +25 -5
  37. package/registry/_docs/food-app.json +47 -0
  38. package/registry/_docs/gift-card.json +16 -0
  39. package/registry/_docs/glass-button.json +24 -0
  40. package/registry/_docs/glass-nav-bar.json +24 -0
  41. package/registry/_docs/glass-search-bar.json +24 -0
  42. package/registry/_docs/glass-segmented.json +28 -0
  43. package/registry/_docs/glass-sheet.json +28 -0
  44. package/registry/_docs/glass-tab-bar.json +24 -0
  45. package/registry/_docs/glass.json +17 -1
  46. package/registry/_docs/grain.json +80 -0
  47. package/registry/_docs/inbox.json +3 -0
  48. package/registry/_docs/isometric-bar.json +3 -0
  49. package/registry/_docs/kit.json +3 -0
  50. package/registry/_docs/liquidglass-tab.json +3 -0
  51. package/registry/_docs/list-item.json +16 -0
  52. package/registry/_docs/loyalty-card.json +16 -0
  53. package/registry/_docs/morphing-button-group.json +96 -0
  54. package/registry/_docs/number-ticker.json +16 -0
  55. package/registry/_docs/offers.json +3 -0
  56. package/registry/_docs/onboarding.json +3 -0
  57. package/registry/_docs/orb-reveal.json +132 -0
  58. package/registry/_docs/otp-input.json +28 -4
  59. package/registry/_docs/parallax-layers.json +20 -0
  60. package/registry/_docs/parallax-scroll.json +24 -0
  61. package/registry/_docs/password-input.json +23 -3
  62. package/registry/_docs/payment-card.json +16 -0
  63. package/registry/_docs/pixel-bar.json +3 -0
  64. package/registry/_docs/polar-area.json +3 -0
  65. package/registry/_docs/profile.json +3 -0
  66. package/registry/_docs/progress-steps.json +24 -0
  67. package/registry/_docs/progress.json +20 -0
  68. package/registry/_docs/qr-code-button.json +122 -0
  69. package/registry/_docs/radio-group.json +20 -0
  70. package/registry/_docs/rating.json +24 -0
  71. package/registry/_docs/ring-carousel.json +20 -0
  72. package/registry/_docs/scratch-card.json +20 -0
  73. package/registry/_docs/search-bar.json +23 -3
  74. package/registry/_docs/search-results.json +3 -0
  75. package/registry/_docs/segmented-control.json +20 -0
  76. package/registry/_docs/select.json +33 -8
  77. package/registry/_docs/settings.json +9 -0
  78. package/registry/_docs/sign-in.json +3 -0
  79. package/registry/_docs/skeleton.json +22 -2
  80. package/registry/_docs/slide-to-confirm.json +24 -0
  81. package/registry/_docs/slider.json +27 -7
  82. package/registry/_docs/spin-wheel.json +24 -0
  83. package/registry/_docs/spinner.json +41 -11
  84. package/registry/_docs/stateful-button.json +184 -0
  85. package/registry/_docs/stepper.json +23 -7
  86. package/registry/_docs/swipe-deck.json +16 -0
  87. package/registry/_docs/swipeable-row.json +24 -1
  88. package/registry/_docs/switch.json +20 -0
  89. package/registry/_docs/tabs.json +24 -0
  90. package/registry/_docs/text-animation.json +141 -0
  91. package/registry/_docs/textarea.json +18 -2
  92. package/registry/_docs/ticket-card.json +20 -0
  93. package/registry/_docs/ticket-shape.json +4 -1
  94. package/registry/_docs/tilt-card.json +20 -4
  95. package/registry/_docs/time-picker.json +22 -6
  96. package/registry/_docs/timeline-chart.json +3 -0
  97. package/registry/_docs/timeline.json +18 -2
  98. package/registry/_docs/toast.json +32 -8
  99. package/registry/_docs/treemap.json +3 -0
  100. package/registry/_docs/waterfall.json +3 -0
  101. package/registry/_docs/wheel-list.json +20 -0
  102. package/registry/accordion/files/src/components/ui/accordion.tsx +5 -4
  103. package/registry/accordion/files/src/lib/kit.tsx +8 -5
  104. package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +9 -8
  105. package/registry/action-sheet/files/src/lib/kit.tsx +8 -5
  106. package/registry/alert/files/src/components/ui/alert.tsx +7 -4
  107. package/registry/alert/files/src/components/ui/button.tsx +4 -3
  108. package/registry/alert/files/src/lib/kit.tsx +8 -5
  109. package/registry/assistant-app.json +49 -0
  110. package/registry/avatar/files/src/components/ui/avatar.tsx +7 -4
  111. package/registry/avatar/files/src/lib/kit.tsx +8 -5
  112. package/registry/badge/files/src/components/ui/badge.tsx +345 -21
  113. package/registry/badge/files/src/lib/kit.tsx +8 -5
  114. package/registry/badge.json +1 -1
  115. package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +14 -7
  116. package/registry/bottom-nav/files/src/lib/kit.tsx +8 -5
  117. package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +7 -6
  118. package/registry/bottom-sheet/files/src/lib/kit.tsx +8 -5
  119. package/registry/button/files/src/components/ui/button.tsx +4 -3
  120. package/registry/button/files/src/lib/kit.tsx +8 -5
  121. package/registry/button-3d/files/src/components/ui/button-3d.tsx +3 -2
  122. package/registry/button-3d/files/src/lib/kit.tsx +8 -5
  123. package/registry/calendar/files/src/components/ui/calendar.tsx +9 -5
  124. package/registry/calendar/files/src/lib/kit.tsx +8 -5
  125. package/registry/card/files/src/components/ui/card.tsx +8 -6
  126. package/registry/card/files/src/lib/kit.tsx +8 -5
  127. package/registry/chat.json +18 -0
  128. package/registry/checkbox/files/src/components/ui/checkbox.tsx +6 -5
  129. package/registry/checkbox/files/src/lib/kit.tsx +8 -5
  130. package/registry/chip-group/files/src/components/ui/chip-group.tsx +4 -2
  131. package/registry/chip-group/files/src/lib/kit.tsx +8 -5
  132. package/registry/confetti/files/src/lib/kit.tsx +8 -5
  133. package/registry/coupon/files/src/components/ui/coupon.tsx +23 -20
  134. package/registry/coupon/files/src/components/ui/flip-card.tsx +1 -1
  135. package/registry/coupon/files/src/components/ui/tilt-card.tsx +2 -2
  136. package/registry/coupon/files/src/lib/kit.tsx +8 -5
  137. package/registry/date-picker/files/src/components/ui/calendar.tsx +9 -5
  138. package/registry/date-picker/files/src/components/ui/date-picker.tsx +2 -2
  139. package/registry/date-picker/files/src/lib/kit.tsx +8 -5
  140. package/registry/dialog/files/src/components/ui/dialog.tsx +12 -6
  141. package/registry/dialog/files/src/lib/kit.tsx +8 -5
  142. package/registry/donut-shop.json +18 -0
  143. package/registry/drawer/files/src/components/ui/drawer.tsx +25 -19
  144. package/registry/drawer/files/src/lib/kit.tsx +8 -5
  145. package/registry/fintech-app.json +57 -0
  146. package/registry/flip-card/files/src/components/ui/flip-card.tsx +1 -1
  147. package/registry/flip-card/files/src/lib/kit.tsx +8 -5
  148. package/registry/floating-input/files/src/components/ui/floating-input.tsx +16 -6
  149. package/registry/floating-input/files/src/lib/kit.tsx +8 -5
  150. package/registry/food-app.json +55 -0
  151. package/registry/grain/files/src/components/ui/grain.tsx +113 -0
  152. package/registry/{tag-input → grain}/files/src/lib/kit.tsx +8 -5
  153. package/registry/grain.json +17 -0
  154. package/registry/kit/files/src/lib/kit.tsx +8 -5
  155. package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +1 -1
  156. package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +1 -1
  157. package/registry/list-item/files/src/components/ui/list-item.tsx +20 -16
  158. package/registry/list-item/files/src/lib/kit.tsx +8 -5
  159. package/registry/morphing-button-group.json +18 -0
  160. package/registry/orb-reveal.json +20 -0
  161. package/registry/otp-input/files/src/components/ui/otp-input.tsx +8 -6
  162. package/registry/otp-input/files/src/lib/kit.tsx +8 -5
  163. package/registry/password-input/files/src/components/ui/floating-input.tsx +16 -6
  164. package/registry/password-input/files/src/components/ui/password-input.tsx +2 -2
  165. package/registry/password-input/files/src/lib/kit.tsx +8 -5
  166. package/registry/payment-card/files/src/components/ui/flip-card.tsx +1 -1
  167. package/registry/payment-card/files/src/components/ui/payment-card.tsx +27 -14
  168. package/registry/payment-card/files/src/components/ui/tilt-card.tsx +2 -2
  169. package/registry/payment-card/files/src/lib/kit.tsx +8 -5
  170. package/registry/progress/files/src/components/ui/progress.tsx +8 -5
  171. package/registry/progress/files/src/lib/kit.tsx +8 -5
  172. package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +15 -11
  173. package/registry/progress-steps/files/src/lib/kit.tsx +8 -5
  174. package/registry/qr-code-button.json +18 -0
  175. package/registry/radio-group/files/src/components/ui/radio-group.tsx +10 -9
  176. package/registry/radio-group/files/src/lib/kit.tsx +8 -5
  177. package/registry/search-bar/files/src/components/ui/search-bar.tsx +13 -11
  178. package/registry/search-bar/files/src/lib/kit.tsx +8 -5
  179. package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +4 -2
  180. package/registry/segmented-control/files/src/lib/kit.tsx +8 -5
  181. package/registry/select/files/src/components/ui/select.tsx +3 -3
  182. package/registry/select/files/src/lib/kit.tsx +8 -5
  183. package/registry/skeleton/files/src/components/ui/skeleton.tsx +4 -4
  184. package/registry/skeleton/files/src/lib/kit.tsx +8 -5
  185. package/registry/slider/files/src/components/ui/slider.tsx +5 -4
  186. package/registry/slider/files/src/lib/kit.tsx +8 -5
  187. package/registry/spinner/files/src/components/ui/spinner.tsx +452 -34
  188. package/registry/spinner/files/src/lib/kit.tsx +8 -5
  189. package/registry/spinner.json +1 -1
  190. package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +532 -0
  191. package/registry/{pagination-dots → stateful-button}/files/src/lib/kit.tsx +8 -5
  192. package/registry/stateful-button.json +18 -0
  193. package/registry/stepper/files/src/components/ui/stepper.tsx +8 -4
  194. package/registry/stepper/files/src/lib/kit.tsx +8 -5
  195. package/registry/switch/files/src/components/ui/switch.tsx +4 -3
  196. package/registry/switch/files/src/lib/kit.tsx +8 -5
  197. package/registry/tabs/files/src/components/ui/tabs.tsx +10 -8
  198. package/registry/tabs/files/src/lib/kit.tsx +8 -5
  199. package/registry/text-animation/files/src/components/ui/text-animation.tsx +650 -0
  200. package/registry/text-animation/files/src/lib/kit.tsx +222 -0
  201. package/registry/text-animation.json +15 -0
  202. package/registry/textarea/files/src/components/ui/textarea.tsx +11 -9
  203. package/registry/textarea/files/src/lib/kit.tsx +8 -5
  204. package/registry/ticket-shape/files/src/lib/kit.tsx +8 -5
  205. package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +2 -2
  206. package/registry/tilt-card/files/src/lib/kit.tsx +8 -5
  207. package/registry/time-picker/files/src/components/ui/time-picker.tsx +2 -2
  208. package/registry/time-picker/files/src/lib/kit.tsx +8 -5
  209. package/registry/timeline/files/src/components/ui/avatar.tsx +7 -4
  210. package/registry/timeline/files/src/components/ui/timeline.tsx +12 -10
  211. package/registry/timeline/files/src/lib/kit.tsx +8 -5
  212. package/registry/toast/files/src/components/ui/button.tsx +4 -3
  213. package/registry/toast/files/src/components/ui/toast.tsx +13 -11
  214. package/registry/toast/files/src/lib/kit.tsx +8 -5
  215. package/schema/components.schema.json +1 -1
  216. package/registry/_docs/color-swatch.json +0 -66
  217. package/registry/_docs/kpi-card.json +0 -11
  218. package/registry/_docs/like-button.json +0 -72
  219. package/registry/_docs/morph-button.json +0 -108
  220. package/registry/_docs/pagination-dots.json +0 -88
  221. package/registry/_docs/shimmer-button.json +0 -78
  222. package/registry/_docs/tag-input.json +0 -128
  223. package/registry/color-swatch.json +0 -17
  224. package/registry/kpi-card/files/src/components/charts/kpi-card.tsx +0 -45
  225. package/registry/kpi-card.json +0 -14
  226. package/registry/like-button.json +0 -18
  227. package/registry/morph-button.json +0 -18
  228. package/registry/pagination-dots/files/src/components/ui/pagination-dots.tsx +0 -262
  229. package/registry/pagination-dots.json +0 -15
  230. package/registry/shimmer-button.json +0 -17
  231. package/registry/tag-input/files/src/components/ui/tag-input.tsx +0 -720
  232. package/registry/tag-input.json +0 -17
@@ -1,11 +1,17 @@
1
1
  {
2
2
  "name": "select",
3
3
  "category": "Inputs",
4
- "aliases": [],
4
+ "aliases": [
5
+ "multi-select",
6
+ "multiselect",
7
+ "combobox",
8
+ "dropdown"
9
+ ],
5
10
  "exportName": "Select",
6
11
  "files": [
7
12
  "src/components/ui/select.tsx"
8
13
  ],
14
+ "summary": "A dropdown or sheet picker with a floating label; multi-select shows removable chips.",
9
15
  "props": [
10
16
  [
11
17
  "options",
@@ -212,41 +218,60 @@
212
218
  "Each option's label text."
213
219
  ]
214
220
  ],
221
+ "behavior": [
222
+ [
223
+ "Touch",
224
+ "Tapping the field opens a dropdown anchored under it, flipping above when there's no room. presentation='sheet' slides up a bottom sheet you drag down to dismiss (a centred dialog on wide screens)."
225
+ ],
226
+ [
227
+ "Motion",
228
+ "The label floats onto the border, a focus halo glows and the chevron turns. In multiple mode chips pop in and collapse out, overflowing into a '+N' chip."
229
+ ],
230
+ [
231
+ "Keyboard",
232
+ "On web, arrow keys move the highlight, Home and End jump, Enter picks, and Escape or Tab close the menu."
233
+ ],
234
+ [
235
+ "Screen readers",
236
+ "The field is a combobox whose value lists the selection, with an expanded state. Options are menu items, or checkboxes in multiple mode."
237
+ ]
238
+ ],
239
+ "usage": "import { useState } from 'react';\nimport { Select } from '@/components/ui/select';\n\nconst [team, setTeam] = useState<string[]>([]);\n<Select multiple label=\"Assignees\" options={['Amelia', 'Ravi', 'Sofia', 'Kenji']} values={team} onValuesChange={setTeam} />",
215
240
  "examples": [
216
241
  {
217
242
  "name": "Validation",
218
243
  "title": "Validation",
219
- "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Validation() {\n const { colors, radius } = useKit();\n const [size, setSize] = useState<string | null>(null);\n const [touched, setTouched] = useState(false);\n const missing = touched && !size;\n return (\n <Card>\n <Heading title=\"Merino crew sweater\" sub=\"₹3,499 · Oatmeal\" />\n <Select\n label=\"Size\"\n floatingLabel={false}\n placeholder=\"Choose a size\"\n size=\"sm\"\n value={size}\n surface={colors.card}\n onValueChange={(v) => {\n setSize(v);\n setTouched(true);\n }}\n options={[\n { label: 'XS', value: 'XS', disabled: true, description: 'Sold out' },\n { label: 'S', value: 'S', description: 'Chest 34–36\"' },\n { label: 'M', value: 'M', description: 'Chest 38–40\"' },\n { label: 'L', value: 'L', description: 'Chest 42–44\"' },\n { label: 'XL', value: 'XL', description: 'Chest 46–48\"' },\n ]}\n error={missing ? 'Pick a size to add this to your bag' : undefined}\n helper={missing ? undefined : 'Runs true to size · free exchanges'}\n />\n <Pressable\n onPress={() => setTouched(true)}\n accessibilityRole=\"button\"\n style={({ pressed }) => [s.button, { backgroundColor: colors.primary, borderRadius: radius.md, opacity: pressed ? 0.88 : 1 }]}\n >\n <Text style={[s.buttonText, { color: colors.primaryForeground }]}>Add to bag</Text>\n </Pressable>\n </Card>\n );\n}\n"
244
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Validation({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n const [size, setSize] = useState<string | null>(null);\n const [touched, setTouched] = useState(false);\n const missing = touched && !size;\n return (\n <Card screen={screen}>\n <Heading title=\"Merino crew sweater\" sub=\"₹3,499 · Oatmeal\" />\n <Select\n label=\"Size\"\n floatingLabel={false}\n placeholder=\"Choose a size\"\n size=\"sm\"\n value={size}\n surface={colors.card}\n onValueChange={(v) => {\n setSize(v);\n setTouched(true);\n }}\n options={[\n { label: 'XS', value: 'XS', disabled: true, description: 'Sold out' },\n { label: 'S', value: 'S', description: 'Chest 34–36\"' },\n { label: 'M', value: 'M', description: 'Chest 38–40\"' },\n { label: 'L', value: 'L', description: 'Chest 42–44\"' },\n { label: 'XL', value: 'XL', description: 'Chest 46–48\"' },\n ]}\n error={missing ? 'Pick a size to add this to your bag' : undefined}\n helper={missing ? undefined : 'Runs true to size · free exchanges'}\n />\n <Pressable\n onPress={() => setTouched(true)}\n accessibilityRole=\"button\"\n style={({ pressed }) => [s.button, { backgroundColor: colors.primary, borderRadius: radius.md, opacity: pressed ? 0.88 : 1 }]}\n >\n <Text style={[s.buttonText, { color: colors.primaryForeground }]}>Add to bag</Text>\n </Pressable>\n </Card>\n );\n}\n"
220
245
  },
221
246
  {
222
247
  "name": "Plan",
223
248
  "title": "Plan",
224
- "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport Svg, { Circle, Path } from 'react-native-svg';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\n/** Small icon tile for option rows. */\nfunction Glyph({ d, tone }: { d: string; tone?: 'accent' | 'success' | 'warning' }) {\n const { colors, radius } = useKit();\n const c = tone ? colors[tone] : colors.mutedForeground;\n return (\n <View style={[s.glyph, { borderRadius: radius.scale(8), backgroundColor: tone ? alpha(c, 0.14) : colors.muted }]}>\n <Svg width={15} height={15} viewBox=\"0 0 24 24\">\n <Path d={d} stroke={c} strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" fill=\"none\" />\n </Svg>\n </View>\n );\n}\n\nconst ICON = {\n leaf: 'M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.5 19 2c1 2 2 4.2 2 8 0 5.5-4.8 10-10 10ZM2 21c0-3 1.9-5.4 5.2-6',\n bolt: 'M13 2 3 14h9l-1 8 10-12h-9l1-8Z',\n users: 'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8ZM22 21v-2a4 4 0 0 0-3-3.9M16 3.1a4 4 0 0 1 0 7.8',\n building: 'M6 22V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18ZM6 12H4a2 2 0 0 0-2 2v8h4M18 9h2a2 2 0 0 1 2 2v11h-4M10 6h4M10 10h4M10 14h4M10 18h4',\n};\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Plan() {\n const { colors } = useKit();\n return (\n <Card>\n <Heading title=\"Workspace plan\" sub=\"Billed monthly · change anytime\" />\n <Select\n label=\"Plan\"\n floatingLabel={false}\n placeholder=\"Select a plan\"\n size=\"sm\"\n defaultValue=\"pro\"\n surface={colors.card}\n helper=\"You can switch plans later from billing.\"\n options={[\n { label: 'Hobby', value: 'hobby', description: 'Free · 1 project', icon: <Glyph d={ICON.leaf} tone=\"success\" /> },\n { label: 'Pro', value: 'pro', description: '₹499 / seat · unlimited projects', icon: <Glyph d={ICON.bolt} tone=\"accent\" /> },\n { label: 'Team', value: 'team', description: '₹1,499 / seat · SSO and audit log', icon: <Glyph d={ICON.users} tone=\"warning\" /> },\n { label: 'Enterprise', value: 'enterprise', description: 'Talk to sales', icon: <Glyph d={ICON.building} />, disabled: true },\n ]}\n />\n </Card>\n );\n}\n"
249
+ "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport Svg, { Circle, Path } from 'react-native-svg';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\n/** Small icon tile for option rows. */\nfunction Glyph({ d, tone }: { d: string; tone?: 'accent' | 'success' | 'warning' }) {\n const { colors, radius } = useKit();\n const c = tone ? colors[tone] : colors.mutedForeground;\n return (\n <View style={[s.glyph, { borderRadius: radius.scale(8), backgroundColor: tone ? alpha(c, 0.14) : colors.muted }]}>\n <Svg width={15} height={15} viewBox=\"0 0 24 24\">\n <Path d={d} stroke={c} strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" fill=\"none\" />\n </Svg>\n </View>\n );\n}\n\nconst ICON = {\n leaf: 'M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.5 19 2c1 2 2 4.2 2 8 0 5.5-4.8 10-10 10ZM2 21c0-3 1.9-5.4 5.2-6',\n bolt: 'M13 2 3 14h9l-1 8 10-12h-9l1-8Z',\n users: 'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8ZM22 21v-2a4 4 0 0 0-3-3.9M16 3.1a4 4 0 0 1 0 7.8',\n building: 'M6 22V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18ZM6 12H4a2 2 0 0 0-2 2v8h4M18 9h2a2 2 0 0 1 2 2v11h-4M10 6h4M10 10h4M10 14h4M10 18h4',\n};\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Plan({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Workspace plan\" sub=\"Billed monthly · change anytime\" />\n <Select\n label=\"Plan\"\n floatingLabel={false}\n placeholder=\"Select a plan\"\n size=\"sm\"\n defaultValue=\"pro\"\n surface={colors.card}\n helper=\"You can switch plans later from billing.\"\n options={[\n { label: 'Hobby', value: 'hobby', description: 'Free · 1 project', icon: <Glyph d={ICON.leaf} tone=\"success\" /> },\n { label: 'Pro', value: 'pro', description: '₹499 / seat · unlimited projects', icon: <Glyph d={ICON.bolt} tone=\"accent\" /> },\n { label: 'Team', value: 'team', description: '₹1,499 / seat · SSO and audit log', icon: <Glyph d={ICON.users} tone=\"warning\" /> },\n { label: 'Enterprise', value: 'enterprise', description: 'Talk to sales', icon: <Glyph d={ICON.building} />, disabled: true },\n ]}\n />\n </Card>\n );\n}\n"
225
250
  },
226
251
  {
227
252
  "name": "Country",
228
253
  "title": "Country",
229
- "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Country() {\n const { colors } = useKit();\n return (\n <Card>\n <Heading title=\"Shipping address\" sub=\"Where should we send this order?\" />\n <Select\n searchable\n label=\"Country\"\n floatingLabel={false}\n placeholder=\"Select country\"\n searchPlaceholder=\"Search countries\"\n size=\"sm\"\n defaultValue=\"DE\"\n surface={colors.card}\n options={[\n { label: 'United States', value: 'US', icon: flag('🇺🇸'), group: 'Americas' },\n { label: 'Canada', value: 'CA', icon: flag('🇨🇦'), group: 'Americas' },\n { label: 'Brazil', value: 'BR', icon: flag('🇧🇷'), group: 'Americas' },\n { label: 'United Kingdom', value: 'GB', icon: flag('🇬🇧'), group: 'Europe' },\n { label: 'Germany', value: 'DE', icon: flag('🇩🇪'), group: 'Europe' },\n { label: 'France', value: 'FR', icon: flag('🇫🇷'), group: 'Europe' },\n { label: 'India', value: 'IN', icon: flag('🇮🇳'), group: 'Asia Pacific' },\n { label: 'Japan', value: 'JP', icon: flag('🇯🇵'), group: 'Asia Pacific' },\n { label: 'Australia', value: 'AU', icon: flag('🇦🇺'), group: 'Asia Pacific' },\n ]}\n />\n </Card>\n );\n}\n"
254
+ "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Country({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Shipping address\" sub=\"Where should we send this order?\" />\n <Select\n searchable\n label=\"Country\"\n floatingLabel={false}\n placeholder=\"Select country\"\n searchPlaceholder=\"Search countries\"\n size=\"sm\"\n defaultValue=\"DE\"\n surface={colors.card}\n options={[\n { label: 'United States', value: 'US', icon: flag('🇺🇸'), group: 'Americas' },\n { label: 'Canada', value: 'CA', icon: flag('🇨🇦'), group: 'Americas' },\n { label: 'Brazil', value: 'BR', icon: flag('🇧🇷'), group: 'Americas' },\n { label: 'United Kingdom', value: 'GB', icon: flag('🇬🇧'), group: 'Europe' },\n { label: 'Germany', value: 'DE', icon: flag('🇩🇪'), group: 'Europe' },\n { label: 'France', value: 'FR', icon: flag('🇫🇷'), group: 'Europe' },\n { label: 'India', value: 'IN', icon: flag('🇮🇳'), group: 'Asia Pacific' },\n { label: 'Japan', value: 'JP', icon: flag('🇯🇵'), group: 'Asia Pacific' },\n { label: 'Australia', value: 'AU', icon: flag('🇦🇺'), group: 'Asia Pacific' },\n ]}\n />\n </Card>\n );\n}\n"
230
255
  },
231
256
  {
232
257
  "name": "Assignees",
233
258
  "title": "Assignees",
234
- "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Assignees() {\n const { colors } = useKit();\n const [people, setPeople] = useState(['maya', 'leo']);\n return (\n <Card>\n <Heading title=\"Fix checkout crash on Android 14\" sub=\"ENG-2142 · In progress\" />\n <Select\n multiple\n searchable\n label=\"Assignees\"\n floatingLabel={false}\n placeholder=\"Add teammates\"\n searchPlaceholder=\"Search teammates\"\n size=\"sm\"\n values={people}\n onValuesChange={setPeople}\n surface={colors.card}\n options={[\n { label: 'Maya Chen', value: 'maya', description: 'Design', icon: avatar('MC', '#f472b6') },\n { label: 'Leo Martins', value: 'leo', description: 'iOS', icon: avatar('LM', '#60a5fa') },\n { label: 'Ana Ruiz', value: 'ana', description: 'Android', icon: avatar('AR', '#34d399') },\n { label: 'Sam Okafor', value: 'sam', description: 'Backend', icon: avatar('SO', '#fbbf24') },\n { label: 'Priya Nair', value: 'priya', description: 'QA', icon: avatar('PN', '#a78bfa') },\n ]}\n />\n </Card>\n );\n}\n"
259
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Assignees({ screen }: DemoProps) {\n const { colors } = useKit();\n const [people, setPeople] = useState(['maya', 'leo']);\n return (\n <Card screen={screen}>\n <Heading title=\"Fix checkout crash on Android 14\" sub=\"ENG-2142 · In progress\" />\n <Select\n multiple\n searchable\n label=\"Assignees\"\n floatingLabel={false}\n placeholder=\"Add teammates\"\n searchPlaceholder=\"Search teammates\"\n size=\"sm\"\n values={people}\n onValuesChange={setPeople}\n surface={colors.card}\n options={[\n { label: 'Maya Chen', value: 'maya', description: 'Design', icon: avatar('MC', '#f472b6') },\n { label: 'Leo Martins', value: 'leo', description: 'iOS', icon: avatar('LM', '#60a5fa') },\n { label: 'Ana Ruiz', value: 'ana', description: 'Android', icon: avatar('AR', '#34d399') },\n { label: 'Sam Okafor', value: 'sam', description: 'Backend', icon: avatar('SO', '#fbbf24') },\n { label: 'Priya Nair', value: 'priya', description: 'QA', icon: avatar('PN', '#a78bfa') },\n ]}\n />\n </Card>\n );\n}\n"
235
260
  },
236
261
  {
237
262
  "name": "Sheet",
238
263
  "title": "Sheet",
239
- "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Sheet() {\n const { colors } = useKit();\n return (\n <Card>\n <Heading title=\"Notifications\" sub=\"Opens as a sheet instead of a dropdown\" />\n <Select\n presentation=\"sheet\"\n label=\"Email digest\"\n floatingLabel={false}\n placeholder=\"How often?\"\n title=\"How often?\"\n size=\"sm\"\n defaultValue=\"weekly\"\n surface={colors.card}\n options={[\n { label: 'Daily', value: 'daily', description: 'Every morning at 8:00' },\n { label: 'Weekly', value: 'weekly', description: 'Mondays at 8:00' },\n { label: 'Monthly', value: 'monthly', description: 'First of the month' },\n { label: 'Never', value: 'never', description: 'Only security alerts' },\n ]}\n />\n </Card>\n );\n}\n"
264
+ "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Sheet({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Notifications\" sub=\"Opens as a sheet instead of a dropdown\" />\n <Select\n presentation=\"sheet\"\n label=\"Email digest\"\n floatingLabel={false}\n placeholder=\"How often?\"\n title=\"How often?\"\n size=\"sm\"\n defaultValue=\"weekly\"\n surface={colors.card}\n options={[\n { label: 'Daily', value: 'daily', description: 'Every morning at 8:00' },\n { label: 'Weekly', value: 'weekly', description: 'Mondays at 8:00' },\n { label: 'Monthly', value: 'monthly', description: 'First of the month' },\n { label: 'Never', value: 'never', description: 'Only security alerts' },\n ]}\n />\n </Card>\n );\n}\n"
240
265
  },
241
266
  {
242
267
  "name": "Sizes",
243
268
  "title": "Sizes",
244
- "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Sizes() {\n const { colors } = useKit();\n const opts = ['Daily', 'Weekly', 'Monthly'];\n return (\n <Card style={{ gap: 16 }}>\n <Select size=\"sm\" label=\"Small\" floatingLabel={false} placeholder=\"Pick one\" defaultValue=\"Weekly\" options={opts} surface={colors.card} />\n <Select size=\"md\" label=\"Medium\" floatingLabel={false} placeholder=\"Pick one\" defaultValue=\"Weekly\" options={opts} surface={colors.card} />\n <Select size=\"lg\" label=\"Large\" floatingLabel={false} placeholder=\"Pick one\" defaultValue=\"Weekly\" options={opts} surface={colors.card} />\n <Select disabled label=\"Disabled\" floatingLabel={false} placeholder=\"Unavailable\" defaultValue=\"Daily\" options={opts} surface={colors.card} />\n </Card>\n );\n}\n"
269
+ "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Sizes({ screen }: DemoProps) {\n const { colors } = useKit();\n const opts = ['Daily', 'Weekly', 'Monthly'];\n return (\n <Card screen={screen} style={{ gap: 16 }}>\n <Select size=\"sm\" label=\"Small\" floatingLabel={false} placeholder=\"Pick one\" defaultValue=\"Weekly\" options={opts} surface={colors.card} />\n <Select size=\"md\" label=\"Medium\" floatingLabel={false} placeholder=\"Pick one\" defaultValue=\"Weekly\" options={opts} surface={colors.card} />\n <Select size=\"lg\" label=\"Large\" floatingLabel={false} placeholder=\"Pick one\" defaultValue=\"Weekly\" options={opts} surface={colors.card} />\n <Select disabled label=\"Disabled\" floatingLabel={false} placeholder=\"Unavailable\" defaultValue=\"Daily\" options={opts} surface={colors.card} />\n </Card>\n );\n}\n"
245
270
  },
246
271
  {
247
272
  "name": "Colors",
248
273
  "title": "Colors",
249
- "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport Svg, { Circle, Path } from 'react-native-svg';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nfunction Dot({ color }: { color: string }) {\n return (\n <Svg width={14} height={14} viewBox=\"0 0 14 14\">\n <Circle cx={7} cy={7} r={5} fill={color} />\n </Svg>\n );\n}\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Colors() {\n const { colors } = useKit();\n return (\n <Card>\n <Heading title=\"Project labels\" sub=\"Accent colour drives focus, checks and chips\" />\n <Select\n multiple\n label=\"Labels\"\n floatingLabel={false}\n placeholder=\"Add labels\"\n size=\"sm\"\n accent={colors.success}\n defaultValues={['frontend', 'perf']}\n surface={colors.card}\n options={[\n { label: 'Frontend', value: 'frontend' },\n { label: 'Performance', value: 'perf' },\n { label: 'Accessibility', value: 'a11y' },\n { label: 'Docs', value: 'docs' },\n ]}\n />\n <Select\n label=\"Priority\"\n floatingLabel={false}\n placeholder=\"Set priority\"\n size=\"sm\"\n accent={colors.warning}\n defaultValue=\"p1\"\n surface={colors.card}\n options={[\n { label: 'Urgent', value: 'p0', icon: <Dot color={colors.danger} /> },\n { label: 'High', value: 'p1', icon: <Dot color={colors.warning} /> },\n { label: 'Medium', value: 'p2', icon: <Dot color={colors.accent} /> },\n { label: 'Low', value: 'p3', icon: <Dot color={colors.mutedForeground} /> },\n ]}\n />\n </Card>\n );\n}\n"
274
+ "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport Svg, { Circle, Path } from 'react-native-svg';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nfunction Dot({ color }: { color: string }) {\n return (\n <Svg width={14} height={14} viewBox=\"0 0 14 14\">\n <Circle cx={7} cy={7} r={5} fill={color} />\n </Svg>\n );\n}\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Colors({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Project labels\" sub=\"Accent colour drives focus, checks and chips\" />\n <Select\n multiple\n label=\"Labels\"\n floatingLabel={false}\n placeholder=\"Add labels\"\n size=\"sm\"\n accent={colors.success}\n defaultValues={['frontend', 'perf']}\n surface={colors.card}\n options={[\n { label: 'Frontend', value: 'frontend' },\n { label: 'Performance', value: 'perf' },\n { label: 'Accessibility', value: 'a11y' },\n { label: 'Docs', value: 'docs' },\n ]}\n />\n <Select\n label=\"Priority\"\n floatingLabel={false}\n placeholder=\"Set priority\"\n size=\"sm\"\n accent={colors.warning}\n defaultValue=\"p1\"\n surface={colors.card}\n options={[\n { label: 'Urgent', value: 'p0', icon: <Dot color={colors.danger} /> },\n { label: 'High', value: 'p1', icon: <Dot color={colors.warning} /> },\n { label: 'Medium', value: 'p2', icon: <Dot color={colors.accent} /> },\n { label: 'Low', value: 'p3', icon: <Dot color={colors.mutedForeground} /> },\n ]}\n />\n </Card>\n );\n}\n"
250
275
  }
251
276
  ]
252
277
  }
@@ -6,6 +6,7 @@
6
6
  "files": [
7
7
  "src/components/blocks/settings.tsx"
8
8
  ],
9
+ "summary": null,
9
10
  "props": [
10
11
  [
11
12
  "user",
@@ -43,6 +44,12 @@
43
44
  "—",
44
45
  "Pressed Sign out."
45
46
  ],
47
+ [
48
+ "onBack",
49
+ "() => void",
50
+ "—",
51
+ "Shows a back button above the title, e.g. when Settings is pushed from a profile tab."
52
+ ],
46
53
  [
47
54
  "storageUsed",
48
55
  "string",
@@ -68,5 +75,7 @@
68
75
  "Root style."
69
76
  ]
70
77
  ],
78
+ "behavior": [],
79
+ "usage": null,
71
80
  "examples": []
72
81
  }
@@ -6,6 +6,7 @@
6
6
  "files": [
7
7
  "src/components/blocks/sign-in.tsx"
8
8
  ],
9
+ "summary": null,
9
10
  "props": [
10
11
  [
11
12
  "methods",
@@ -194,5 +195,7 @@
194
195
  "Aliases of onEmailPassword / onOAuth; providers (apple / google) sets the OAuth part of methods when methods isn't given."
195
196
  ]
196
197
  ],
198
+ "behavior": [],
199
+ "usage": null,
197
200
  "examples": []
198
201
  }
@@ -2,10 +2,11 @@
2
2
  "name": "skeleton",
3
3
  "category": "Feedback",
4
4
  "aliases": [],
5
- "exportName": "SkeletonGroup",
5
+ "exportName": "Skeleton",
6
6
  "files": [
7
7
  "src/components/ui/skeleton.tsx"
8
8
  ],
9
+ "summary": "Loading placeholders swept by one angled light band across the whole screen.",
9
10
  "props": [
10
11
  [
11
12
  "width / height",
@@ -92,6 +93,25 @@
92
93
  "Style for the block (composites apply it to their wrapper)."
93
94
  ]
94
95
  ],
96
+ "behavior": [
97
+ [
98
+ "Motion",
99
+ "Every block reads one shared native-driver clock, so the shimmer band, wave or pulse moves across a Skeleton.Group (or the window) as a single sweep."
100
+ ],
101
+ [
102
+ "Reduced motion",
103
+ "Every variant renders static, and Skeleton.Reveal swaps the skeleton for content instantly."
104
+ ],
105
+ [
106
+ "Screen readers",
107
+ "Placeholder blocks are hidden from screen readers. Skeleton.Reveal is marked busy and exposes a progressbar named by loadingLabel; the content arrives in a polite live region."
108
+ ],
109
+ [
110
+ "Platforms",
111
+ "Plain Animated views plus a react-native-svg gradient, so it runs the same on iOS, Android and web."
112
+ ]
113
+ ],
114
+ "usage": "import { Skeleton } from '@/components/ui/skeleton';\n\n<Skeleton.Group style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <Skeleton.Avatar size={44} />\n <Skeleton.Text lines={2} style={{ flex: 1 }} />\n</Skeleton.Group>",
95
115
  "examples": [
96
116
  {
97
117
  "name": "Shimmer",
@@ -136,7 +156,7 @@
136
156
  {
137
157
  "name": "Nav",
138
158
  "title": "Nav",
139
- "code": "import { Skeleton } from '@/components/ui/skeleton';\n\nexport default function Nav() {\n return <Skeleton.Nav rows={6} tabCount={5} style={{ minHeight: 600 }} />;\n}\n"
159
+ "code": "import { Skeleton } from '@/components/ui/skeleton';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nexport default function Nav({ screen }: DemoProps) {\n return <Skeleton.Nav rows={6} tabCount={5} style={screen ? undefined : { minHeight: 600 }} />;\n}\n"
140
160
  }
141
161
  ]
142
162
  }
@@ -6,6 +6,7 @@
6
6
  "files": [
7
7
  "src/components/ui/slide-to-confirm.tsx"
8
8
  ],
9
+ "summary": "A slide-to-pay track whose thumb drags a glowing trail and ticks with haptics.",
9
10
  "props": [
10
11
  [
11
12
  "label",
@@ -80,5 +81,28 @@
80
81
  "Style for the track (merged over its height, radius and colour)."
81
82
  ]
82
83
  ],
84
+ "behavior": [
85
+ [
86
+ "Touch",
87
+ "Drag past the threshold, or flick fast past the halfway point, to confirm; anything less springs back. If onConfirm returns a promise the thumb spins until it settles."
88
+ ],
89
+ [
90
+ "Motion",
91
+ "The thumb swells while held and a check draws on success; a rejected promise shakes the track and slides it back."
92
+ ],
93
+ [
94
+ "Reduced motion",
95
+ "The idle nudge and the chevron shimmer on the thumb are turned off."
96
+ ],
97
+ [
98
+ "Haptics",
99
+ "Selection ticks at 25, 50 and 75 percent of the drag, a medium impact on release, then a success or error notification."
100
+ ],
101
+ [
102
+ "Screen readers",
103
+ "A button whose label switches to confirmedLabel when done, with busy and disabled state. The 'activate' action (double tap) confirms without dragging."
104
+ ]
105
+ ],
106
+ "usage": null,
83
107
  "examples": []
84
108
  }
@@ -1,11 +1,16 @@
1
1
  {
2
2
  "name": "slider",
3
3
  "category": "Inputs",
4
- "aliases": [],
4
+ "aliases": [
5
+ "range-slider",
6
+ "range",
7
+ "dual-slider"
8
+ ],
5
9
  "exportName": "Slider",
6
10
  "files": [
7
11
  "src/components/ui/slider.tsx"
8
12
  ],
13
+ "summary": "A native-driver slider with a swelling thumb, tap-to-seek and a two-thumb range mode.",
9
14
  "props": [
10
15
  [
11
16
  "value / defaultValue",
@@ -122,36 +127,51 @@
122
127
  "Root, inactive track, active fill, thumb and value text."
123
128
  ]
124
129
  ],
130
+ "behavior": [
131
+ [
132
+ "Touch",
133
+ "Tap anywhere on the track to seek, or grab a thumb and drag. Built-in leftIcon and rightIcon names step the value down or up when tapped."
134
+ ],
135
+ [
136
+ "Motion",
137
+ "Thumb and fill move on the native driver; the thumb swells under your finger and values spring-snap to the step."
138
+ ],
139
+ [
140
+ "Screen readers",
141
+ "Single mode is one adjustable element with min, max, current value and increment / decrement actions. In range mode each thumb is its own adjustable, labelled 'minimum' and 'maximum'."
142
+ ]
143
+ ],
144
+ "usage": "import { useState } from 'react';\nimport { Slider } from '@/components/ui/slider';\n\nconst [price, setPrice] = useState<[number, number]>([20, 80]);\n<Slider range value={price} onValueChange={setPrice} accessibilityLabel=\"Price\" />",
125
145
  "examples": [
126
146
  {
127
147
  "name": "ControlCenter",
128
148
  "title": "Control Center",
129
- "code": "import React, { useState } from 'react';\nimport { StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Slider } from '@/components/ui/slider';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]}\n {...rest}\n />\n );\n}\n\nfunction Header({ title, value }: { title: string; value?: string }) {\n const { colors } = useKit();\n return (\n <View style={s.head}>\n <Text style={[s.title, { color: colors.foreground }]}>{title}</Text>\n {value ? <Text style={[s.value, { color: colors.mutedForeground }]}>{value}</Text> : null}\n </View>\n );\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n head: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { fontSize: 15, fontWeight: '400' },\n value: { fontSize: 15, fontWeight: '400', fontVariant: ['tabular-nums'] },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -8 },\n note: { fontSize: 13 },\n});\n\nexport default function ControlCenter() {\n const [brightness, setBrightness] = useState(72);\n return (\n <Card>\n <Header title=\"Display & sound\" />\n <Slider\n variant=\"bar\"\n size=\"lg\"\n step={0}\n value={brightness}\n onValueChange={setBrightness}\n icon=\"sun-high\"\n showValue=\"always\"\n formatValue={(v) => `${Math.round(v)}%`}\n accessibilityLabel=\"Brightness\"\n />\n <Slider variant=\"bar\" size=\"lg\" defaultValue={40} icon=\"volume-high\" accessibilityLabel=\"Volume\" />\n </Card>\n );\n}\n"
149
+ "code": "import React, { useState } from 'react';\nimport { StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Slider } from '@/components/ui/slider';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n s.card,\n screen\n ? { flex: 1, backgroundColor: colors.background, borderWidth: 0 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nfunction Header({ title, value }: { title: string; value?: string }) {\n const { colors } = useKit();\n return (\n <View style={s.head}>\n <Text style={[s.title, { color: colors.foreground }]}>{title}</Text>\n {value ? <Text style={[s.value, { color: colors.mutedForeground }]}>{value}</Text> : null}\n </View>\n );\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n head: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { fontSize: 15, fontWeight: '400' },\n value: { fontSize: 15, fontWeight: '400', fontVariant: ['tabular-nums'] },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -8 },\n note: { fontSize: 13 },\n});\n\nexport default function ControlCenter({ screen }: DemoProps) {\n const [brightness, setBrightness] = useState(72);\n return (\n <Card screen={screen}>\n <Header title=\"Display & sound\" />\n <Slider\n variant=\"bar\"\n size=\"lg\"\n step={0}\n value={brightness}\n onValueChange={setBrightness}\n icon=\"sun-high\"\n showValue=\"always\"\n formatValue={(v) => `${Math.round(v)}%`}\n accessibilityLabel=\"Brightness\"\n />\n <Slider variant=\"bar\" size=\"lg\" defaultValue={40} icon=\"volume-high\" accessibilityLabel=\"Volume\" />\n </Card>\n );\n}\n"
130
150
  },
131
151
  {
132
152
  "name": "Default",
133
153
  "title": "Default",
134
- "code": "import React, { useState } from 'react';\nimport { StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Slider } from '@/components/ui/slider';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]}\n {...rest}\n />\n );\n}\n\nfunction Header({ title, value }: { title: string; value?: string }) {\n const { colors } = useKit();\n return (\n <View style={s.head}>\n <Text style={[s.title, { color: colors.foreground }]}>{title}</Text>\n {value ? <Text style={[s.value, { color: colors.mutedForeground }]}>{value}</Text> : null}\n </View>\n );\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n head: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { fontSize: 15, fontWeight: '400' },\n value: { fontSize: 15, fontWeight: '400', fontVariant: ['tabular-nums'] },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -8 },\n note: { fontSize: 13 },\n});\n\nexport default function Default() {\n const [volume, setVolume] = useState(64);\n return (\n <Card>\n <Header title=\"Media volume\" value={`${volume}%`} />\n <Slider\n value={volume}\n onValueChange={setVolume}\n leftIcon=\"volume-low\"\n rightIcon=\"volume-high\"\n formatValue={(v) => `${v}%`}\n accessibilityLabel=\"Media volume\"\n />\n </Card>\n );\n}\n"
154
+ "code": "import React, { useState } from 'react';\nimport { StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Slider } from '@/components/ui/slider';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n s.card,\n screen\n ? { flex: 1, backgroundColor: colors.background, borderWidth: 0 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nfunction Header({ title, value }: { title: string; value?: string }) {\n const { colors } = useKit();\n return (\n <View style={s.head}>\n <Text style={[s.title, { color: colors.foreground }]}>{title}</Text>\n {value ? <Text style={[s.value, { color: colors.mutedForeground }]}>{value}</Text> : null}\n </View>\n );\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n head: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { fontSize: 15, fontWeight: '400' },\n value: { fontSize: 15, fontWeight: '400', fontVariant: ['tabular-nums'] },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -8 },\n note: { fontSize: 13 },\n});\n\nexport default function Default({ screen }: DemoProps) {\n const [volume, setVolume] = useState(64);\n return (\n <Card screen={screen}>\n <Header title=\"Media volume\" value={`${volume}%`} />\n <Slider\n value={volume}\n onValueChange={setVolume}\n leftIcon=\"volume-low\"\n rightIcon=\"volume-high\"\n formatValue={(v) => `${v}%`}\n accessibilityLabel=\"Media volume\"\n />\n </Card>\n );\n}\n"
135
155
  },
136
156
  {
137
157
  "name": "PriceRange",
138
158
  "title": "Price Range",
139
- "code": "import React, { useState } from 'react';\nimport { StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Slider } from '@/components/ui/slider';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]}\n {...rest}\n />\n );\n}\n\nfunction Header({ title, value }: { title: string; value?: string }) {\n const { colors } = useKit();\n return (\n <View style={s.head}>\n <Text style={[s.title, { color: colors.foreground }]}>{title}</Text>\n {value ? <Text style={[s.value, { color: colors.mutedForeground }]}>{value}</Text> : null}\n </View>\n );\n}\n\nconst PRICE_MAX = 2000;\n\nconst rupees = (v: number) => `₹${String(v).replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}`;\n\nconst PRICES = [2, 4, 7, 11, 16, 22, 30, 34, 38, 33, 29, 26, 21, 18, 14, 12, 9, 7, 5, 4, 3, 2, 2, 1];\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n head: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { fontSize: 15, fontWeight: '400' },\n value: { fontSize: 15, fontWeight: '400', fontVariant: ['tabular-nums'] },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -8 },\n note: { fontSize: 13 },\n});\n\nexport default function PriceRange() {\n const { colors } = useKit();\n const [price, setPrice] = useState<[number, number]>([400, 1400]);\n const per = PRICE_MAX / PRICES.length;\n const results = PRICES.reduce((n, c, i) => (i * per + per / 2 >= price[0] && i * per + per / 2 <= price[1] ? n + c : n), 0);\n return (\n <Card>\n <Header title=\"Price\" value={`${rupees(price[0])} – ${rupees(price[1])}${price[1] === PRICE_MAX ? '+' : ''}`} />\n <Slider\n range\n min={0}\n max={PRICE_MAX}\n step={50}\n value={price}\n onValueChange={setPrice}\n histogram={PRICES}\n histogramHeight={40}\n showMinMax\n maxLabel={`${rupees(PRICE_MAX)}+`}\n formatValue={rupees}\n accessibilityLabel=\"Price\"\n />\n <Text style={[s.note, { color: colors.mutedForeground }]}>{results * 3} stays match</Text>\n </Card>\n );\n}\n"
159
+ "code": "import React, { useState } from 'react';\nimport { StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Slider } from '@/components/ui/slider';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n s.card,\n screen\n ? { flex: 1, backgroundColor: colors.background, borderWidth: 0 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nfunction Header({ title, value }: { title: string; value?: string }) {\n const { colors } = useKit();\n return (\n <View style={s.head}>\n <Text style={[s.title, { color: colors.foreground }]}>{title}</Text>\n {value ? <Text style={[s.value, { color: colors.mutedForeground }]}>{value}</Text> : null}\n </View>\n );\n}\n\nconst PRICE_MAX = 2000;\n\nconst rupees = (v: number) => `₹${String(v).replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}`;\n\nconst PRICES = [2, 4, 7, 11, 16, 22, 30, 34, 38, 33, 29, 26, 21, 18, 14, 12, 9, 7, 5, 4, 3, 2, 2, 1];\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n head: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { fontSize: 15, fontWeight: '400' },\n value: { fontSize: 15, fontWeight: '400', fontVariant: ['tabular-nums'] },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -8 },\n note: { fontSize: 13 },\n});\n\nexport default function PriceRange({ screen }: DemoProps) {\n const { colors } = useKit();\n const [price, setPrice] = useState<[number, number]>([400, 1400]);\n const per = PRICE_MAX / PRICES.length;\n const results = PRICES.reduce((n, c, i) => (i * per + per / 2 >= price[0] && i * per + per / 2 <= price[1] ? n + c : n), 0);\n return (\n <Card screen={screen}>\n <Header title=\"Price\" value={`${rupees(price[0])} – ${rupees(price[1])}${price[1] === PRICE_MAX ? '+' : ''}`} />\n <Slider\n range\n min={0}\n max={PRICE_MAX}\n step={50}\n value={price}\n onValueChange={setPrice}\n histogram={PRICES}\n histogramHeight={40}\n showMinMax\n maxLabel={`${rupees(PRICE_MAX)}+`}\n formatValue={rupees}\n accessibilityLabel=\"Price\"\n />\n <Text style={[s.note, { color: colors.mutedForeground }]}>{results * 3} stays match</Text>\n </Card>\n );\n}\n"
140
160
  },
141
161
  {
142
162
  "name": "FontSize",
143
163
  "title": "Font Size",
144
- "code": "import React, { useState } from 'react';\nimport { StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Slider } from '@/components/ui/slider';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]}\n {...rest}\n />\n );\n}\n\nfunction Header({ title, value }: { title: string; value?: string }) {\n const { colors } = useKit();\n return (\n <View style={s.head}>\n <Text style={[s.title, { color: colors.foreground }]}>{title}</Text>\n {value ? <Text style={[s.value, { color: colors.mutedForeground }]}>{value}</Text> : null}\n </View>\n );\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n head: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { fontSize: 15, fontWeight: '400' },\n value: { fontSize: 15, fontWeight: '400', fontVariant: ['tabular-nums'] },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -8 },\n note: { fontSize: 13 },\n});\n\nexport default function FontSize() {\n const { colors } = useKit();\n const [size, setSize] = useState(17);\n const glyph = (px: number) => <Text style={{ color: colors.mutedForeground, fontSize: px, fontWeight: '400' }}>A</Text>;\n return (\n <Card>\n <Header title=\"Text size\" value={`${size} pt`} />\n <Text style={{ color: colors.foreground, fontSize: size, lineHeight: size * 1.35 }}>\n Your reading view will look like this.\n </Text>\n <Slider\n variant=\"stepped\"\n min={13}\n max={23}\n step={2}\n value={size}\n onValueChange={setSize}\n leftIcon={glyph(13)}\n rightIcon={glyph(21)}\n showValue=\"never\"\n accessibilityLabel=\"Text size\"\n />\n </Card>\n );\n}\n"
164
+ "code": "import React, { useState } from 'react';\nimport { StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Slider } from '@/components/ui/slider';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n s.card,\n screen\n ? { flex: 1, backgroundColor: colors.background, borderWidth: 0 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nfunction Header({ title, value }: { title: string; value?: string }) {\n const { colors } = useKit();\n return (\n <View style={s.head}>\n <Text style={[s.title, { color: colors.foreground }]}>{title}</Text>\n {value ? <Text style={[s.value, { color: colors.mutedForeground }]}>{value}</Text> : null}\n </View>\n );\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n head: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { fontSize: 15, fontWeight: '400' },\n value: { fontSize: 15, fontWeight: '400', fontVariant: ['tabular-nums'] },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -8 },\n note: { fontSize: 13 },\n});\n\nexport default function FontSize({ screen }: DemoProps) {\n const { colors } = useKit();\n const [size, setSize] = useState(17);\n const glyph = (px: number) => <Text style={{ color: colors.mutedForeground, fontSize: px, fontWeight: '400' }}>A</Text>;\n return (\n <Card screen={screen}>\n <Header title=\"Text size\" value={`${size} pt`} />\n <Text style={{ color: colors.foreground, fontSize: size, lineHeight: size * 1.35 }}>\n Your reading view will look like this.\n </Text>\n <Slider\n variant=\"stepped\"\n min={13}\n max={23}\n step={2}\n value={size}\n onValueChange={setSize}\n leftIcon={glyph(13)}\n rightIcon={glyph(21)}\n showValue=\"never\"\n accessibilityLabel=\"Text size\"\n />\n </Card>\n );\n}\n"
145
165
  },
146
166
  {
147
167
  "name": "Sizes",
148
168
  "title": "Sizes",
149
- "code": "import { StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Slider } from '@/components/ui/slider';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]}\n {...rest}\n />\n );\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n head: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { fontSize: 15, fontWeight: '400' },\n value: { fontSize: 15, fontWeight: '400', fontVariant: ['tabular-nums'] },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -8 },\n note: { fontSize: 13 },\n});\n\nexport default function Sizes() {\n const { colors } = useKit();\n const label = (t: string) => <Text style={[s.label, { color: colors.mutedForeground }]}>{t}</Text>;\n return (\n <Card>\n {label('Small')}\n <Slider size=\"sm\" defaultValue={25} accessibilityLabel=\"Small\" />\n {label('Medium')}\n <Slider size=\"md\" defaultValue={50} accessibilityLabel=\"Medium\" />\n {label('Large')}\n <Slider size=\"lg\" defaultValue={75} accessibilityLabel=\"Large\" />\n {label('Disabled')}\n <Slider defaultValue={40} disabled accessibilityLabel=\"Disabled\" />\n </Card>\n );\n}\n"
169
+ "code": "import { StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Slider } from '@/components/ui/slider';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n s.card,\n screen\n ? { flex: 1, backgroundColor: colors.background, borderWidth: 0 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n head: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { fontSize: 15, fontWeight: '400' },\n value: { fontSize: 15, fontWeight: '400', fontVariant: ['tabular-nums'] },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -8 },\n note: { fontSize: 13 },\n});\n\nexport default function Sizes({ screen }: DemoProps) {\n const { colors } = useKit();\n const label = (t: string) => <Text style={[s.label, { color: colors.mutedForeground }]}>{t}</Text>;\n return (\n <Card screen={screen}>\n {label('Small')}\n <Slider size=\"sm\" defaultValue={25} accessibilityLabel=\"Small\" />\n {label('Medium')}\n <Slider size=\"md\" defaultValue={50} accessibilityLabel=\"Medium\" />\n {label('Large')}\n <Slider size=\"lg\" defaultValue={75} accessibilityLabel=\"Large\" />\n {label('Disabled')}\n <Slider defaultValue={40} disabled accessibilityLabel=\"Disabled\" />\n </Card>\n );\n}\n"
150
170
  },
151
171
  {
152
172
  "name": "Colors",
153
173
  "title": "Colors",
154
- "code": "import { StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Slider } from '@/components/ui/slider';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]}\n {...rest}\n />\n );\n}\n\nfunction Header({ title, value }: { title: string; value?: string }) {\n const { colors } = useKit();\n return (\n <View style={s.head}>\n <Text style={[s.title, { color: colors.foreground }]}>{title}</Text>\n {value ? <Text style={[s.value, { color: colors.mutedForeground }]}>{value}</Text> : null}\n </View>\n );\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n head: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { fontSize: 15, fontWeight: '400' },\n value: { fontSize: 15, fontWeight: '400', fontVariant: ['tabular-nums'] },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -8 },\n note: { fontSize: 13 },\n});\n\nexport default function Colors() {\n const { colors } = useKit();\n return (\n <Card>\n <Header title=\"Daily goals\" />\n <Slider defaultValue={80} accent={colors.success} showValue=\"always\" formatValue={(v) => `${v}% steps`} accessibilityLabel=\"Steps\" />\n <Slider variant=\"bar\" size=\"sm\" defaultValue={55} accent={colors.warning} accessibilityLabel=\"Calories\" />\n <Slider variant=\"stepped\" min={0} max={10} step={1} defaultValue={3} accent={colors.danger} accessibilityLabel=\"Screen time hours\" />\n </Card>\n );\n}\n"
174
+ "code": "import { StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Slider } from '@/components/ui/slider';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n s.card,\n screen\n ? { flex: 1, backgroundColor: colors.background, borderWidth: 0 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nfunction Header({ title, value }: { title: string; value?: string }) {\n const { colors } = useKit();\n return (\n <View style={s.head}>\n <Text style={[s.title, { color: colors.foreground }]}>{title}</Text>\n {value ? <Text style={[s.value, { color: colors.mutedForeground }]}>{value}</Text> : null}\n </View>\n );\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n head: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { fontSize: 15, fontWeight: '400' },\n value: { fontSize: 15, fontWeight: '400', fontVariant: ['tabular-nums'] },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -8 },\n note: { fontSize: 13 },\n});\n\nexport default function Colors({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Header title=\"Daily goals\" />\n <Slider defaultValue={80} accent={colors.success} showValue=\"always\" formatValue={(v) => `${v}% steps`} accessibilityLabel=\"Steps\" />\n <Slider variant=\"bar\" size=\"sm\" defaultValue={55} accent={colors.warning} accessibilityLabel=\"Calories\" />\n <Slider variant=\"stepped\" min={0} max={10} step={1} defaultValue={3} accent={colors.danger} accessibilityLabel=\"Screen time hours\" />\n </Card>\n );\n}\n"
155
175
  }
156
176
  ]
157
177
  }
@@ -6,6 +6,7 @@
6
6
  "files": [
7
7
  "src/components/ui/spin-wheel.tsx"
8
8
  ],
9
+ "summary": "A prize wheel whose flapper ticks over every divider as it eases onto the win.",
9
10
  "props": [
10
11
  [
11
12
  "segments",
@@ -110,5 +111,28 @@
110
111
  "Spin from your own button (optionally onto a fixed index), or cancel and turn back to the start."
111
112
  ]
112
113
  ],
114
+ "behavior": [
115
+ [
116
+ "Touch",
117
+ "Tap the SPIN hub or flick the wheel; a harder flick throws it further. A slow drag just springs back to rest."
118
+ ],
119
+ [
120
+ "Motion",
121
+ "It keeps turning while getResult resolves, then eases onto the chosen wedge; the other wedges dim and the winner glows while the rim lights flash."
122
+ ],
123
+ [
124
+ "Reduced motion",
125
+ "The idle chasing lights, the win celebration and the winner pulse are skipped; the spin itself still plays."
126
+ ],
127
+ [
128
+ "Haptics",
129
+ "A selection tick each time the flapper crosses a divider, a light impact on the hub, and a success notification on the win (skipped on web)."
130
+ ],
131
+ [
132
+ "Screen readers",
133
+ "The wheel is an image whose label lists every prize; the hub is a button with busy state while spinning, and the result is announced."
134
+ ]
135
+ ],
136
+ "usage": null,
113
137
  "examples": []
114
138
  }