@expokit/cli 0.3.0 → 0.4.1

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 (188) hide show
  1. package/README.md +8 -4
  2. package/bin/expo-kit.js +137 -24
  3. package/bin/lib.mjs +110 -1
  4. package/bin/mcp.mjs +40 -18
  5. package/package.json +2 -2
  6. package/registry/_docs/accordion.json +1 -1
  7. package/registry/_docs/assistant-app.json +47 -0
  8. package/registry/_docs/avatar.json +6 -6
  9. package/registry/_docs/badge.json +4 -4
  10. package/registry/_docs/bottom-nav.json +6 -6
  11. package/registry/_docs/bottom-sheet.json +3 -3
  12. package/registry/_docs/bullet.json +6 -0
  13. package/registry/_docs/button-3d.json +3 -3
  14. package/registry/_docs/button.json +2 -2
  15. package/registry/_docs/calendar.json +5 -5
  16. package/registry/_docs/card.json +1 -1
  17. package/registry/_docs/checkout.json +7 -1
  18. package/registry/_docs/chip-group.json +1 -1
  19. package/registry/_docs/confetti.json +3 -3
  20. package/registry/_docs/date-picker.json +4 -4
  21. package/registry/_docs/donut-shop.json +88 -0
  22. package/registry/_docs/drawer.json +4 -4
  23. package/registry/_docs/fintech-app.json +49 -0
  24. package/registry/_docs/flip-card.json +4 -4
  25. package/registry/_docs/floating-input.json +5 -5
  26. package/registry/_docs/food-app.json +47 -0
  27. package/registry/_docs/glass.json +1 -1
  28. package/registry/_docs/grain.json +80 -0
  29. package/registry/_docs/isometric-bar.json +6 -0
  30. package/registry/_docs/liquidglass-tab.json +1 -1
  31. package/registry/_docs/morphing-button-group.json +96 -0
  32. package/registry/_docs/orb-reveal.json +132 -0
  33. package/registry/_docs/otp-input.json +4 -4
  34. package/registry/_docs/password-input.json +3 -3
  35. package/registry/_docs/payment-card.json +3 -3
  36. package/registry/_docs/pixel-bar.json +6 -0
  37. package/registry/_docs/polar-area.json +6 -0
  38. package/registry/_docs/progress-steps.json +4 -4
  39. package/registry/_docs/progress.json +2 -2
  40. package/registry/_docs/qr-code-button.json +122 -0
  41. package/registry/_docs/radio-group.json +1 -1
  42. package/registry/_docs/search-bar.json +4 -4
  43. package/registry/_docs/segmented-control.json +1 -1
  44. package/registry/_docs/select.json +7 -7
  45. package/registry/_docs/settings.json +6 -0
  46. package/registry/_docs/skeleton.json +3 -3
  47. package/registry/_docs/slider.json +6 -6
  48. package/registry/_docs/spinner.json +3 -3
  49. package/registry/_docs/stateful-button.json +1 -1
  50. package/registry/_docs/stepper.json +7 -7
  51. package/registry/_docs/tabs.json +2 -2
  52. package/registry/_docs/text-animation.json +4 -4
  53. package/registry/_docs/textarea.json +3 -3
  54. package/registry/_docs/ticket-shape.json +1 -1
  55. package/registry/_docs/tilt-card.json +4 -4
  56. package/registry/_docs/time-picker.json +6 -6
  57. package/registry/_docs/timeline-chart.json +6 -0
  58. package/registry/_docs/timeline.json +6 -6
  59. package/registry/_docs/toast.json +8 -8
  60. package/registry/_docs/treemap.json +6 -0
  61. package/registry/_docs/waterfall.json +6 -0
  62. package/registry/accordion/files/src/components/ui/accordion.tsx +36 -7
  63. package/registry/accordion/files/src/lib/kit.tsx +8 -5
  64. package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +12 -8
  65. package/registry/action-sheet/files/src/lib/kit.tsx +8 -5
  66. package/registry/alert/files/src/components/ui/alert.tsx +11 -5
  67. package/registry/alert/files/src/components/ui/button.tsx +35 -6
  68. package/registry/alert/files/src/lib/kit.tsx +8 -5
  69. package/registry/assistant-app.json +49 -0
  70. package/registry/avatar/files/src/components/ui/avatar.tsx +7 -4
  71. package/registry/avatar/files/src/lib/kit.tsx +8 -5
  72. package/registry/badge/files/src/components/ui/badge.tsx +23 -9
  73. package/registry/badge/files/src/lib/kit.tsx +8 -5
  74. package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +21 -8
  75. package/registry/bottom-nav/files/src/lib/kit.tsx +8 -5
  76. package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +7 -6
  77. package/registry/bottom-sheet/files/src/lib/kit.tsx +8 -5
  78. package/registry/button/files/src/components/ui/button.tsx +35 -6
  79. package/registry/button/files/src/lib/kit.tsx +8 -5
  80. package/registry/button-3d/files/src/components/ui/button-3d.tsx +35 -7
  81. package/registry/button-3d/files/src/lib/kit.tsx +8 -5
  82. package/registry/calendar/files/src/components/ui/calendar.tsx +68 -19
  83. package/registry/calendar/files/src/lib/kit.tsx +8 -5
  84. package/registry/card/files/src/components/ui/card.tsx +32 -10
  85. package/registry/card/files/src/lib/kit.tsx +8 -5
  86. package/registry/checkbox/files/src/components/ui/checkbox.tsx +31 -8
  87. package/registry/checkbox/files/src/lib/kit.tsx +8 -5
  88. package/registry/chip-group/files/src/components/ui/chip-group.tsx +39 -7
  89. package/registry/chip-group/files/src/lib/kit.tsx +8 -5
  90. package/registry/confetti/files/src/lib/kit.tsx +8 -5
  91. package/registry/coupon/files/src/components/ui/coupon.tsx +55 -28
  92. package/registry/coupon/files/src/components/ui/flip-card.tsx +23 -4
  93. package/registry/coupon/files/src/components/ui/tilt-card.tsx +59 -18
  94. package/registry/coupon/files/src/lib/kit.tsx +8 -5
  95. package/registry/date-picker/files/src/components/ui/calendar.tsx +68 -19
  96. package/registry/date-picker/files/src/components/ui/date-picker.tsx +98 -44
  97. package/registry/date-picker/files/src/lib/kit.tsx +8 -5
  98. package/registry/dialog/files/src/components/ui/dialog.tsx +19 -9
  99. package/registry/dialog/files/src/lib/kit.tsx +8 -5
  100. package/registry/donut-shop.json +18 -0
  101. package/registry/drawer/files/src/components/ui/drawer.tsx +31 -21
  102. package/registry/drawer/files/src/lib/kit.tsx +8 -5
  103. package/registry/fintech-app.json +57 -0
  104. package/registry/flip-card/files/src/components/ui/flip-card.tsx +23 -4
  105. package/registry/flip-card/files/src/lib/kit.tsx +8 -5
  106. package/registry/floating-input/files/src/components/ui/floating-input.tsx +91 -32
  107. package/registry/floating-input/files/src/lib/kit.tsx +8 -5
  108. package/registry/food-app.json +55 -0
  109. package/registry/grain/files/src/components/ui/grain.tsx +113 -0
  110. package/registry/{tag-input → grain}/files/src/lib/kit.tsx +8 -5
  111. package/registry/grain.json +17 -0
  112. package/registry/kit/files/src/lib/kit.tsx +8 -5
  113. package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +2 -2
  114. package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +1 -1
  115. package/registry/list-item/files/src/components/ui/list-item.tsx +28 -17
  116. package/registry/list-item/files/src/lib/kit.tsx +8 -5
  117. package/registry/morphing-button-group.json +18 -0
  118. package/registry/orb-reveal.json +20 -0
  119. package/registry/otp-input/files/src/components/ui/otp-input.tsx +57 -24
  120. package/registry/otp-input/files/src/lib/kit.tsx +8 -5
  121. package/registry/password-input/files/src/components/ui/floating-input.tsx +91 -32
  122. package/registry/password-input/files/src/components/ui/password-input.tsx +55 -21
  123. package/registry/password-input/files/src/lib/kit.tsx +8 -5
  124. package/registry/payment-card/files/src/components/ui/flip-card.tsx +23 -4
  125. package/registry/payment-card/files/src/components/ui/payment-card.tsx +35 -18
  126. package/registry/payment-card/files/src/components/ui/tilt-card.tsx +59 -18
  127. package/registry/payment-card/files/src/lib/kit.tsx +8 -5
  128. package/registry/progress/files/src/components/ui/progress.tsx +16 -6
  129. package/registry/progress/files/src/lib/kit.tsx +8 -5
  130. package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +22 -11
  131. package/registry/progress-steps/files/src/lib/kit.tsx +8 -5
  132. package/registry/qr-code-button.json +18 -0
  133. package/registry/radio-group/files/src/components/ui/radio-group.tsx +48 -17
  134. package/registry/radio-group/files/src/lib/kit.tsx +8 -5
  135. package/registry/search-bar/files/src/components/ui/search-bar.tsx +81 -26
  136. package/registry/search-bar/files/src/lib/kit.tsx +8 -5
  137. package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +40 -10
  138. package/registry/segmented-control/files/src/lib/kit.tsx +8 -5
  139. package/registry/select/files/src/components/ui/select.tsx +161 -86
  140. package/registry/select/files/src/lib/kit.tsx +8 -5
  141. package/registry/skeleton/files/src/components/ui/skeleton.tsx +4 -4
  142. package/registry/skeleton/files/src/lib/kit.tsx +8 -5
  143. package/registry/slider/files/src/components/ui/slider.tsx +56 -27
  144. package/registry/slider/files/src/lib/kit.tsx +8 -5
  145. package/registry/spinner/files/src/components/ui/spinner.tsx +17 -12
  146. package/registry/spinner/files/src/lib/kit.tsx +8 -5
  147. package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +12 -4
  148. package/registry/stateful-button/files/src/lib/kit.tsx +8 -5
  149. package/registry/stepper/files/src/components/ui/stepper.tsx +83 -29
  150. package/registry/stepper/files/src/lib/kit.tsx +8 -5
  151. package/registry/switch/files/src/components/ui/switch.tsx +60 -22
  152. package/registry/switch/files/src/lib/kit.tsx +8 -5
  153. package/registry/tabs/files/src/components/ui/tabs.tsx +15 -10
  154. package/registry/tabs/files/src/lib/kit.tsx +8 -5
  155. package/registry/text-animation/files/src/components/ui/text-animation.tsx +11 -6
  156. package/registry/text-animation/files/src/lib/kit.tsx +8 -5
  157. package/registry/textarea/files/src/components/ui/textarea.tsx +115 -45
  158. package/registry/textarea/files/src/lib/kit.tsx +8 -5
  159. package/registry/ticket-shape/files/src/lib/kit.tsx +8 -5
  160. package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +59 -18
  161. package/registry/tilt-card/files/src/lib/kit.tsx +8 -5
  162. package/registry/time-picker/files/src/components/ui/time-picker.tsx +156 -75
  163. package/registry/time-picker/files/src/lib/kit.tsx +8 -5
  164. package/registry/timeline/files/src/components/ui/avatar.tsx +7 -4
  165. package/registry/timeline/files/src/components/ui/timeline.tsx +19 -12
  166. package/registry/timeline/files/src/lib/kit.tsx +8 -5
  167. package/registry/toast/files/src/components/ui/button.tsx +35 -6
  168. package/registry/toast/files/src/components/ui/toast.tsx +25 -14
  169. package/registry/toast/files/src/lib/kit.tsx +8 -5
  170. package/schema/components.schema.json +1 -1
  171. package/registry/_docs/color-swatch.json +0 -82
  172. package/registry/_docs/kpi-card.json +0 -14
  173. package/registry/_docs/like-button.json +0 -92
  174. package/registry/_docs/morph-button.json +0 -128
  175. package/registry/_docs/pagination-dots.json +0 -108
  176. package/registry/_docs/shimmer-button.json +0 -98
  177. package/registry/_docs/tag-input.json +0 -148
  178. package/registry/color-swatch.json +0 -17
  179. package/registry/kpi-card/files/src/components/charts/kpi-card.tsx +0 -45
  180. package/registry/kpi-card.json +0 -14
  181. package/registry/like-button.json +0 -18
  182. package/registry/morph-button.json +0 -18
  183. package/registry/pagination-dots/files/src/components/ui/pagination-dots.tsx +0 -262
  184. package/registry/pagination-dots/files/src/lib/kit.tsx +0 -219
  185. package/registry/pagination-dots.json +0 -15
  186. package/registry/shimmer-button.json +0 -17
  187. package/registry/tag-input/files/src/components/ui/tag-input.tsx +0 -720
  188. package/registry/tag-input.json +0 -17
@@ -146,32 +146,32 @@
146
146
  {
147
147
  "name": "ControlCenter",
148
148
  "title": "Control Center",
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/** 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', flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { flexShrink: 1, 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"
150
150
  },
151
151
  {
152
152
  "name": "Default",
153
153
  "title": "Default",
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/** 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', flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { flexShrink: 1, 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"
155
155
  },
156
156
  {
157
157
  "name": "PriceRange",
158
158
  "title": "Price Range",
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/** 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', flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { flexShrink: 1, 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"
160
160
  },
161
161
  {
162
162
  "name": "FontSize",
163
163
  "title": "Font Size",
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/** 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/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\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', flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { flexShrink: 1, 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 maxFontSizeMultiplier={FONT_CAP} 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"
165
165
  },
166
166
  {
167
167
  "name": "Sizes",
168
168
  "title": "Sizes",
169
- "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', flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { flexShrink: 1, 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"
170
170
  },
171
171
  {
172
172
  "name": "Colors",
173
173
  "title": "Colors",
174
- "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', flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'baseline' },\n title: { flexShrink: 1, 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"
175
175
  }
176
176
  ]
177
177
  }
@@ -90,17 +90,17 @@
90
90
  {
91
91
  "name": "LoaderGallery",
92
92
  "title": "Loader Gallery",
93
- "code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner, type SpinnerVariant } from '@/components/ui/spinner';\nimport { readableOn, useKit } from '@/lib/kit';\n\nconst LOADERS: SpinnerVariant[] = ['ring', 'dots', 'bars', 'pulse', 'orbit', 'bounce', 'wave', 'grid', 'typing', 'arc', 'square', 'line'];\n\nexport default function LoaderGallery() {\n const { colors, radius } = useKit();\n return (\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', rowGap: 12 }}>\n {LOADERS.map((variant) => (\n <View key={variant} style={{ width: '33.33%', alignItems: 'center', gap: 8, paddingHorizontal: 4 }}>\n <View\n style={{\n alignSelf: 'stretch',\n height: 64,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: radius.md,\n backgroundColor: colors.muted,\n }}\n >\n <Spinner\n variant={variant}\n size={variant === 'line' ? 'sm' : 'lg'}\n track={variant === 'line'}\n label={`${variant} loader`}\n style={variant === 'line' ? { width: '72%' } : undefined}\n />\n </View>\n <Text style={{ fontSize: 12, color: colors.mutedForeground }}>{variant}</Text>\n </View>\n ))}\n </View>\n );\n}\n"
93
+ "code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner, type SpinnerVariant } from '@/components/ui/spinner';\nimport { readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nconst LOADERS: SpinnerVariant[] = ['ring', 'dots', 'bars', 'pulse', 'orbit', 'bounce', 'wave', 'grid', 'typing', 'arc', 'square', 'line'];\n\nexport default function LoaderGallery() {\n const { colors, radius } = useKit();\n return (\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', rowGap: 12 }}>\n {LOADERS.map((variant) => (\n <View key={variant} style={{ width: '33.33%', alignItems: 'center', gap: 8, paddingHorizontal: 4 }}>\n <View\n style={{\n alignSelf: 'stretch',\n height: 64,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: radius.md,\n backgroundColor: colors.muted,\n }}\n >\n <Spinner\n variant={variant}\n size={variant === 'line' ? 'sm' : 'lg'}\n track={variant === 'line'}\n label={`${variant} loader`}\n style={variant === 'line' ? { width: '72%' } : undefined}\n />\n </View>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 12, color: colors.mutedForeground }}>{variant}</Text>\n </View>\n ))}\n </View>\n );\n}\n"
94
94
  },
95
95
  {
96
96
  "name": "InButton",
97
97
  "title": "In Button",
98
- "code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { Spinner, type SpinnerVariant } from '@/components/ui/spinner';\nimport { readableOn, useKit } from '@/lib/kit';\n\nexport default function InButton() {\n const { colors, radius } = useKit();\n const [busy, setBusy] = useState(false);\n const fg = readableOn(colors.accent);\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ busy }}\n onPress={() => {\n setBusy(true);\n setTimeout(() => setBusy(false), 2200);\n }}\n style={{\n height: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n alignSelf: 'stretch',\n }}\n >\n {busy ? (\n <Spinner size=\"sm\" color={fg} label=\"Placing order\" labelPosition=\"right\" />\n ) : (\n <Text style={{ color: fg, fontSize: 16, fontWeight: '400' }}>Place order · ₹749</Text>\n )}\n </Pressable>\n );\n}\n"
98
+ "code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { Spinner, type SpinnerVariant } from '@/components/ui/spinner';\nimport { readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nexport default function InButton() {\n const { colors, radius } = useKit();\n const [busy, setBusy] = useState(false);\n const fg = readableOn(colors.accent);\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ busy }}\n onPress={() => {\n setBusy(true);\n setTimeout(() => setBusy(false), 2200);\n }}\n style={{\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n alignSelf: 'stretch',\n }}\n >\n {busy ? (\n <Spinner size=\"sm\" color={fg} label=\"Placing order\" labelPosition=\"right\" />\n ) : (\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: fg, fontSize: 16, fontWeight: '400' }}>Place order · ₹749</Text>\n )}\n </Pressable>\n );\n}\n"
99
99
  },
100
100
  {
101
101
  "name": "LoadingCard",
102
102
  "title": "Loading Card",
103
- "code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner, type SpinnerVariant } from '@/components/ui/spinner';\nimport { readableOn, useKit } from '@/lib/kit';\n\nexport default function LoadingCard() {\n const { colors, radius } = useKit();\n return (\n <View\n style={{\n alignSelf: 'stretch',\n alignItems: 'center',\n gap: 6,\n paddingVertical: 32,\n paddingHorizontal: 20,\n borderRadius: radius.lg,\n backgroundColor: colors.card,\n borderWidth: 1,\n borderColor: colors.border,\n }}\n >\n <Spinner size=\"xl\" track label=\"Preparing your itinerary\" labelPosition=\"bottom\" />\n <Text style={{ fontSize: 13, color: colors.mutedForeground, textAlign: 'center' }}>\n Checking 3 flights and 2 hotels\n </Text>\n </View>\n );\n}\n"
103
+ "code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner, type SpinnerVariant } from '@/components/ui/spinner';\nimport { 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\nexport default function LoadingCard({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { alignSelf: 'stretch', alignItems: 'center', gap: 6, paddingVertical: 32, paddingHorizontal: 20 },\n screen\n ? { flex: 1, justifyContent: 'center', backgroundColor: colors.background }\n : { borderRadius: radius.lg, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },\n ]}\n >\n <Spinner size=\"xl\" track label=\"Preparing your itinerary\" labelPosition=\"bottom\" />\n <Text style={{ fontSize: 13, color: colors.mutedForeground, textAlign: 'center' }}>\n Checking 3 flights and 2 hotels\n </Text>\n </View>\n );\n}\n"
104
104
  },
105
105
  {
106
106
  "name": "InlineStatus",
@@ -168,7 +168,7 @@
168
168
  {
169
169
  "name": "Controlled",
170
170
  "title": "Controlled",
171
- "code": "import React, { useRef, useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { StatefulButton, type StatefulButtonState } from '@/components/ui/stateful-button';\nimport { useKit } from '@/lib/kit';\n\nconst ORDER: StatefulButtonState[] = ['idle', 'loading', 'success', 'error'];\n\nexport default function Controlled() {\n const { colors, radius } = useKit();\n const [state, setState] = useState<StatefulButtonState>('idle');\n return (\n <View style={{ gap: 14 }}>\n <StatefulButton state={state} loadingText=\"Uploading\" successText=\"Uploaded\" errorText=\"Retry upload\">\n Upload\n </StatefulButton>\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>\n {ORDER.map((s) => (\n <Text\n key={s}\n accessibilityRole=\"button\"\n accessibilityState={{ selected: s === state }}\n onPress={() => setState(s)}\n style={{\n paddingHorizontal: 12,\n paddingVertical: 8,\n borderRadius: radius.capsule(34),\n overflow: 'hidden',\n fontSize: 13,\n color: s === state ? colors.accentForeground : colors.foreground,\n backgroundColor: s === state ? colors.accent : colors.muted,\n }}\n >\n {s}\n </Text>\n ))}\n </View>\n </View>\n );\n}\n"
171
+ "code": "import React, { useRef, useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { StatefulButton, type StatefulButtonState } from '@/components/ui/stateful-button';\nimport { useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nconst ORDER: StatefulButtonState[] = ['idle', 'loading', 'success', 'error'];\n\nexport default function Controlled() {\n const { colors, radius } = useKit();\n const [state, setState] = useState<StatefulButtonState>('idle');\n return (\n <View style={{ gap: 14 }}>\n <StatefulButton state={state} loadingText=\"Uploading\" successText=\"Uploaded\" errorText=\"Retry upload\">\n Upload\n </StatefulButton>\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>\n {ORDER.map((s) => (\n <Text\n key={s}\n maxFontSizeMultiplier={FONT_CAP}\n accessibilityRole=\"button\"\n accessibilityState={{ selected: s === state }}\n onPress={() => setState(s)}\n style={{\n paddingHorizontal: 12,\n paddingVertical: 8,\n borderRadius: radius.capsule(34),\n overflow: 'hidden',\n fontSize: 13,\n color: s === state ? colors.accentForeground : colors.foreground,\n backgroundColor: s === state ? colors.accent : colors.muted,\n }}\n >\n {s}\n </Text>\n ))}\n </View>\n </View>\n );\n}\n"
172
172
  },
173
173
  {
174
174
  "name": "VariantsAndSizes",
@@ -148,37 +148,37 @@
148
148
  {
149
149
  "name": "Cart",
150
150
  "title": "Cart",
151
- "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst eyebrow = { fontSize: 12, fontWeight: '400', letterSpacing: 0.6, textTransform: 'uppercase' } as const;\n\nconst money = (n: number) => {\n const [int, frac] = n.toFixed(2).split('.');\n return `₹${int.replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}${frac === '00' ? '' : `.${frac}`}`;\n};\n\nconst MENU = [\n { key: 'bowl', emoji: '🥗', name: 'Harvest bowl', note: 'Kale, squash, tahini', price: 349 },\n { key: 'ramen', emoji: '🍜', name: 'Miso ramen', note: 'Spicy · extra egg', price: 420 },\n { key: 'mochi', emoji: '🍡', name: 'Mochi trio', note: 'Matcha, yuzu, sesame', price: 180 },\n];\n\nexport default function Cart() {\n const { colors, radius } = useKit();\n const [qty, setQty] = useState<Record<string, number>>({ bowl: 1, ramen: 2, mochi: 0 });\n const total = MENU.reduce((sum, it) => sum + it.price * (qty[it.key] ?? 0), 0);\n return (\n <Card>\n <Text style={[eyebrow, { color: colors.mutedForeground }]}>Your order</Text>\n {MENU.map((it) => (\n <View key={it.key} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ width: 44, height: 44, borderRadius: radius.scale(12), backgroundColor: colors.muted, alignItems: 'center', justifyContent: 'center' }}>\n <Text style={{ fontSize: 22 }}>{it.emoji}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.foreground }}>{it.name}</Text>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{money(it.price)} · {it.note}</Text>\n </View>\n <Stepper\n variant=\"cart\"\n size=\"sm\"\n value={qty[it.key]}\n onValueChange={(n) => setQty((q) => ({ ...q, [it.key]: n }))}\n max={9}\n accessibilityLabel={`${it.name} quantity`}\n />\n </View>\n ))}\n <View style={{ height: StyleSheet.hairlineWidth, backgroundColor: colors.border }} />\n <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' }}>\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.foreground }}>Subtotal</Text>\n <Text style={{ fontSize: 18, fontWeight: '400', color: colors.foreground, fontVariant: ['tabular-nums'] }}>{money(total)}</Text>\n </View>\n </Card>\n );\n}\n"
151
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\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({ style, screen, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 16, gap: 12 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst eyebrow = { fontSize: 12, fontWeight: '400', letterSpacing: 0.6, textTransform: 'uppercase' } as const;\n\nconst money = (n: number) => {\n const [int, frac] = n.toFixed(2).split('.');\n return `₹${int.replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}${frac === '00' ? '' : `.${frac}`}`;\n};\n\nconst MENU = [\n { key: 'bowl', emoji: '🥗', name: 'Harvest bowl', note: 'Kale, squash, tahini', price: 349 },\n { key: 'ramen', emoji: '🍜', name: 'Miso ramen', note: 'Spicy · extra egg', price: 420 },\n { key: 'mochi', emoji: '🍡', name: 'Mochi trio', note: 'Matcha, yuzu, sesame', price: 180 },\n];\n\nexport default function Cart({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n const [qty, setQty] = useState<Record<string, number>>({ bowl: 1, ramen: 2, mochi: 0 });\n const total = MENU.reduce((sum, it) => sum + it.price * (qty[it.key] ?? 0), 0);\n return (\n <Card screen={screen}>\n <Text style={[eyebrow, { color: colors.mutedForeground }]}>Your order</Text>\n {MENU.map((it) => (\n <View key={it.key} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ width: 44, height: 44, borderRadius: radius.scale(12), backgroundColor: colors.muted, alignItems: 'center', justifyContent: 'center' }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 22 }}>{it.emoji}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.foreground }}>{it.name}</Text>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{money(it.price)} · {it.note}</Text>\n </View>\n <Stepper\n variant=\"cart\"\n size=\"sm\"\n value={qty[it.key]}\n onValueChange={(n) => setQty((q) => ({ ...q, [it.key]: n }))}\n max={9}\n accessibilityLabel={`${it.name} quantity`}\n />\n </View>\n ))}\n <View style={{ height: StyleSheet.hairlineWidth, backgroundColor: colors.border }} />\n <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' }}>\n <Text style={{ flexShrink: 1, fontSize: 15, fontWeight: '400', color: colors.foreground }}>Subtotal</Text>\n <Text style={{ fontSize: 18, fontWeight: '400', color: colors.foreground, fontVariant: ['tabular-nums'] }}>{money(total)}</Text>\n </View>\n </Card>\n );\n}\n"
152
152
  },
153
153
  {
154
154
  "name": "Guests",
155
155
  "title": "Guests",
156
- "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Guests() {\n const { colors } = useKit();\n const [adults, setAdults] = useState(2);\n const [children, setChildren] = useState(0);\n const [infants, setInfants] = useState(0);\n const cap = 6;\n const rule = <View style={{ height: StyleSheet.hairlineWidth, backgroundColor: colors.border }} />;\n return (\n <Card style={{ gap: 14 }}>\n <Stepper label=\"Adults\" description=\"Ages 13 or above\" value={adults} onValueChange={setAdults} min={1} max={cap - children} />\n {rule}\n <Stepper label=\"Children\" description=\"Ages 2–12\" value={children} onValueChange={setChildren} max={cap - adults} />\n {rule}\n <Stepper label=\"Infants\" description=\"Under 2\" value={infants} onValueChange={setInfants} max={5} />\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>\n This place has a maximum of {cap} guests, not including infants.\n </Text>\n </Card>\n );\n}\n"
156
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\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({ style, screen, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 16, gap: 12 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Guests({ screen }: DemoProps) {\n const { colors } = useKit();\n const [adults, setAdults] = useState(2);\n const [children, setChildren] = useState(0);\n const [infants, setInfants] = useState(0);\n const cap = 6;\n const rule = <View style={{ height: StyleSheet.hairlineWidth, backgroundColor: colors.border }} />;\n return (\n <Card screen={screen} style={{ gap: 14 }}>\n <Stepper label=\"Adults\" description=\"Ages 13 or above\" value={adults} onValueChange={setAdults} min={1} max={cap - children} />\n {rule}\n <Stepper label=\"Children\" description=\"Ages 2–12\" value={children} onValueChange={setChildren} max={cap - adults} />\n {rule}\n <Stepper label=\"Infants\" description=\"Under 2\" value={infants} onValueChange={setInfants} max={5} />\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>\n This place has a maximum of {cap} guests, not including infants.\n </Text>\n </Card>\n );\n}\n"
157
157
  },
158
158
  {
159
159
  "name": "AddToBag",
160
160
  "title": "Add To Bag",
161
- "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst money = (n: number) => {\n const [int, frac] = n.toFixed(2).split('.');\n return `₹${int.replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}${frac === '00' ? '' : `.${frac}`}`;\n};\n\nexport default function AddToBag() {\n const { colors, radius } = useKit();\n const [qty, setQty] = useState(1);\n const price = 1899;\n return (\n <Card>\n <View style={{ gap: 2 }}>\n <Text style={{ fontSize: 17, fontWeight: '400', color: colors.foreground }}>Linen overshirt</Text>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>Sand · Size M · Free returns</Text>\n </View>\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <Stepper variant=\"filled\" size=\"lg\" value={qty} onValueChange={setQty} min={1} max={10} accessibilityLabel=\"Quantity\" />\n <Pressable\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n height: 44,\n borderRadius: radius.capsule(44),\n backgroundColor: colors.primary,\n alignItems: 'center',\n justifyContent: 'center',\n opacity: pressed ? 0.85 : 1,\n })}\n >\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.primaryForeground, fontVariant: ['tabular-nums'] }}>\n Add to bag · {money(price * qty)}\n </Text>\n </Pressable>\n </View>\n <Text style={{ fontSize: 12, color: colors.mutedForeground }}>Only 10 left — hold + to add faster.</Text>\n </Card>\n );\n}\n"
161
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\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({ style, screen, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 16, gap: 12 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst money = (n: number) => {\n const [int, frac] = n.toFixed(2).split('.');\n return `₹${int.replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}${frac === '00' ? '' : `.${frac}`}`;\n};\n\nexport default function AddToBag({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n const [qty, setQty] = useState(1);\n const price = 1899;\n return (\n <Card screen={screen}>\n <View style={{ gap: 2 }}>\n <Text style={{ fontSize: 17, fontWeight: '400', color: colors.foreground }}>Linen overshirt</Text>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>Sand · Size M · Free returns</Text>\n </View>\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <Stepper variant=\"filled\" size=\"lg\" value={qty} onValueChange={setQty} min={1} max={10} accessibilityLabel=\"Quantity\" />\n <Pressable\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n minHeight: 44,\n borderRadius: radius.capsule(44),\n backgroundColor: colors.primary,\n alignItems: 'center',\n justifyContent: 'center',\n opacity: pressed ? 0.85 : 1,\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, fontWeight: '400', color: colors.primaryForeground, fontVariant: ['tabular-nums'] }}>\n Add to bag · {money(price * qty)}\n </Text>\n </Pressable>\n </View>\n <Text style={{ fontSize: 12, color: colors.mutedForeground }}>Only 10 left — hold + to add faster.</Text>\n </Card>\n );\n}\n"
162
162
  },
163
163
  {
164
164
  "name": "Menu",
165
165
  "title": "Menu",
166
- "code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst eyebrow = { fontSize: 12, fontWeight: '400', letterSpacing: 0.6, textTransform: 'uppercase' } as const;\n\nexport default function Menu() {\n const { colors } = useKit();\n return (\n <Card>\n <Text style={[eyebrow, { color: colors.mutedForeground }]}>Popular</Text>\n <Stepper variant=\"cart\" addLabel=\"Add\" label=\"Pistachio croissant\" description=\"₹220 · Baked this morning\" accessibilityLabel=\"Pistachio croissant\" />\n <Stepper variant=\"cart\" addLabel=\"Add\" label=\"Iced oat latte\" description=\"₹280 · 16 oz\" defaultValue={1} accessibilityLabel=\"Iced oat latte\" />\n </Card>\n );\n}\n"
166
+ "code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\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({ style, screen, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 16, gap: 12 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst eyebrow = { fontSize: 12, fontWeight: '400', letterSpacing: 0.6, textTransform: 'uppercase' } as const;\n\nexport default function Menu({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Text style={[eyebrow, { color: colors.mutedForeground }]}>Popular</Text>\n <Stepper variant=\"cart\" addLabel=\"Add\" label=\"Pistachio croissant\" description=\"₹220 · Baked this morning\" accessibilityLabel=\"Pistachio croissant\" />\n <Stepper variant=\"cart\" addLabel=\"Add\" label=\"Iced oat latte\" description=\"₹280 · 16 oz\" defaultValue={1} accessibilityLabel=\"Iced oat latte\" />\n </Card>\n );\n}\n"
167
167
  },
168
168
  {
169
169
  "name": "Formatted",
170
170
  "title": "Formatted",
171
- "code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst money = (n: number) => {\n const [int, frac] = n.toFixed(2).split('.');\n return `₹${int.replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}${frac === '00' ? '' : `.${frac}`}`;\n};\n\nexport default function Formatted() {\n return (\n <Card style={{ gap: 16 }}>\n <Stepper variant=\"filled\" size=\"sm\" label=\"Nights\" description=\"Check-in Fri, Oct 24\" defaultValue={3} min={1} max={28} suffix={(n) => (n === 1 ? 'night' : 'nights')} />\n <Stepper variant=\"filled\" size=\"sm\" label=\"Driver tip\" description=\"100% goes to your driver\" defaultValue={50} step={10} min={0} max={500} format={money} />\n <Stepper variant=\"filled\" size=\"sm\" label=\"Search radius\" defaultValue={2.5} step={0.5} min={0.5} max={10} suffix=\"km\" />\n <Stepper variant=\"compact\" label=\"Seats\" description=\"₹499 per seat / month\" defaultValue={5} min={1} max={50} />\n </Card>\n );\n}\n"
171
+ "code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\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({ style, screen, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 16, gap: 12 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst money = (n: number) => {\n const [int, frac] = n.toFixed(2).split('.');\n return `₹${int.replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}${frac === '00' ? '' : `.${frac}`}`;\n};\n\nexport default function Formatted({ screen }: DemoProps) {\n return (\n <Card screen={screen} style={{ gap: 16 }}>\n <Stepper variant=\"filled\" size=\"sm\" label=\"Nights\" description=\"Check-in Fri, Oct 24\" defaultValue={3} min={1} max={28} suffix={(n) => (n === 1 ? 'night' : 'nights')} />\n <Stepper variant=\"filled\" size=\"sm\" label=\"Driver tip\" description=\"100% goes to your driver\" defaultValue={50} step={10} min={0} max={500} format={money} />\n <Stepper variant=\"filled\" size=\"sm\" label=\"Search radius\" defaultValue={2.5} step={0.5} min={0.5} max={10} suffix=\"km\" />\n <Stepper variant=\"compact\" label=\"Seats\" description=\"₹499 per seat / month\" defaultValue={5} min={1} max={50} />\n </Card>\n );\n}\n"
172
172
  },
173
173
  {
174
174
  "name": "Sizes",
175
175
  "title": "Sizes",
176
- "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Sizes() {\n const { colors } = useKit();\n const row = (name: string, el: React.ReactNode) => (\n <View key={name} style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', minHeight: 48 }}>\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{name}</Text>\n {el}\n </View>\n );\n return (\n <Card style={{ gap: 4 }}>\n {row('Small', <Stepper size=\"sm\" defaultValue={2} max={20} accessibilityLabel=\"Small stepper\" />)}\n {row('Medium', <Stepper size=\"md\" defaultValue={2} max={20} accessibilityLabel=\"Medium stepper\" />)}\n {row('Large', <Stepper size=\"lg\" defaultValue={2} max={20} accessibilityLabel=\"Large stepper\" />)}\n {row('Compact', <Stepper variant=\"compact\" defaultValue={2} max={20} accessibilityLabel=\"Compact stepper\" />)}\n </Card>\n );\n}\n"
176
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\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({ style, screen, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 16, gap: 12 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Sizes({ screen }: DemoProps) {\n const { colors } = useKit();\n const row = (name: string, el: React.ReactNode) => (\n <View key={name} style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', minHeight: 48 }}>\n <Text style={{ flexShrink: 1, fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{name}</Text>\n {el}\n </View>\n );\n return (\n <Card screen={screen} style={{ gap: 4 }}>\n {row('Small', <Stepper size=\"sm\" defaultValue={2} max={20} accessibilityLabel=\"Small stepper\" />)}\n {row('Medium', <Stepper size=\"md\" defaultValue={2} max={20} accessibilityLabel=\"Medium stepper\" />)}\n {row('Large', <Stepper size=\"lg\" defaultValue={2} max={20} accessibilityLabel=\"Large stepper\" />)}\n {row('Compact', <Stepper variant=\"compact\" defaultValue={2} max={20} accessibilityLabel=\"Compact stepper\" />)}\n </Card>\n );\n}\n"
177
177
  },
178
178
  {
179
179
  "name": "Colors",
180
180
  "title": "Colors",
181
- "code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Colors() {\n const { colors } = useKit();\n return (\n <Card style={{ gap: 16 }}>\n <Stepper variant=\"filled\" accent={colors.success} label=\"Water\" description=\"Glasses today\" defaultValue={6} max={12} />\n <Stepper variant=\"compact\" accent={colors.warning} label=\"Espresso shots\" defaultValue={2} max={4} />\n <Stepper variant=\"cart\" accent={colors.danger} addLabel=\"Add\" label=\"Hot sauce\" description=\"₹30 each\" />\n </Card>\n );\n}\n"
181
+ "code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\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({ style, screen, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 16, gap: 12 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Colors({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen} style={{ gap: 16 }}>\n <Stepper variant=\"filled\" accent={colors.success} label=\"Water\" description=\"Glasses today\" defaultValue={6} max={12} />\n <Stepper variant=\"compact\" accent={colors.warning} label=\"Espresso shots\" defaultValue={2} max={4} />\n <Stepper variant=\"cart\" accent={colors.danger} addLabel=\"Add\" label=\"Hot sauce\" description=\"₹30 each\" />\n </Card>\n );\n}\n"
182
182
  }
183
183
  ]
184
184
  }
@@ -113,7 +113,7 @@
113
113
  {
114
114
  "name": "Profile",
115
115
  "title": "Profile",
116
- "code": "import { Pressable, Text, View } from 'react-native';\nimport { Tabs } from '@/components/ui/tabs';\nimport { alpha, useKit } from '@/lib/kit';\n\ntype Post = [who: string, text: string, meta: string];\n\nfunction Feed({ posts }: { posts: Post[] }) {\n const { colors, radius, dark } = useKit();\n return (\n <View style={{ gap: 14 }}>\n {posts.map(([who, text, meta]) => (\n <View key={text} style={{ flexDirection: 'row', gap: 10 }}>\n <View\n style={{\n width: 34,\n height: 34,\n borderRadius: radius.capsule(34),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{who.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1, gap: 2 }}>\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>\n <Text style={{ color: colors.foreground }}>{who}</Text> · {meta}\n </Text>\n <Text style={{ fontSize: 14.5, fontWeight: '400', lineHeight: 20, color: colors.foreground }}>{text}</Text>\n </View>\n </View>\n ))}\n </View>\n );\n}\n\nfunction Grid() {\n const { colors, radius, dark } = useKit();\n const tones = [0.1, 0.18, 0.26, 0.14, 0.22, 0.3];\n return (\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 6 }}>\n {tones.map((a, i) => (\n <View\n key={i}\n style={{ width: '32%', aspectRatio: 1, borderRadius: radius.scale(10), backgroundColor: alpha(colors.accent, dark ? a + 0.08 : a) }}\n />\n ))}\n </View>\n );\n}\n\nexport default function Profile() {\n return (\n <Tabs\n items={[\n {\n value: 'posts',\n label: 'Posts',\n badge: 128,\n content: (\n <Feed\n posts={[\n ['Maya Chen', 'Shipped the new onboarding today. Three screens instead of seven, and it finally feels calm.', '2h'],\n ['Maya Chen', 'Hot take: every tab bar needs a haptic tick.', '1d'],\n ]}\n />\n ),\n },\n {\n value: 'replies',\n label: 'Replies',\n content: <Feed posts={[['Maya Chen', 'Agreed, the spring on that indicator is perfect. Ship it.', '5h']]} />,\n },\n { value: 'media', label: 'Media', badge: 24, content: <Grid /> },\n {\n value: 'likes',\n label: 'Likes',\n content: (\n <Feed\n posts={[\n ['Leo Park', 'Offsite is booked: June 12–14 in Lisbon. Bring sunscreen.', '3h'],\n ['Ana Ruiz', 'Pricing page v3 is live. Conversion up 0.6 points so far.', '1d'],\n ]}\n />\n ),\n },\n ]}\n />\n );\n}\n"
116
+ "code": "import { Pressable, Text, View } from 'react-native';\nimport { Tabs } from '@/components/ui/tabs';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\ntype Post = [who: string, text: string, meta: string];\n\nfunction Feed({ posts }: { posts: Post[] }) {\n const { colors, radius, dark } = useKit();\n return (\n <View style={{ gap: 14 }}>\n {posts.map(([who, text, meta]) => (\n <View key={text} style={{ flexDirection: 'row', gap: 10 }}>\n <View\n style={{\n width: 34,\n height: 34,\n borderRadius: radius.capsule(34),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{who.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1, gap: 2 }}>\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>\n <Text style={{ color: colors.foreground }}>{who}</Text> · {meta}\n </Text>\n <Text style={{ fontSize: 14.5, fontWeight: '400', lineHeight: 20, color: colors.foreground }}>{text}</Text>\n </View>\n </View>\n ))}\n </View>\n );\n}\n\nfunction Grid() {\n const { colors, radius, dark } = useKit();\n const tones = [0.1, 0.18, 0.26, 0.14, 0.22, 0.3];\n return (\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 6 }}>\n {tones.map((a, i) => (\n <View\n key={i}\n style={{ width: '32%', aspectRatio: 1, borderRadius: radius.scale(10), backgroundColor: alpha(colors.accent, dark ? a + 0.08 : a) }}\n />\n ))}\n </View>\n );\n}\n\nexport default function Profile() {\n return (\n <Tabs\n items={[\n {\n value: 'posts',\n label: 'Posts',\n badge: 128,\n content: (\n <Feed\n posts={[\n ['Maya Chen', 'Shipped the new onboarding today. Three screens instead of seven, and it finally feels calm.', '2h'],\n ['Maya Chen', 'Hot take: every tab bar needs a haptic tick.', '1d'],\n ]}\n />\n ),\n },\n {\n value: 'replies',\n label: 'Replies',\n content: <Feed posts={[['Maya Chen', 'Agreed, the spring on that indicator is perfect. Ship it.', '5h']]} />,\n },\n { value: 'media', label: 'Media', badge: 24, content: <Grid /> },\n {\n value: 'likes',\n label: 'Likes',\n content: (\n <Feed\n posts={[\n ['Leo Park', 'Offsite is booked: June 12–14 in Lisbon. Bring sunscreen.', '3h'],\n ['Ana Ruiz', 'Pricing page v3 is live. Conversion up 0.6 points so far.', '1d'],\n ]}\n />\n ),\n },\n ]}\n />\n );\n}\n"
117
117
  },
118
118
  {
119
119
  "name": "Scrollable",
@@ -123,7 +123,7 @@
123
123
  {
124
124
  "name": "WithIcons",
125
125
  "title": "With Icons",
126
- "code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { BottomNavIcon } from '@/components/ui/bottom-nav';\nimport { Tabs } from '@/components/ui/tabs';\nimport { alpha, useKit } from '@/lib/kit';\n\nfunction Panel({ title, body }: { title: string; body: string }) {\n const { colors } = useKit();\n return (\n <View style={{ gap: 4 }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground }}>{title}</Text>\n <Text style={{ fontSize: 14, fontWeight: '400', lineHeight: 20, color: colors.mutedForeground }}>{body}</Text>\n </View>\n );\n}\n\ntype Post = [who: string, text: string, meta: string];\n\nfunction Feed({ posts }: { posts: Post[] }) {\n const { colors, radius, dark } = useKit();\n return (\n <View style={{ gap: 14 }}>\n {posts.map(([who, text, meta]) => (\n <View key={text} style={{ flexDirection: 'row', gap: 10 }}>\n <View\n style={{\n width: 34,\n height: 34,\n borderRadius: radius.capsule(34),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{who.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1, gap: 2 }}>\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>\n <Text style={{ color: colors.foreground }}>{who}</Text> · {meta}\n </Text>\n <Text style={{ fontSize: 14.5, fontWeight: '400', lineHeight: 20, color: colors.foreground }}>{text}</Text>\n </View>\n </View>\n ))}\n </View>\n );\n}\n\nexport default function WithIcons() {\n const { colors } = useKit();\n const [unread, setUnread] = useState(4);\n const [mentions, setMentions] = useState(2);\n const clear = (\n <Pressable\n accessibilityRole=\"button\"\n onPress={() => {\n setUnread(0);\n setMentions(0);\n }}\n hitSlop={10}\n style={{ alignSelf: 'flex-start', marginTop: 12 }}\n >\n <Text style={{ fontSize: 14, fontWeight: '400', color: colors.accent }}>Mark all as read</Text>\n </Pressable>\n );\n return (\n <Tabs\n size=\"sm\"\n items={[\n {\n value: 'inbox',\n label: 'Inbox',\n icon: ({ active, color }) => <BottomNavIcon name=\"inbox\" color={color} size={17} active={active} />,\n badge: unread || undefined,\n content: (\n <View>\n <Feed posts={[['Priya Nair', 'Can you review the pricing page before 3pm?', '9:41']]} />\n {clear}\n </View>\n ),\n },\n {\n value: 'mentions',\n label: 'Mentions',\n icon: ({ active, color }) => <BottomNavIcon name=\"message\" color={color} size={17} active={active} />,\n badge: mentions || undefined,\n content: <Feed posts={[['Leo Park', '@karan the offsite deck is ready for a look.', '9:12']]} />,\n },\n { value: 'activity', label: 'Activity', icon: ({ active, color }) => <BottomNavIcon name=\"activity\" color={color} size={17} active={active} />, content: <Panel title=\"All quiet\" body=\"Nothing new since you last checked.\" /> },\n ]}\n />\n );\n}\n"
126
+ "code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { BottomNavIcon } from '@/components/ui/bottom-nav';\nimport { Tabs } from '@/components/ui/tabs';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\nfunction Panel({ title, body }: { title: string; body: string }) {\n const { colors } = useKit();\n return (\n <View style={{ gap: 4 }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground }}>{title}</Text>\n <Text style={{ fontSize: 14, fontWeight: '400', lineHeight: 20, color: colors.mutedForeground }}>{body}</Text>\n </View>\n );\n}\n\ntype Post = [who: string, text: string, meta: string];\n\nfunction Feed({ posts }: { posts: Post[] }) {\n const { colors, radius, dark } = useKit();\n return (\n <View style={{ gap: 14 }}>\n {posts.map(([who, text, meta]) => (\n <View key={text} style={{ flexDirection: 'row', gap: 10 }}>\n <View\n style={{\n width: 34,\n height: 34,\n borderRadius: radius.capsule(34),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{who.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1, gap: 2 }}>\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>\n <Text style={{ color: colors.foreground }}>{who}</Text> · {meta}\n </Text>\n <Text style={{ fontSize: 14.5, fontWeight: '400', lineHeight: 20, color: colors.foreground }}>{text}</Text>\n </View>\n </View>\n ))}\n </View>\n );\n}\n\nexport default function WithIcons() {\n const { colors } = useKit();\n const [unread, setUnread] = useState(4);\n const [mentions, setMentions] = useState(2);\n const clear = (\n <Pressable\n accessibilityRole=\"button\"\n onPress={() => {\n setUnread(0);\n setMentions(0);\n }}\n hitSlop={10}\n style={{ alignSelf: 'flex-start', marginTop: 12 }}\n >\n <Text style={{ fontSize: 14, fontWeight: '400', color: colors.accent }}>Mark all as read</Text>\n </Pressable>\n );\n return (\n <Tabs\n size=\"sm\"\n items={[\n {\n value: 'inbox',\n label: 'Inbox',\n icon: ({ active, color }) => <BottomNavIcon name=\"inbox\" color={color} size={17} active={active} />,\n badge: unread || undefined,\n content: (\n <View>\n <Feed posts={[['Priya Nair', 'Can you review the pricing page before 3pm?', '9:41']]} />\n {clear}\n </View>\n ),\n },\n {\n value: 'mentions',\n label: 'Mentions',\n icon: ({ active, color }) => <BottomNavIcon name=\"message\" color={color} size={17} active={active} />,\n badge: mentions || undefined,\n content: <Feed posts={[['Leo Park', '@karan the offsite deck is ready for a look.', '9:12']]} />,\n },\n { value: 'activity', label: 'Activity', icon: ({ active, color }) => <BottomNavIcon name=\"activity\" color={color} size={17} active={active} />, content: <Panel title=\"All quiet\" body=\"Nothing new since you last checked.\" /> },\n ]}\n />\n );\n}\n"
127
127
  }
128
128
  ]
129
129
  }
@@ -110,7 +110,7 @@
110
110
  {
111
111
  "name": "Reveal",
112
112
  "title": "Reveal",
113
- "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { AnimatedText } from '@/components/ui/text-animation';\nimport { useKit } from '@/lib/kit';\n\nfunction Chip({ label, onPress }: { label: string; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n accessibilityRole=\"button\"\n onPress={onPress}\n hitSlop={4}\n style={{ height: 36, paddingHorizontal: 14, borderRadius: radius.capsule(36), justifyContent: 'center', alignSelf: 'flex-start', backgroundColor: colors.muted }}\n >\n <Text style={{ color: colors.foreground, fontSize: 14 }}>{label}</Text>\n </Pressable>\n );\n}\n\nexport default function Reveal() {\n const [run, setRun] = useState(0);\n return (\n <View style={{ gap: 16 }}>\n <AnimatedText replayKey={run} textStyle={{ fontSize: 30, fontWeight: '600', letterSpacing: -0.8, lineHeight: 36 }}>\n Ship native apps that feel alive.\n </AnimatedText>\n <Chip label=\"Replay\" onPress={() => setRun((r) => r + 1)} />\n </View>\n );\n}\n"
113
+ "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { AnimatedText } from '@/components/ui/text-animation';\nimport { useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nfunction Chip({ label, onPress }: { label: string; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n accessibilityRole=\"button\"\n onPress={onPress}\n hitSlop={4}\n style={{ minHeight: 36, paddingHorizontal: 14, borderRadius: radius.capsule(36), justifyContent: 'center', alignSelf: 'flex-start', backgroundColor: colors.muted }}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 14 }}>{label}</Text>\n </Pressable>\n );\n}\n\nexport default function Reveal() {\n const [run, setRun] = useState(0);\n return (\n <View style={{ gap: 16 }}>\n <AnimatedText replayKey={run} textStyle={{ fontSize: 30, fontWeight: '600', letterSpacing: -0.8, lineHeight: 36 }}>\n Ship native apps that feel alive.\n </AnimatedText>\n <Chip label=\"Replay\" onPress={() => setRun((r) => r + 1)} />\n </View>\n );\n}\n"
114
114
  },
115
115
  {
116
116
  "name": "Thinking",
@@ -120,17 +120,17 @@
120
120
  {
121
121
  "name": "DeployStatus",
122
122
  "title": "Deploy Status",
123
- "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { AnimatedText } from '@/components/ui/text-animation';\nimport { useKit } from '@/lib/kit';\n\nfunction Chip({ label, onPress }: { label: string; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n accessibilityRole=\"button\"\n onPress={onPress}\n hitSlop={4}\n style={{ height: 36, paddingHorizontal: 14, borderRadius: radius.capsule(36), justifyContent: 'center', alignSelf: 'flex-start', backgroundColor: colors.muted }}\n >\n <Text style={{ color: colors.foreground, fontSize: 14 }}>{label}</Text>\n </Pressable>\n );\n}\n\nconst STATUSES = ['Queued', 'Building', 'Deploying', 'Live'];\n\nexport default function DeployStatus() {\n const { colors, radius } = useKit();\n const [i, setI] = useState(0);\n const status = STATUSES[i];\n const live = status === 'Live';\n return (\n <View style={{ gap: 16 }}>\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>\n <View style={{ width: 10, height: 10, borderRadius: radius.capsule(10), backgroundColor: live ? colors.success : colors.warning }} />\n <Text style={{ fontSize: 17, color: colors.mutedForeground }}>expokit.dev</Text>\n <AnimatedText variant=\"cascade\" color={live ? colors.success : colors.foreground} textStyle={{ fontSize: 17, fontWeight: '600' }}>\n {status}\n </AnimatedText>\n </View>\n <Chip label=\"Next status\" onPress={() => setI((n) => (n + 1) % STATUSES.length)} />\n </View>\n );\n}\n"
123
+ "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { AnimatedText } from '@/components/ui/text-animation';\nimport { useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nfunction Chip({ label, onPress }: { label: string; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n accessibilityRole=\"button\"\n onPress={onPress}\n hitSlop={4}\n style={{ minHeight: 36, paddingHorizontal: 14, borderRadius: radius.capsule(36), justifyContent: 'center', alignSelf: 'flex-start', backgroundColor: colors.muted }}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 14 }}>{label}</Text>\n </Pressable>\n );\n}\n\nconst STATUSES = ['Queued', 'Building', 'Deploying', 'Live'];\n\nexport default function DeployStatus() {\n const { colors, radius } = useKit();\n const [i, setI] = useState(0);\n const status = STATUSES[i];\n const live = status === 'Live';\n return (\n <View style={{ gap: 16 }}>\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', alignItems: 'center', gap: 10 }}>\n <View style={{ width: 10, height: 10, borderRadius: radius.capsule(10), backgroundColor: live ? colors.success : colors.warning }} />\n <Text style={{ flexShrink: 1, fontSize: 17, color: colors.mutedForeground }}>expokit.dev</Text>\n <AnimatedText variant=\"cascade\" color={live ? colors.success : colors.foreground} textStyle={{ fontSize: 17, fontWeight: '600' }}>\n {status}\n </AnimatedText>\n </View>\n <Chip label=\"Next status\" onPress={() => setI((n) => (n + 1) % STATUSES.length)} />\n </View>\n );\n}\n"
124
124
  },
125
125
  {
126
126
  "name": "Scramble",
127
127
  "title": "Scramble",
128
- "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { AnimatedText } from '@/components/ui/text-animation';\nimport { useKit } from '@/lib/kit';\n\nfunction Chip({ label, onPress }: { label: string; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n accessibilityRole=\"button\"\n onPress={onPress}\n hitSlop={4}\n style={{ height: 36, paddingHorizontal: 14, borderRadius: radius.capsule(36), justifyContent: 'center', alignSelf: 'flex-start', backgroundColor: colors.muted }}\n >\n <Text style={{ color: colors.foreground, fontSize: 14 }}>{label}</Text>\n </Pressable>\n );\n}\n\nexport default function Scramble() {\n const { colors, font } = useKit();\n const [run, setRun] = useState(0);\n return (\n <View style={{ gap: 16 }}>\n <View style={{ gap: 4 }}>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>Your referral code</Text>\n <AnimatedText variant=\"scramble\" replayKey={run} textStyle={{ fontSize: 28, fontWeight: '600', letterSpacing: 2, fontFamily: font.mono }}>\n SPRING-7Q2X\n </AnimatedText>\n </View>\n <Chip label=\"Generate again\" onPress={() => setRun((r) => r + 1)} />\n </View>\n );\n}\n"
128
+ "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { AnimatedText } from '@/components/ui/text-animation';\nimport { useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nfunction Chip({ label, onPress }: { label: string; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n accessibilityRole=\"button\"\n onPress={onPress}\n hitSlop={4}\n style={{ minHeight: 36, paddingHorizontal: 14, borderRadius: radius.capsule(36), justifyContent: 'center', alignSelf: 'flex-start', backgroundColor: colors.muted }}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 14 }}>{label}</Text>\n </Pressable>\n );\n}\n\nexport default function Scramble() {\n const { colors, font } = useKit();\n const [run, setRun] = useState(0);\n return (\n <View style={{ gap: 16 }}>\n <View style={{ gap: 4 }}>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>Your referral code</Text>\n <AnimatedText variant=\"scramble\" replayKey={run} textStyle={{ fontSize: 28, fontWeight: '600', letterSpacing: 2, fontFamily: font.mono }}>\n SPRING-7Q2X\n </AnimatedText>\n </View>\n <Chip label=\"Generate again\" onPress={() => setRun((r) => r + 1)} />\n </View>\n );\n}\n"
129
129
  },
130
130
  {
131
131
  "name": "Streaming",
132
132
  "title": "Streaming",
133
- "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { AnimatedText } from '@/components/ui/text-animation';\nimport { useKit } from '@/lib/kit';\n\nfunction Chip({ label, onPress }: { label: string; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n accessibilityRole=\"button\"\n onPress={onPress}\n hitSlop={4}\n style={{ height: 36, paddingHorizontal: 14, borderRadius: radius.capsule(36), justifyContent: 'center', alignSelf: 'flex-start', backgroundColor: colors.muted }}\n >\n <Text style={{ color: colors.foreground, fontSize: 14 }}>{label}</Text>\n </Pressable>\n );\n}\n\nconst ANSWER =\n 'Spring animations feel natural because they keep momentum: a flick carries its speed into the motion, and nothing stops dead.';\n\nexport default function Streaming() {\n const { colors, radius } = useKit();\n const [text, setText] = useState('');\n const [run, setRun] = useState(0);\n useEffect(() => {\n // Simulates tokens arriving from a model, a few words at a time.\n const words = ANSWER.split(' ');\n let n = 0;\n const id = setInterval(() => {\n n = Math.min(words.length, n + 1 + Math.floor(Math.random() * 3));\n setText(words.slice(0, n).join(' '));\n if (n >= words.length) clearInterval(id);\n }, 260);\n return () => clearInterval(id);\n }, [run]);\n return (\n <View style={{ gap: 16 }}>\n <View style={{ padding: 14, borderRadius: radius.lg, backgroundColor: colors.muted }}>\n <AnimatedText key={run} variant=\"typewriter\" textStyle={{ fontSize: 16, lineHeight: 23 }}>\n {text}\n </AnimatedText>\n </View>\n <Chip\n label=\"Ask again\"\n onPress={() => {\n setText('');\n setRun((r) => r + 1);\n }}\n />\n </View>\n );\n}\n"
133
+ "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { AnimatedText } from '@/components/ui/text-animation';\nimport { useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nfunction Chip({ label, onPress }: { label: string; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n accessibilityRole=\"button\"\n onPress={onPress}\n hitSlop={4}\n style={{ minHeight: 36, paddingHorizontal: 14, borderRadius: radius.capsule(36), justifyContent: 'center', alignSelf: 'flex-start', backgroundColor: colors.muted }}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 14 }}>{label}</Text>\n </Pressable>\n );\n}\n\nconst ANSWER =\n 'Spring animations feel natural because they keep momentum: a flick carries its speed into the motion, and nothing stops dead.';\n\nexport default function Streaming() {\n const { colors, radius } = useKit();\n const [text, setText] = useState('');\n const [run, setRun] = useState(0);\n useEffect(() => {\n // Simulates tokens arriving from a model, a few words at a time.\n const words = ANSWER.split(' ');\n let n = 0;\n const id = setInterval(() => {\n n = Math.min(words.length, n + 1 + Math.floor(Math.random() * 3));\n setText(words.slice(0, n).join(' '));\n if (n >= words.length) clearInterval(id);\n }, 260);\n return () => clearInterval(id);\n }, [run]);\n return (\n <View style={{ gap: 16 }}>\n <View style={{ padding: 14, borderRadius: radius.lg, backgroundColor: colors.muted }}>\n <AnimatedText key={run} variant=\"typewriter\" textStyle={{ fontSize: 16, lineHeight: 23 }}>\n {text}\n </AnimatedText>\n </View>\n <Chip\n label=\"Ask again\"\n onPress={() => {\n setText('');\n setRun((r) => r + 1);\n }}\n />\n </View>\n );\n}\n"
134
134
  },
135
135
  {
136
136
  "name": "Hero",
@@ -160,17 +160,17 @@
160
160
  {
161
161
  "name": "Composer",
162
162
  "title": "Composer",
163
- "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { Textarea } from '@/components/ui/textarea';\nimport { useKit } from '@/lib/kit';\n\nfunction ToolButton({ glyph, label }: { glyph: string; label: string }) {\n const { colors } = useKit();\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={label}\n hitSlop={6}\n style={{ width: 32, height: 32, borderRadius: 16, alignItems: 'center', justifyContent: 'center', backgroundColor: colors.muted }}\n >\n <Text style={{ fontSize: 17, fontWeight: '400', color: colors.mutedForeground }}>{glyph}</Text>\n </Pressable>\n );\n}\n\nexport default function Composer() {\n const { colors, radius } = useKit();\n const [draft, setDraft] = useState('');\n const [sent, setSent] = useState<string[]>(['Are we still on for the design review at 3?']);\n const canSend = draft.trim().length > 0;\n const send = () => {\n if (!canSend) return;\n setSent((s) => [...s.slice(-1), draft.trim()]);\n setDraft('');\n };\n return (\n <View style={{ gap: 10 }}>\n {sent.map((m, i) => (\n <View\n key={i}\n style={{ alignSelf: 'flex-end', maxWidth: '80%', backgroundColor: colors.accent, borderRadius: radius.scale(18), paddingHorizontal: 14, paddingVertical: 9 }}\n >\n <Text style={{ color: colors.accentForeground, fontSize: 15 }}>{m}</Text>\n </View>\n ))}\n <Textarea\n placeholder=\"Message Alex…\"\n value={draft}\n onChangeText={setDraft}\n minRows={1}\n maxRows={5}\n toolbar={\n <>\n <ToolButton glyph=\"+\" label=\"Attach a file\" />\n <ToolButton glyph=\"@\" label=\"Mention someone\" />\n <View style={{ flex: 1 }} />\n <Pressable\n onPress={send}\n disabled={!canSend}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Send message\"\n hitSlop={6}\n style={{\n height: 32,\n paddingHorizontal: 14,\n borderRadius: radius.capsule(32),\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: colors.accent,\n opacity: canSend ? 1 : 0.4,\n }}\n >\n <Text style={{ color: colors.accentForeground, fontSize: 14, fontWeight: '400' }}>Send ↑</Text>\n </Pressable>\n </>\n }\n />\n </View>\n );\n}\n"
163
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { Textarea } from '@/components/ui/textarea';\nimport { useKit } from '@/lib/kit';\n\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\nfunction ToolButton({ glyph, label }: { glyph: string; label: string }) {\n const { colors } = useKit();\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={label}\n hitSlop={6}\n style={{ width: 32, minHeight: 32, borderRadius: 16, alignItems: 'center', justifyContent: 'center', backgroundColor: colors.muted }}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 17, fontWeight: '400', color: colors.mutedForeground }}>{glyph}</Text>\n </Pressable>\n );\n}\n\nexport default function Composer() {\n const { colors, radius } = useKit();\n const [draft, setDraft] = useState('');\n const [sent, setSent] = useState<string[]>(['Are we still on for the design review at 3?']);\n const canSend = draft.trim().length > 0;\n const send = () => {\n if (!canSend) return;\n setSent((s) => [...s.slice(-1), draft.trim()]);\n setDraft('');\n };\n return (\n <View style={{ gap: 10 }}>\n {sent.map((m, i) => (\n <View\n key={i}\n style={{ alignSelf: 'flex-end', maxWidth: '80%', backgroundColor: colors.accent, borderRadius: radius.scale(18), paddingHorizontal: 14, paddingVertical: 9 }}\n >\n <Text style={{ color: colors.accentForeground, fontSize: 15 }}>{m}</Text>\n </View>\n ))}\n <Textarea\n placeholder=\"Message Alex…\"\n value={draft}\n onChangeText={setDraft}\n minRows={1}\n maxRows={5}\n toolbar={\n <>\n <ToolButton glyph=\"+\" label=\"Attach a file\" />\n <ToolButton glyph=\"@\" label=\"Mention someone\" />\n <View style={{ flex: 1 }} />\n <Pressable\n onPress={send}\n disabled={!canSend}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Send message\"\n hitSlop={6}\n style={{\n minHeight: 32,\n paddingHorizontal: 14,\n borderRadius: radius.capsule(32),\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: colors.accent,\n opacity: canSend ? 1 : 0.4,\n }}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.accentForeground, fontSize: 14, fontWeight: '400' }}>Send ↑</Text>\n </Pressable>\n </>\n }\n />\n </View>\n );\n}\n"
164
164
  },
165
165
  {
166
166
  "name": "Review",
167
167
  "title": "Review",
168
- "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { Textarea } from '@/components/ui/textarea';\nimport { useKit } from '@/lib/kit';\n\nfunction Card({ title, subtitle, children }: { title: string; subtitle?: string; children: React.ReactNode }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={{\n padding: 16,\n gap: 12,\n borderRadius: radius.xl,\n borderWidth: StyleSheet.hairlineWidth,\n borderColor: colors.border,\n backgroundColor: colors.card,\n }}\n >\n <View style={{ gap: 2, marginBottom: 2 }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground }}>{title}</Text>\n {subtitle ? <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{subtitle}</Text> : null}\n </View>\n {children}\n </View>\n );\n}\n\nexport default function Review() {\n const [review, setReview] = useState('Great coffee, but');\n const length = review.trim().length;\n return (\n <Card title=\"Rate Blue Bottle, Hayes Valley\" subtitle=\"Reviews are public and show your first name.\">\n <Textarea\n label=\"Your review\"\n value={review}\n onChangeText={setReview}\n maxLength={200}\n enforceMaxLength={false}\n minRows={3}\n error={length > 0 && length < 20 ? 'Tell us a little more — at least 20 characters' : length > 200 ? 'Keep it under 200 characters' : undefined}\n success={length >= 20 && length <= 200 ? 'Thanks — that will really help others' : undefined}\n />\n </Card>\n );\n}\n"
168
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { Textarea } from '@/components/ui/textarea';\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\nfunction Card({ title, subtitle, screen, children }: { title: string; subtitle?: string; screen?: boolean; children: React.ReactNode }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={\n screen\n ? { flex: 1, padding: 16, gap: 12, backgroundColor: colors.background }\n : {\n padding: 16,\n gap: 12,\n borderRadius: radius.xl,\n borderWidth: StyleSheet.hairlineWidth,\n borderColor: colors.border,\n backgroundColor: colors.card,\n }\n }\n >\n <View style={{ gap: 2, marginBottom: 2 }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground }}>{title}</Text>\n {subtitle ? <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{subtitle}</Text> : null}\n </View>\n {children}\n </View>\n );\n}\n\nexport default function Review({ screen }: DemoProps) {\n const [review, setReview] = useState('Great coffee, but');\n const length = review.trim().length;\n return (\n <Card screen={screen} title=\"Rate Blue Bottle, Hayes Valley\" subtitle=\"Reviews are public and show your first name.\">\n <Textarea\n label=\"Your review\"\n value={review}\n onChangeText={setReview}\n maxLength={200}\n enforceMaxLength={false}\n minRows={3}\n error={length > 0 && length < 20 ? 'Tell us a little more — at least 20 characters' : length > 200 ? 'Keep it under 200 characters' : undefined}\n success={length >= 20 && length <= 200 ? 'Thanks — that will really help others' : undefined}\n />\n </Card>\n );\n}\n"
169
169
  },
170
170
  {
171
171
  "name": "Feedback",
172
172
  "title": "Feedback",
173
- "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { Textarea } from '@/components/ui/textarea';\nimport { useKit } from '@/lib/kit';\n\nfunction Card({ title, subtitle, children }: { title: string; subtitle?: string; children: React.ReactNode }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={{\n padding: 16,\n gap: 12,\n borderRadius: radius.xl,\n borderWidth: StyleSheet.hairlineWidth,\n borderColor: colors.border,\n backgroundColor: colors.card,\n }}\n >\n <View style={{ gap: 2, marginBottom: 2 }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground }}>{title}</Text>\n {subtitle ? <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{subtitle}</Text> : null}\n </View>\n {children}\n </View>\n );\n}\n\nexport default function Feedback() {\n return (\n <Card title=\"Send feedback\" subtitle=\"Goes straight to the product team.\">\n <Textarea\n variant=\"filled\"\n label=\"What could be better?\"\n defaultValue=\"Dark mode on the invoices page is a little too low-contrast for me.\"\n helper=\"Screenshots help — attach them in the next step.\"\n minRows={3}\n maxRows={6}\n />\n </Card>\n );\n}\n"
173
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { Textarea } from '@/components/ui/textarea';\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\nfunction Card({ title, subtitle, screen, children }: { title: string; subtitle?: string; screen?: boolean; children: React.ReactNode }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={\n screen\n ? { flex: 1, padding: 16, gap: 12, backgroundColor: colors.background }\n : {\n padding: 16,\n gap: 12,\n borderRadius: radius.xl,\n borderWidth: StyleSheet.hairlineWidth,\n borderColor: colors.border,\n backgroundColor: colors.card,\n }\n }\n >\n <View style={{ gap: 2, marginBottom: 2 }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground }}>{title}</Text>\n {subtitle ? <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{subtitle}</Text> : null}\n </View>\n {children}\n </View>\n );\n}\n\nexport default function Feedback({ screen }: DemoProps) {\n return (\n <Card screen={screen} title=\"Send feedback\" subtitle=\"Goes straight to the product team.\">\n <Textarea\n variant=\"filled\"\n label=\"What could be better?\"\n defaultValue=\"Dark mode on the invoices page is a little too low-contrast for me.\"\n helper=\"Screenshots help — attach them in the next step.\"\n minRows={3}\n maxRows={6}\n />\n </Card>\n );\n}\n"
174
174
  },
175
175
  {
176
176
  "name": "CharacterLimit",
@@ -2,7 +2,7 @@
2
2
  "name": "ticket-shape",
3
3
  "category": null,
4
4
  "aliases": [],
5
- "exportName": "TicketStamp",
5
+ "exportName": "TicketShape",
6
6
  "files": [
7
7
  "src/lib/ticket-shape.tsx"
8
8
  ],
@@ -136,22 +136,22 @@
136
136
  {
137
137
  "name": "TravelCard",
138
138
  "title": "Travel Card",
139
- "code": "import React, { useState } from 'react';\nimport { Image, Pressable, Text, View } from 'react-native';\nimport { TiltCard, TiltLayer } from '@/components/ui/tilt-card';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Any photo works (`require()` or a URL). `blur` gives soft, abstract art. */\nconst photo = (id: number, w: number, h: number, blur = false) => ({ uri: `https://picsum.photos/id/${id}/${w}/${h}${blur ? '?blur=10' : ''}` });\n\n/** A screen title, as it sits above the card in your app. */\nfunction Header({ title, sub, right }: { title: string; sub?: string; right?: React.ReactNode }) {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 22, fontWeight: '700', letterSpacing: -0.4 }}>{title}</Text>\n {sub ? <Text style={{ color: colors.mutedForeground, fontSize: 13, marginTop: 2 }}>{sub}</Text> : null}\n </View>\n {right}\n </View>\n );\n}\n\nconst glass = {\n paddingHorizontal: 10,\n paddingVertical: 5,\n borderRadius: 999,\n backgroundColor: 'rgba(255,255,255,0.18)',\n borderWidth: 1,\n borderColor: 'rgba(255,255,255,0.32)',\n} as const;\n\nexport default function TravelCard() {\n const { colors } = useKit();\n const [tab, setTab] = useState('Coast');\n return (\n <View style={{ gap: 18 }}>\n <Header title=\"Explore\" sub=\"Stays our guests loved this month\" />\n <View style={{ flexDirection: 'row', gap: 8 }}>\n {['Coast', 'Mountains', 'Cities', 'Lakes'].map((t) => {\n const on = t === tab;\n return (\n <Pressable\n key={t}\n onPress={() => setTab(t)}\n accessibilityRole=\"tab\"\n accessibilityState={{ selected: on }}\n style={{ paddingHorizontal: 14, paddingVertical: 8, borderRadius: 999, backgroundColor: on ? colors.foreground : colors.muted }}\n >\n <Text style={{ color: on ? colors.background : colors.foreground, fontSize: 13, fontWeight: '600' }}>{t}</Text>\n </Pressable>\n );\n })}\n </View>\n <TiltCard idle image={photo(397, 540, 720)} radius={26} accessibilityLabel=\"Manarola, Cinque Terre\" style={{ width: 270, alignSelf: 'center' }}>\n <View style={{ height: 360 }} />\n <TiltLayer depth={1.2}>\n <View style={[glass, { position: 'absolute', top: 16, right: 16 }]}>\n <Text style={{ color: '#fff', fontSize: 12, fontWeight: '700' }}>★ 4.9</Text>\n </View>\n </TiltLayer>\n <TiltLayer depth={0.8}>\n <View style={{ position: 'absolute', left: 20, right: 20, bottom: 20, gap: 2 }}>\n <Text style={{ color: 'rgba(255,255,255,0.8)', fontSize: 12, fontWeight: '600', letterSpacing: 1 }}>CINQUE TERRE · ITALY</Text>\n <Text style={{ color: '#fff', fontSize: 30, fontWeight: '800', letterSpacing: -0.8 }}>Manarola</Text>\n <Text style={{ color: 'rgba(255,255,255,0.8)', fontSize: 13 }}>3 nights · from €140</Text>\n </View>\n </TiltLayer>\n </TiltCard>\n </View>\n );\n}\n"
139
+ "code": "import React, { useState } from 'react';\nimport { Image, Pressable, Text, View } from 'react-native';\nimport { TiltCard, TiltLayer } from '@/components/ui/tilt-card';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Demo control text (buttons, chips, labels on fixed shapes) stops at iOS's largest standard text size. */\nconst FONT_CAP = 1.35;\n\n/** Any photo works (`require()` or a URL). */\nconst photo = (id: number, w: number, h: number) => ({ uri: `https://picsum.photos/id/${id}/${w}/${h}` });\n\n/** A screen title, as it sits above the card in your app. */\nfunction Header({ title, sub, right }: { title: string; sub?: string; right?: React.ReactNode }) {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 22, fontWeight: '700', letterSpacing: -0.4 }}>{title}</Text>\n {sub ? <Text style={{ color: colors.mutedForeground, fontSize: 13, marginTop: 2 }}>{sub}</Text> : null}\n </View>\n {right}\n </View>\n );\n}\n\nconst glass = {\n paddingHorizontal: 10,\n paddingVertical: 5,\n borderRadius: 999,\n backgroundColor: 'rgba(255,255,255,0.18)',\n borderWidth: 1,\n borderColor: 'rgba(255,255,255,0.32)',\n} as const;\n\nexport default function TravelCard() {\n const { colors } = useKit();\n const [tab, setTab] = useState('Coast');\n return (\n <View style={{ gap: 18 }}>\n <Header title=\"Explore\" sub=\"Stays our guests loved this month\" />\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>\n {['Coast', 'Mountains', 'Cities', 'Lakes'].map((t) => {\n const on = t === tab;\n return (\n <Pressable\n key={t}\n onPress={() => setTab(t)}\n accessibilityRole=\"tab\"\n accessibilityState={{ selected: on }}\n style={{ paddingHorizontal: 14, paddingVertical: 8, borderRadius: 999, backgroundColor: on ? colors.foreground : colors.muted }}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: on ? colors.background : colors.foreground, fontSize: 13, fontWeight: '600' }}>{t}</Text>\n </Pressable>\n );\n })}\n </View>\n <TiltCard idle image={photo(397, 540, 720)} radius={26} accessibilityLabel=\"Manarola, Cinque Terre\" style={{ width: 270, alignSelf: 'center' }}>\n <View style={{ height: 360 }} />\n <TiltLayer depth={1.2}>\n <View style={[glass, { position: 'absolute', top: 16, right: 16 }]}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: '#fff', fontSize: 12, fontWeight: '700' }}>★ 4.9</Text>\n </View>\n </TiltLayer>\n <TiltLayer depth={0.8}>\n <View style={{ position: 'absolute', left: 20, right: 20, bottom: 20, gap: 2 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: 'rgba(255,255,255,0.8)', fontSize: 12, fontWeight: '600', letterSpacing: 1 }}>CINQUE TERRE · ITALY</Text>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: '#fff', fontSize: 30, fontWeight: '800', letterSpacing: -0.8 }}>Manarola</Text>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: 'rgba(255,255,255,0.8)', fontSize: 13 }}>3 nights · from €140</Text>\n </View>\n </TiltLayer>\n </TiltCard>\n </View>\n );\n}\n"
140
140
  },
141
141
  {
142
142
  "name": "NowPlaying",
143
143
  "title": "Now Playing",
144
- "code": "import React, { useState } from 'react';\nimport { Image, Pressable, Text, View } from 'react-native';\nimport { TiltCard, TiltLayer } from '@/components/ui/tilt-card';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Any photo works (`require()` or a URL). `blur` gives soft, abstract art. */\nconst photo = (id: number, w: number, h: number, blur = false) => ({ uri: `https://picsum.photos/id/${id}/${w}/${h}${blur ? '?blur=10' : ''}` });\n\nexport default function NowPlaying() {\n const { colors } = useKit();\n const [playing, setPlaying] = useState(true);\n return (\n <View style={{ alignItems: 'center', gap: 18 }}>\n <View style={{ alignSelf: 'stretch', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>\n <Text style={{ color: colors.foreground, fontSize: 20 }}>⌄</Text>\n <View style={{ alignItems: 'center' }}>\n <Text style={{ color: colors.mutedForeground, fontSize: 11, fontWeight: '700', letterSpacing: 1 }}>PLAYING FROM</Text>\n <Text style={{ color: colors.foreground, fontSize: 13, fontWeight: '600' }}>Late night drive</Text>\n </View>\n <Text style={{ color: colors.foreground, fontSize: 20 }}>⋯</Text>\n </View>\n <TiltCard idle image={photo(888, 300, 300, true)} shade={0.35} radius={20} accessibilityLabel=\"Midnight Bloom by Lumen\" style={{ width: 250 }}>\n <View style={{ height: 250 }} />\n <TiltLayer depth={1}>\n <View style={{ position: 'absolute', left: 18, bottom: 16 }}>\n <Text style={{ color: '#fff', fontSize: 30, fontWeight: '900', letterSpacing: -1, lineHeight: 30 }}>MIDNIGHT</Text>\n <Text style={{ color: '#fff', fontSize: 30, fontWeight: '300', letterSpacing: -1, lineHeight: 32 }}>bloom</Text>\n </View>\n </TiltLayer>\n </TiltCard>\n <View style={{ alignSelf: 'stretch', gap: 10 }}>\n <View>\n <Text style={{ color: colors.foreground, fontSize: 18, fontWeight: '700' }}>Midnight Bloom</Text>\n <Text style={{ color: colors.mutedForeground, fontSize: 14 }}>Lumen</Text>\n </View>\n <View style={{ height: 4, borderRadius: 2, backgroundColor: alpha(colors.mutedForeground, 0.25) }}>\n <View style={{ width: '42%', height: 4, borderRadius: 2, backgroundColor: colors.foreground }} />\n </View>\n <View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: -4 }}>\n <Text style={{ color: colors.mutedForeground, fontSize: 11, fontVariant: ['tabular-nums'] }}>1:42</Text>\n <Text style={{ color: colors.mutedForeground, fontSize: 11, fontVariant: ['tabular-nums'] }}>−2:21</Text>\n </View>\n <View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center', gap: 34 }}>\n <Text style={{ color: colors.foreground, fontSize: 22 }}>⏮</Text>\n <Pressable\n onPress={() => setPlaying((p) => !p)}\n accessibilityRole=\"button\"\n accessibilityLabel={playing ? 'Pause' : 'Play'}\n style={{ width: 58, height: 58, borderRadius: 29, backgroundColor: colors.foreground, alignItems: 'center', justifyContent: 'center' }}\n >\n <Text style={{ color: colors.background, fontSize: 20 }}>{playing ? '❚❚' : '▶'}</Text>\n </Pressable>\n <Text style={{ color: colors.foreground, fontSize: 22 }}>⏭</Text>\n </View>\n </View>\n </View>\n );\n}\n"
144
+ "code": "import React, { useState } from 'react';\nimport { Image, Pressable, Text, View } from 'react-native';\nimport { GrainGradient } from '@/components/ui/grain';\nimport { TiltCard, TiltLayer } from '@/components/ui/tilt-card';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Demo control text (buttons, chips, labels on fixed shapes) stops at iOS's largest standard text size. */\nconst FONT_CAP = 1.35;\n\nexport default function NowPlaying() {\n const { colors } = useKit();\n const [playing, setPlaying] = useState(true);\n return (\n <View style={{ alignItems: 'center', gap: 18 }}>\n <View style={{ alignSelf: 'stretch', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 20 }}>⌄</Text>\n <View style={{ flexShrink: 1, alignItems: 'center' }}>\n <Text style={{ color: colors.mutedForeground, fontSize: 11, fontWeight: '700', letterSpacing: 1 }}>PLAYING FROM</Text>\n <Text style={{ color: colors.foreground, fontSize: 13, fontWeight: '600' }}>Late night drive</Text>\n </View>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 20 }}>⋯</Text>\n </View>\n <TiltCard idle radius={20} accessibilityLabel=\"Midnight Bloom by Lumen\" style={{ width: 250 }}>\n <GrainGradient palette=\"noir\" seed={1} style={{ height: 250 }} />\n <TiltLayer depth={1}>\n <View style={{ position: 'absolute', left: 18, bottom: 16 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: '#fff', fontSize: 30, fontWeight: '900', letterSpacing: -1, lineHeight: 30 }}>MIDNIGHT</Text>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: '#fff', fontSize: 30, fontWeight: '300', letterSpacing: -1, lineHeight: 32 }}>bloom</Text>\n </View>\n </TiltLayer>\n </TiltCard>\n <View style={{ alignSelf: 'stretch', gap: 10 }}>\n <View>\n <Text style={{ color: colors.foreground, fontSize: 18, fontWeight: '700' }}>Midnight Bloom</Text>\n <Text style={{ color: colors.mutedForeground, fontSize: 14 }}>Lumen</Text>\n </View>\n <View style={{ height: 4, borderRadius: 2, backgroundColor: alpha(colors.mutedForeground, 0.25) }}>\n <View style={{ width: '42%', height: 4, borderRadius: 2, backgroundColor: colors.foreground }} />\n </View>\n <View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: -4 }}>\n <Text style={{ color: colors.mutedForeground, fontSize: 11, fontVariant: ['tabular-nums'] }}>1:42</Text>\n <Text style={{ color: colors.mutedForeground, fontSize: 11, fontVariant: ['tabular-nums'] }}>−2:21</Text>\n </View>\n <View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center', gap: 34 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 22 }}>⏮</Text>\n <Pressable\n onPress={() => setPlaying((p) => !p)}\n accessibilityRole=\"button\"\n accessibilityLabel={playing ? 'Pause' : 'Play'}\n style={{ width: 58, height: 58, borderRadius: 29, backgroundColor: colors.foreground, alignItems: 'center', justifyContent: 'center' }}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.background, fontSize: 20 }}>{playing ? '❚❚' : '▶'}</Text>\n </Pressable>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 22 }}>⏭</Text>\n </View>\n </View>\n </View>\n );\n}\n"
145
145
  },
146
146
  {
147
147
  "name": "Product",
148
148
  "title": "Product",
149
- "code": "import React, { useState } from 'react';\nimport { Image, Pressable, Text, View } from 'react-native';\nimport { TiltCard, TiltLayer } from '@/components/ui/tilt-card';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Any photo works (`require()` or a URL). `blur` gives soft, abstract art. */\nconst photo = (id: number, w: number, h: number, blur = false) => ({ uri: `https://picsum.photos/id/${id}/${w}/${h}${blur ? '?blur=10' : ''}` });\n\n/** A screen title, as it sits above the card in your app. */\nfunction Header({ title, sub, right }: { title: string; sub?: string; right?: React.ReactNode }) {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 22, fontWeight: '700', letterSpacing: -0.4 }}>{title}</Text>\n {sub ? <Text style={{ color: colors.mutedForeground, fontSize: 13, marginTop: 2 }}>{sub}</Text> : null}\n </View>\n {right}\n </View>\n );\n}\n\nexport default function Product() {\n const { colors } = useKit();\n return (\n <View style={{ gap: 18 }}>\n <Header\n title=\"New in\"\n sub=\"Film cameras, serviced and tested\"\n right={\n <View style={{ paddingHorizontal: 12, paddingVertical: 7, borderRadius: 999, backgroundColor: colors.muted }}>\n <Text style={{ color: colors.foreground, fontSize: 13, fontWeight: '600' }}>Bag · 2</Text>\n </View>\n }\n />\n <TiltCard maxTilt={14} radius={26} accessibilityLabel=\"Leica M6 film camera\" style={{ width: 280, alignSelf: 'center' }}>\n <View style={{ height: 360, backgroundColor: '#efece6' }} />\n <TiltLayer depth={1.4}>\n <View style={{ position: 'absolute', top: 26, left: 22, right: 22, height: 190, borderRadius: 18, overflow: 'hidden' }}>\n <Image alt=\"\" source={photo(250, 560, 380)} style={{ flex: 1 }} />\n </View>\n </TiltLayer>\n <TiltLayer depth={0.4}>\n <View style={{ position: 'absolute', left: 24, right: 24, bottom: 22, gap: 12 }}>\n <View>\n <Text style={{ color: '#8a8378', fontSize: 11, fontWeight: '700', letterSpacing: 1.2 }}>FILM CAMERA</Text>\n <Text style={{ color: '#1b1915', fontSize: 22, fontWeight: '800', letterSpacing: -0.4 }}>Rangefinder 35</Text>\n </View>\n <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>\n <Text style={{ color: '#1b1915', fontSize: 20, fontWeight: '700' }}>$1,290</Text>\n <View style={{ paddingHorizontal: 16, paddingVertical: 10, borderRadius: 999, backgroundColor: '#1b1915' }}>\n <Text style={{ color: '#fff', fontSize: 13, fontWeight: '700' }}>Add to bag</Text>\n </View>\n </View>\n </View>\n </TiltLayer>\n </TiltCard>\n <Text style={{ color: colors.mutedForeground, fontSize: 13, textAlign: 'center' }}>Free shipping · 2-year warranty</Text>\n </View>\n );\n}\n"
149
+ "code": "import React, { useState } from 'react';\nimport { Image, Pressable, Text, View } from 'react-native';\nimport { TiltCard, TiltLayer } from '@/components/ui/tilt-card';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Demo control text (buttons, chips, labels on fixed shapes) stops at iOS's largest standard text size. */\nconst FONT_CAP = 1.35;\n\n/** Any photo works (`require()` or a URL). */\nconst photo = (id: number, w: number, h: number) => ({ uri: `https://picsum.photos/id/${id}/${w}/${h}` });\n\n/** A screen title, as it sits above the card in your app. */\nfunction Header({ title, sub, right }: { title: string; sub?: string; right?: React.ReactNode }) {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 22, fontWeight: '700', letterSpacing: -0.4 }}>{title}</Text>\n {sub ? <Text style={{ color: colors.mutedForeground, fontSize: 13, marginTop: 2 }}>{sub}</Text> : null}\n </View>\n {right}\n </View>\n );\n}\n\nexport default function Product() {\n const { colors } = useKit();\n return (\n <View style={{ gap: 18 }}>\n <Header\n title=\"New in\"\n sub=\"Film cameras, serviced and tested\"\n right={\n <View style={{ paddingHorizontal: 12, paddingVertical: 7, borderRadius: 999, backgroundColor: colors.muted }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 13, fontWeight: '600' }}>Bag · 2</Text>\n </View>\n }\n />\n <TiltCard maxTilt={14} radius={26} accessibilityLabel=\"Leica M6 film camera\" style={{ width: 280, alignSelf: 'center' }}>\n <View style={{ height: 360, backgroundColor: '#efece6' }} />\n <TiltLayer depth={1.4}>\n <View style={{ position: 'absolute', top: 26, left: 22, right: 22, height: 190, borderRadius: 18, overflow: 'hidden' }}>\n <Image alt=\"\" source={photo(250, 560, 380)} style={{ flex: 1 }} />\n </View>\n </TiltLayer>\n <TiltLayer depth={0.4}>\n <View style={{ position: 'absolute', left: 24, right: 24, bottom: 22, gap: 12 }}>\n <View>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: '#8a8378', fontSize: 11, fontWeight: '700', letterSpacing: 1.2 }}>FILM CAMERA</Text>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: '#1b1915', fontSize: 22, fontWeight: '800', letterSpacing: -0.4 }}>Rangefinder 35</Text>\n </View>\n <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: '#1b1915', fontSize: 20, fontWeight: '700' }}>$1,290</Text>\n <View style={{ paddingHorizontal: 16, paddingVertical: 10, borderRadius: 999, backgroundColor: '#1b1915' }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: '#fff', fontSize: 13, fontWeight: '700' }}>Add to bag</Text>\n </View>\n </View>\n </View>\n </TiltLayer>\n </TiltCard>\n <Text style={{ color: colors.mutedForeground, fontSize: 13, textAlign: 'center' }}>Free shipping · 2-year warranty</Text>\n </View>\n );\n}\n"
150
150
  },
151
151
  {
152
152
  "name": "FeedCard",
153
153
  "title": "Feed Card",
154
- "code": "import React, { useState } from 'react';\nimport { Image, Pressable, Text, View } from 'react-native';\nimport { TiltCard, TiltLayer } from '@/components/ui/tilt-card';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Any photo works (`require()` or a URL). `blur` gives soft, abstract art. */\nconst photo = (id: number, w: number, h: number, blur = false) => ({ uri: `https://picsum.photos/id/${id}/${w}/${h}${blur ? '?blur=10' : ''}` });\n\n/** A screen title, as it sits above the card in your app. */\nfunction Header({ title, sub, right }: { title: string; sub?: string; right?: React.ReactNode }) {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 22, fontWeight: '700', letterSpacing: -0.4 }}>{title}</Text>\n {sub ? <Text style={{ color: colors.mutedForeground, fontSize: 13, marginTop: 2 }}>{sub}</Text> : null}\n </View>\n {right}\n </View>\n );\n}\n\nexport default function FeedCard() {\n const { colors } = useKit();\n return (\n <View style={{ gap: 16 }}>\n <Header title=\"For you\" sub=\"Picked from places you saved\" />\n <TiltCard maxTilt={5} pressScale={1.015} glare=\"soft\" radius={20} onPress={() => {}} accessibilityLabel=\"Read: A fairytale weekend in Bavaria\" style={{ alignSelf: 'stretch' }}>\n <View style={{ backgroundColor: colors.card }}>\n <Image alt=\"\" source={photo(1040, 720, 360)} style={{ height: 170 }} />\n <View style={{ padding: 16, gap: 4 }}>\n <Text style={{ color: colors.mutedForeground, fontSize: 11, fontWeight: '700', letterSpacing: 1 }}>TRAVEL · 6 MIN READ</Text>\n <Text style={{ color: colors.foreground, fontSize: 18, fontWeight: '700', letterSpacing: -0.3 }}>A fairytale weekend in Bavaria</Text>\n <Text style={{ color: colors.mutedForeground, fontSize: 13, lineHeight: 19 }}>Castles, lakes and the best pretzels we had all year.</Text>\n </View>\n </View>\n </TiltCard>\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12, padding: 10, borderRadius: 16, backgroundColor: colors.card }}>\n <Image alt=\"\" source={photo(419, 160, 160)} style={{ width: 56, height: 56, borderRadius: 12, backgroundColor: colors.muted }} />\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.mutedForeground, fontSize: 11, fontWeight: '700', letterSpacing: 1 }}>UP NEXT · 4 MIN READ</Text>\n <Text style={{ color: colors.foreground, fontSize: 15, fontWeight: '600' }}>Lisbon by tram, end to end</Text>\n </View>\n </View>\n </View>\n );\n}\n"
154
+ "code": "import React, { useState } from 'react';\nimport { Image, Pressable, Text, View } from 'react-native';\nimport { TiltCard, TiltLayer } from '@/components/ui/tilt-card';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Any photo works (`require()` or a URL). */\nconst photo = (id: number, w: number, h: number) => ({ uri: `https://picsum.photos/id/${id}/${w}/${h}` });\n\n/** A screen title, as it sits above the card in your app. */\nfunction Header({ title, sub, right }: { title: string; sub?: string; right?: React.ReactNode }) {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 22, fontWeight: '700', letterSpacing: -0.4 }}>{title}</Text>\n {sub ? <Text style={{ color: colors.mutedForeground, fontSize: 13, marginTop: 2 }}>{sub}</Text> : null}\n </View>\n {right}\n </View>\n );\n}\n\nexport default function FeedCard() {\n const { colors } = useKit();\n return (\n <View style={{ gap: 16 }}>\n <Header title=\"For you\" sub=\"Picked from places you saved\" />\n <TiltCard maxTilt={5} pressScale={1.015} glare=\"soft\" radius={20} onPress={() => {}} accessibilityLabel=\"Read: A fairytale weekend in Bavaria\" style={{ alignSelf: 'stretch' }}>\n <View style={{ backgroundColor: colors.card }}>\n <Image alt=\"\" source={photo(1040, 720, 360)} style={{ height: 170 }} />\n <View style={{ padding: 16, gap: 4 }}>\n <Text style={{ color: colors.mutedForeground, fontSize: 11, fontWeight: '700', letterSpacing: 1 }}>TRAVEL · 6 MIN READ</Text>\n <Text style={{ color: colors.foreground, fontSize: 18, fontWeight: '700', letterSpacing: -0.3 }}>A fairytale weekend in Bavaria</Text>\n <Text style={{ color: colors.mutedForeground, fontSize: 13, lineHeight: 19 }}>Castles, lakes and the best pretzels we had all year.</Text>\n </View>\n </View>\n </TiltCard>\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12, padding: 10, borderRadius: 16, backgroundColor: colors.card }}>\n <Image alt=\"\" source={photo(419, 160, 160)} style={{ width: 56, height: 56, borderRadius: 12, backgroundColor: colors.muted }} />\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.mutedForeground, fontSize: 11, fontWeight: '700', letterSpacing: 1 }}>UP NEXT · 4 MIN READ</Text>\n <Text style={{ color: colors.foreground, fontSize: 15, fontWeight: '600' }}>Lisbon by tram, end to end</Text>\n </View>\n </View>\n </View>\n );\n}\n"
155
155
  }
156
156
  ]
157
157
  }