@expokit/cli 0.4.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 (92) hide show
  1. package/bin/lib.mjs +1 -1
  2. package/package.json +1 -1
  3. package/registry/_docs/accordion.json +1 -1
  4. package/registry/_docs/avatar.json +6 -6
  5. package/registry/_docs/badge.json +3 -3
  6. package/registry/_docs/bottom-nav.json +6 -6
  7. package/registry/_docs/bottom-sheet.json +3 -3
  8. package/registry/_docs/bullet.json +6 -0
  9. package/registry/_docs/button-3d.json +2 -2
  10. package/registry/_docs/button.json +2 -2
  11. package/registry/_docs/calendar.json +5 -5
  12. package/registry/_docs/card.json +1 -1
  13. package/registry/_docs/chip-group.json +1 -1
  14. package/registry/_docs/confetti.json +3 -3
  15. package/registry/_docs/date-picker.json +4 -4
  16. package/registry/_docs/drawer.json +4 -4
  17. package/registry/_docs/flip-card.json +4 -4
  18. package/registry/_docs/floating-input.json +1 -1
  19. package/registry/_docs/grain.json +2 -2
  20. package/registry/_docs/isometric-bar.json +6 -0
  21. package/registry/_docs/liquidglass-tab.json +1 -1
  22. package/registry/_docs/otp-input.json +1 -1
  23. package/registry/_docs/payment-card.json +3 -3
  24. package/registry/_docs/pixel-bar.json +6 -0
  25. package/registry/_docs/polar-area.json +6 -0
  26. package/registry/_docs/progress-steps.json +4 -4
  27. package/registry/_docs/progress.json +2 -2
  28. package/registry/_docs/radio-group.json +1 -1
  29. package/registry/_docs/search-bar.json +3 -3
  30. package/registry/_docs/segmented-control.json +1 -1
  31. package/registry/_docs/select.json +6 -6
  32. package/registry/_docs/skeleton.json +1 -1
  33. package/registry/_docs/slider.json +6 -6
  34. package/registry/_docs/spinner.json +2 -2
  35. package/registry/_docs/stateful-button.json +1 -1
  36. package/registry/_docs/stepper.json +3 -3
  37. package/registry/_docs/tabs.json +2 -2
  38. package/registry/_docs/text-animation.json +4 -4
  39. package/registry/_docs/textarea.json +1 -1
  40. package/registry/_docs/tilt-card.json +3 -3
  41. package/registry/_docs/time-picker.json +6 -6
  42. package/registry/_docs/timeline-chart.json +6 -0
  43. package/registry/_docs/timeline.json +4 -4
  44. package/registry/_docs/treemap.json +6 -0
  45. package/registry/_docs/waterfall.json +6 -0
  46. package/registry/accordion/files/src/components/ui/accordion.tsx +31 -3
  47. package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +4 -1
  48. package/registry/alert/files/src/components/ui/alert.tsx +5 -2
  49. package/registry/alert/files/src/components/ui/button.tsx +32 -4
  50. package/registry/badge/files/src/components/ui/badge.tsx +2 -2
  51. package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +11 -5
  52. package/registry/button/files/src/components/ui/button.tsx +32 -4
  53. package/registry/button-3d/files/src/components/ui/button-3d.tsx +33 -6
  54. package/registry/calendar/files/src/components/ui/calendar.tsx +62 -17
  55. package/registry/card/files/src/components/ui/card.tsx +25 -5
  56. package/registry/checkbox/files/src/components/ui/checkbox.tsx +26 -4
  57. package/registry/chip-group/files/src/components/ui/chip-group.tsx +35 -5
  58. package/registry/coupon/files/src/components/ui/coupon.tsx +45 -21
  59. package/registry/coupon/files/src/components/ui/flip-card.tsx +22 -3
  60. package/registry/coupon/files/src/components/ui/tilt-card.tsx +57 -16
  61. package/registry/date-picker/files/src/components/ui/calendar.tsx +62 -17
  62. package/registry/date-picker/files/src/components/ui/date-picker.tsx +96 -42
  63. package/registry/dialog/files/src/components/ui/dialog.tsx +9 -5
  64. package/registry/drawer/files/src/components/ui/drawer.tsx +8 -4
  65. package/registry/flip-card/files/src/components/ui/flip-card.tsx +22 -3
  66. package/registry/floating-input/files/src/components/ui/floating-input.tsx +75 -26
  67. package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +1 -1
  68. package/registry/list-item/files/src/components/ui/list-item.tsx +10 -3
  69. package/registry/otp-input/files/src/components/ui/otp-input.tsx +50 -19
  70. package/registry/password-input/files/src/components/ui/floating-input.tsx +75 -26
  71. package/registry/password-input/files/src/components/ui/password-input.tsx +53 -19
  72. package/registry/payment-card/files/src/components/ui/flip-card.tsx +22 -3
  73. package/registry/payment-card/files/src/components/ui/payment-card.tsx +19 -15
  74. package/registry/payment-card/files/src/components/ui/tilt-card.tsx +57 -16
  75. package/registry/progress/files/src/components/ui/progress.tsx +9 -2
  76. package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +11 -4
  77. package/registry/radio-group/files/src/components/ui/radio-group.tsx +39 -9
  78. package/registry/search-bar/files/src/components/ui/search-bar.tsx +70 -17
  79. package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +37 -9
  80. package/registry/select/files/src/components/ui/select.tsx +158 -83
  81. package/registry/slider/files/src/components/ui/slider.tsx +53 -25
  82. package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +12 -4
  83. package/registry/stepper/files/src/components/ui/stepper.tsx +76 -26
  84. package/registry/switch/files/src/components/ui/switch.tsx +56 -19
  85. package/registry/tabs/files/src/components/ui/tabs.tsx +7 -4
  86. package/registry/text-animation/files/src/components/ui/text-animation.tsx +7 -2
  87. package/registry/textarea/files/src/components/ui/textarea.tsx +104 -36
  88. package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +57 -16
  89. package/registry/time-picker/files/src/components/ui/time-picker.tsx +154 -73
  90. package/registry/timeline/files/src/components/ui/timeline.tsx +9 -4
  91. package/registry/toast/files/src/components/ui/button.tsx +32 -4
  92. package/registry/toast/files/src/components/ui/toast.tsx +15 -6
@@ -132,7 +132,7 @@
132
132
  {
133
133
  "name": "VerifyPhone",
134
134
  "title": "Verify Phone",
135
- "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { OtpInput } from '@/components/ui/otp-input';\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 { padding: 16, gap: 12 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : { borderRadius: radius.xl, borderWidth: StyleSheet.hairlineWidth, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nfunction Header({ title, body }: { title: string; body: string }) {\n const { colors } = useKit();\n return (\n <View style={{ gap: 4, alignItems: 'center' }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground, textAlign: 'center' }}>{title}</Text>\n <Text style={{ fontSize: 13, color: colors.mutedForeground, textAlign: 'center' }}>{body}</Text>\n </View>\n );\n}\n\nexport default function VerifyPhone({ screen }: DemoProps) {\n const { colors } = useKit();\n const [code, setCode] = useState('');\n const [left, setLeft] = useState(24);\n useEffect(() => {\n if (left <= 0) return;\n const id = setTimeout(() => setLeft((s) => s - 1), 1000);\n return () => clearTimeout(id);\n }, [left]);\n const done = code.length === 6;\n return (\n <Card screen={screen}>\n <Header title=\"Verify your number\" body=\"Enter the code sent to +91 98•••• 4210\" />\n <OtpInput\n value={code}\n onChangeText={setCode}\n success={code === '123456'}\n error={done && code !== '123456'}\n successText=\"Number verified\"\n errorText=\"That code didn't match. Try 123456.\"\n helperText=\"Tip: the demo code is 123456\"\n />\n <View style={{ flexDirection: 'row', justifyContent: 'center', gap: 16 }}>\n <Pressable disabled={left > 0} onPress={() => { setLeft(24); setCode(''); }} hitSlop={10}>\n <Text style={{ fontSize: 13, fontWeight: '400', color: left > 0 ? colors.mutedForeground : colors.accent }}>\n {left > 0 ? `Resend in 0:${String(left).padStart(2, '0')}` : 'Resend code'}\n </Text>\n </Pressable>\n <Pressable hitSlop={10} accessibilityRole=\"button\">\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>Change number</Text>\n </Pressable>\n </View>\n </Card>\n );\n}\n"
135
+ "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { OtpInput } from '@/components/ui/otp-input';\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 { padding: 16, gap: 12 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : { borderRadius: radius.xl, borderWidth: StyleSheet.hairlineWidth, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nfunction Header({ title, body }: { title: string; body: string }) {\n const { colors } = useKit();\n return (\n <View style={{ gap: 4, alignItems: 'center' }}>\n <Text style={{ fontSize: 16, fontWeight: '400', color: colors.foreground, textAlign: 'center' }}>{title}</Text>\n <Text style={{ fontSize: 13, color: colors.mutedForeground, textAlign: 'center' }}>{body}</Text>\n </View>\n );\n}\n\nexport default function VerifyPhone({ screen }: DemoProps) {\n const { colors } = useKit();\n const [code, setCode] = useState('');\n const [left, setLeft] = useState(24);\n useEffect(() => {\n if (left <= 0) return;\n const id = setTimeout(() => setLeft((s) => s - 1), 1000);\n return () => clearTimeout(id);\n }, [left]);\n const done = code.length === 6;\n return (\n <Card screen={screen}>\n <Header title=\"Verify your number\" body=\"Enter the code sent to +91 98•••• 4210\" />\n <OtpInput\n value={code}\n onChangeText={setCode}\n success={code === '123456'}\n error={done && code !== '123456'}\n successText=\"Number verified\"\n errorText=\"That code didn't match. Try 123456.\"\n helperText=\"Tip: the demo code is 123456\"\n />\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'center', columnGap: 16, rowGap: 8 }}>\n <Pressable disabled={left > 0} onPress={() => { setLeft(24); setCode(''); }} hitSlop={10} style={{ flexShrink: 1 }}>\n <Text style={{ fontSize: 13, fontWeight: '400', textAlign: 'center', color: left > 0 ? colors.mutedForeground : colors.accent }}>\n {left > 0 ? `Resend in 0:${String(left).padStart(2, '0')}` : 'Resend code'}\n </Text>\n </Pressable>\n <Pressable hitSlop={10} accessibilityRole=\"button\" style={{ flexShrink: 1 }}>\n <Text style={{ fontSize: 13, fontWeight: '400', textAlign: 'center', color: colors.accent }}>Change number</Text>\n </Pressable>\n </View>\n </Card>\n );\n}\n"
136
136
  },
137
137
  {
138
138
  "name": "Pin",
@@ -148,7 +148,7 @@
148
148
  {
149
149
  "name": "WalletScreen",
150
150
  "title": "Wallet Screen",
151
- "code": "import { Image, Pressable, Text, View } from 'react-native';\nimport { PaymentCard } from '@/components/ui/payment-card';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Soft, blurred photos make premium card art — any image works (`require()` or a URL). */\nconst art = (id: number) => ({ uri: `https://picsum.photos/id/${id}/240/151?blur=10` });\n\nexport default function WalletScreen() {\n const { colors } = useKit();\n const actions = [\n ['↑', 'Send'],\n ['↓', 'Request'],\n ['+', 'Top up'],\n ['⋯', 'More'],\n ];\n const activity = [\n ['B', 'Blue Tokai Coffee', 'Today · 09:12', '−₹240'],\n ['U', 'Uber', 'Yesterday · 21:40', '−₹386'],\n ['S', 'Salary · Northwind Labs', 'Mon · 10:00', '+₹82,000'],\n ];\n return (\n <View style={{ gap: 20 }}>\n <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>\n <View>\n <Text style={{ color: colors.mutedForeground, fontSize: 13 }}>Total balance</Text>\n <Text style={{ color: colors.foreground, fontSize: 30, fontWeight: '700', letterSpacing: -0.8, fontVariant: ['tabular-nums'] }}>₹1,24,580.20</Text>\n <Text style={{ color: colors.success, fontSize: 12, fontWeight: '500' }}>+₹2,140 this week</Text>\n </View>\n <View style={{ width: 40, height: 40, borderRadius: 20, backgroundColor: colors.muted, alignItems: 'center', justifyContent: 'center' }}>\n <Text style={{ color: colors.foreground, fontSize: 14, fontWeight: '700' }}>MP</Text>\n </View>\n </View>\n\n <PaymentCard\n image={art(189)}\n bank=\"Northwind\"\n label=\"World Elite\"\n number=\"5412 7590 1234 0317\"\n name=\"Maya Patel\"\n expiry=\"09/29\"\n cvv=\"482\"\n brand=\"mastercard\"\n />\n\n <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>\n {actions.map(([glyph, label]) => (\n <View key={label} style={{ alignItems: 'center', gap: 6, width: 64 }}>\n <View style={{ width: 52, height: 52, borderRadius: 26, backgroundColor: colors.muted, alignItems: 'center', justifyContent: 'center' }}>\n <Text style={{ color: colors.foreground, fontSize: 20, fontWeight: '500' }}>{glyph}</Text>\n </View>\n <Text style={{ color: colors.mutedForeground, fontSize: 12 }}>{label}</Text>\n </View>\n ))}\n </View>\n\n <View>\n <Text style={{ color: colors.foreground, fontSize: 16, fontWeight: '600', marginBottom: 4 }}>Recent activity</Text>\n {activity.map(([initial, what, when, amount]) => (\n <View key={what} style={{ flexDirection: 'row', alignItems: 'center', gap: 12, paddingVertical: 10 }}>\n <View style={{ width: 38, height: 38, borderRadius: 12, backgroundColor: colors.muted, alignItems: 'center', justifyContent: 'center' }}>\n <Text style={{ color: colors.foreground, fontSize: 14, fontWeight: '700' }}>{initial}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 14, fontWeight: '500' }}>{what}</Text>\n <Text style={{ color: colors.mutedForeground, fontSize: 12 }}>{when}</Text>\n </View>\n <Text style={{ color: amount.startsWith('+') ? colors.success : colors.foreground, fontSize: 14, fontWeight: '600', fontVariant: ['tabular-nums'] }}>{amount}</Text>\n </View>\n ))}\n </View>\n </View>\n );\n}\n"
151
+ "code": "import { Image, Pressable, Text, View } from 'react-native';\nimport { PaymentCard } from '@/components/ui/payment-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/** Soft, blurred photos make premium card art — any image works (`require()` or a URL). */\nconst art = (id: number) => ({ uri: `https://picsum.photos/id/${id}/240/151?blur=10` });\n\nexport default function WalletScreen() {\n const { colors } = useKit();\n const actions = [\n ['↑', 'Send'],\n ['↓', 'Request'],\n ['+', 'Top up'],\n ['⋯', 'More'],\n ];\n const activity = [\n ['B', 'Blue Tokai Coffee', 'Today · 09:12', '−₹240'],\n ['U', 'Uber', 'Yesterday · 21:40', '−₹386'],\n ['S', 'Salary · Northwind Labs', 'Mon · 10:00', '+₹82,000'],\n ];\n return (\n <View style={{ gap: 20 }}>\n <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>\n <View style={{ flexShrink: 1 }}>\n <Text style={{ color: colors.mutedForeground, fontSize: 13 }}>Total balance</Text>\n <Text style={{ color: colors.foreground, fontSize: 30, fontWeight: '700', letterSpacing: -0.8, fontVariant: ['tabular-nums'] }}>₹1,24,580.20</Text>\n <Text style={{ color: colors.success, fontSize: 12, fontWeight: '500' }}>+₹2,140 this week</Text>\n </View>\n <View style={{ width: 40, height: 40, borderRadius: 20, backgroundColor: colors.muted, alignItems: 'center', justifyContent: 'center' }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 14, fontWeight: '700' }}>MP</Text>\n </View>\n </View>\n\n <PaymentCard\n image={art(189)}\n bank=\"Northwind\"\n label=\"World Elite\"\n number=\"5412 7590 1234 0317\"\n name=\"Maya Patel\"\n expiry=\"09/29\"\n cvv=\"482\"\n brand=\"mastercard\"\n />\n\n <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>\n {actions.map(([glyph, label]) => (\n <View key={label} style={{ alignItems: 'center', gap: 6, width: 64 }}>\n <View style={{ width: 52, height: 52, borderRadius: 26, backgroundColor: colors.muted, alignItems: 'center', justifyContent: 'center' }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 20, fontWeight: '500' }}>{glyph}</Text>\n </View>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.mutedForeground, fontSize: 12 }}>{label}</Text>\n </View>\n ))}\n </View>\n\n <View>\n <Text style={{ color: colors.foreground, fontSize: 16, fontWeight: '600', marginBottom: 4 }}>Recent activity</Text>\n {activity.map(([initial, what, when, amount]) => (\n <View key={what} style={{ flexDirection: 'row', alignItems: 'center', gap: 12, paddingVertical: 10 }}>\n <View style={{ width: 38, height: 38, borderRadius: 12, backgroundColor: colors.muted, alignItems: 'center', justifyContent: 'center' }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 14, fontWeight: '700' }}>{initial}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 14, fontWeight: '500' }}>{what}</Text>\n <Text style={{ color: colors.mutedForeground, fontSize: 12 }}>{when}</Text>\n </View>\n <Text style={{ color: amount.startsWith('+') ? colors.success : colors.foreground, fontSize: 14, fontWeight: '600', fontVariant: ['tabular-nums'] }}>{amount}</Text>\n </View>\n ))}\n </View>\n </View>\n );\n}\n"
152
152
  },
153
153
  {
154
154
  "name": "CardControls",
@@ -158,12 +158,12 @@
158
158
  {
159
159
  "name": "ChooseDesign",
160
160
  "title": "Choose Design",
161
- "code": "import React, { useState } from 'react';\nimport { Image, Pressable, Text, View } from 'react-native';\nimport { PaymentCard } from '@/components/ui/payment-card';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Soft, blurred photos make premium card art — any image works (`require()` or a URL). */\nconst art = (id: number) => ({ uri: `https://picsum.photos/id/${id}/240/151?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 DESIGNS = [\n { key: 'meadow', image: art(189) },\n { key: 'lagoon', image: art(846) },\n { key: 'violet', image: art(888) },\n { key: 'dusk', image: art(721) },\n { key: 'night', image: art(765) },\n];\n\nexport default function ChooseDesign() {\n const { colors } = useKit();\n const [pick, setPick] = useState(0);\n return (\n <View style={{ gap: 16 }}>\n <Header title=\"Pick your card\" sub=\"Your design ships in 3–5 days\" />\n <PaymentCard\n image={DESIGNS[pick].image}\n bank=\"Northwind\"\n label=\"Debit\"\n number=\"6521 4400 9911 4410\"\n name=\"Maya Patel\"\n expiry=\"01/30\"\n brand=\"rupay\"\n />\n <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>\n {DESIGNS.map((d, i) => {\n const on = i === pick;\n return (\n <Pressable\n key={d.key}\n onPress={() => setPick(i)}\n accessibilityRole=\"radio\"\n accessibilityLabel={d.key}\n accessibilityState={{ selected: on }}\n style={{ padding: 2, borderRadius: 10, borderWidth: 2, borderColor: on ? colors.foreground : 'transparent' }}\n >\n <Image alt=\"\" source={d.image} style={{ width: 52, height: 33, borderRadius: 6, backgroundColor: colors.muted }} />\n </Pressable>\n );\n })}\n </View>\n <Pressable accessibilityRole=\"button\" style={{ alignItems: 'center', paddingVertical: 14, borderRadius: 14, backgroundColor: colors.primary, marginTop: 8 }}>\n <Text style={{ color: colors.primaryForeground, fontSize: 15, fontWeight: '600' }}>Order this card</Text>\n </Pressable>\n </View>\n );\n}\n"
161
+ "code": "import React, { useState } from 'react';\nimport { Image, Pressable, Text, View } from 'react-native';\nimport { PaymentCard } from '@/components/ui/payment-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/** Soft, blurred photos make premium card art — any image works (`require()` or a URL). */\nconst art = (id: number) => ({ uri: `https://picsum.photos/id/${id}/240/151?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 DESIGNS = [\n { key: 'meadow', image: art(189) },\n { key: 'lagoon', image: art(846) },\n { key: 'violet', image: art(888) },\n { key: 'dusk', image: art(721) },\n { key: 'night', image: art(765) },\n];\n\nexport default function ChooseDesign() {\n const { colors } = useKit();\n const [pick, setPick] = useState(0);\n return (\n <View style={{ gap: 16 }}>\n <Header title=\"Pick your card\" sub=\"Your design ships in 3–5 days\" />\n <PaymentCard\n image={DESIGNS[pick].image}\n bank=\"Northwind\"\n label=\"Debit\"\n number=\"6521 4400 9911 4410\"\n name=\"Maya Patel\"\n expiry=\"01/30\"\n brand=\"rupay\"\n />\n <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>\n {DESIGNS.map((d, i) => {\n const on = i === pick;\n return (\n <Pressable\n key={d.key}\n onPress={() => setPick(i)}\n accessibilityRole=\"radio\"\n accessibilityLabel={d.key}\n accessibilityState={{ selected: on }}\n style={{ padding: 2, borderRadius: 10, borderWidth: 2, borderColor: on ? colors.foreground : 'transparent' }}\n >\n <Image alt=\"\" source={d.image} style={{ width: 52, height: 33, borderRadius: 6, backgroundColor: colors.muted }} />\n </Pressable>\n );\n })}\n </View>\n <Pressable accessibilityRole=\"button\" style={{ alignItems: 'center', paddingVertical: 14, borderRadius: 14, backgroundColor: colors.primary, marginTop: 8 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.primaryForeground, fontSize: 15, fontWeight: '600' }}>Order this card</Text>\n </Pressable>\n </View>\n );\n}\n"
162
162
  },
163
163
  {
164
164
  "name": "AddCard",
165
165
  "title": "Add Card",
166
- "code": "import React, { useState } from 'react';\nimport { Image, Pressable, Text, View } from 'react-native';\nimport { PaymentCard } from '@/components/ui/payment-card';\nimport { alpha, useKit } from '@/lib/kit';\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 AddCard() {\n const { colors } = useKit();\n const [digits, setDigits] = useState('4821');\n const keys = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];\n return (\n <View style={{ gap: 16 }}>\n <Header title=\"Add a card\" sub=\"Type the number on the front\" />\n <PaymentCard bank=\"New card\" number={digits.padEnd(16, '•')} name=\"Your name\" expiry=\"MM/YY\" brand=\"visa\" chip=\"silver\" masked={false} />\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8, justifyContent: 'center' }}>\n {keys.map((k) => (\n <Pressable\n key={k}\n onPress={() => setDigits((d) => (d.length >= 16 ? '4821' : d + k))}\n accessibilityRole=\"button\"\n style={{ width: 58, height: 46, borderRadius: 12, alignItems: 'center', justifyContent: 'center', backgroundColor: colors.muted }}\n >\n <Text style={{ color: colors.foreground, fontSize: 19, fontWeight: '500' }}>{k}</Text>\n </Pressable>\n ))}\n </View>\n </View>\n );\n}\n"
166
+ "code": "import React, { useState } from 'react';\nimport { Image, Pressable, Text, View } from 'react-native';\nimport { PaymentCard } from '@/components/ui/payment-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/** 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 AddCard() {\n const { colors } = useKit();\n const [digits, setDigits] = useState('4821');\n const keys = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];\n return (\n <View style={{ gap: 16 }}>\n <Header title=\"Add a card\" sub=\"Type the number on the front\" />\n <PaymentCard bank=\"New card\" number={digits.padEnd(16, '•')} name=\"Your name\" expiry=\"MM/YY\" brand=\"visa\" chip=\"silver\" masked={false} />\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8, justifyContent: 'center' }}>\n {keys.map((k) => (\n <Pressable\n key={k}\n onPress={() => setDigits((d) => (d.length >= 16 ? '4821' : d + k))}\n accessibilityRole=\"button\"\n style={{ width: 58, minHeight: 46, borderRadius: 12, alignItems: 'center', justifyContent: 'center', backgroundColor: colors.muted }}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 19, fontWeight: '500' }}>{k}</Text>\n </Pressable>\n ))}\n </View>\n </View>\n );\n}\n"
167
167
  }
168
168
  ]
169
169
  }
@@ -61,6 +61,12 @@
61
61
  "boolean",
62
62
  "true",
63
63
  "Fade colour upwards."
64
+ ],
65
+ [
66
+ "accessibilityLabel",
67
+ "string",
68
+ "auto summary",
69
+ "What screen readers announce; defaults to a summary of the data."
64
70
  ]
65
71
  ],
66
72
  "behavior": [],
@@ -73,6 +73,12 @@
73
73
  "number",
74
74
  "4",
75
75
  "Grid circles."
76
+ ],
77
+ [
78
+ "accessibilityLabel",
79
+ "string",
80
+ "auto summary",
81
+ "What screen readers announce; defaults to a summary of the data."
76
82
  ]
77
83
  ],
78
84
  "behavior": [],
@@ -90,22 +90,22 @@
90
90
  {
91
91
  "name": "Checkout",
92
92
  "title": "Checkout",
93
- "code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, useKit } from '@/lib/kit';\n\nfunction Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n height: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: alpha(colors.foreground, pressed ? 0.1 : 0.05),\n alignItems: 'center',\n justifyContent: 'center',\n opacity: step === 0 ? 0.4 : 1,\n })}\n >\n <Text style={{ fontSize: 15, color: colors.foreground }}>Back</Text>\n </Pressable>\n <Pressable\n onPress={() => set(done ? 0 : step + 1)}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1.5,\n height: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n transform: [{ scale: pressed ? 0.98 : 1 }],\n })}\n >\n <Text style={{ fontSize: 15, color: colors.accentForeground }}>\n {done ? 'Start over' : step === max - 1 ? last : 'Continue'}\n </Text>\n </Pressable>\n </View>\n );\n}\n\nexport default function Checkout() {\n const { colors } = useKit();\n const [step, setStep] = useState(1);\n const copy = [\n '2 items · ₹2,798',\n 'Delivering to 14 Rua das Flores, Lisbon',\n 'Visa ending 4242',\n 'Arrives Thursday, 16 October',\n ];\n return (\n <View style={{ gap: 22 }}>\n <ProgressSteps\n steps={['Cart', 'Shipping', 'Payment', 'Review']}\n current={step}\n pressableSteps=\"completed\"\n onStepPress={setStep}\n />\n <Text style={{ fontSize: 14, textAlign: 'center', color: colors.mutedForeground }}>\n {step >= 4 ? 'Order #4821 placed — receipt sent to amelia@hart.studio' : copy[step]}\n </Text>\n <Buttons step={step} max={4} set={setStep} />\n </View>\n );\n}\n"
93
+ "code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, 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 Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: alpha(colors.foreground, pressed ? 0.1 : 0.05),\n alignItems: 'center',\n justifyContent: 'center',\n opacity: step === 0 ? 0.4 : 1,\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.foreground }}>Back</Text>\n </Pressable>\n <Pressable\n onPress={() => set(done ? 0 : step + 1)}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1.5,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n transform: [{ scale: pressed ? 0.98 : 1 }],\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.accentForeground }}>\n {done ? 'Start over' : step === max - 1 ? last : 'Continue'}\n </Text>\n </Pressable>\n </View>\n );\n}\n\nexport default function Checkout() {\n const { colors } = useKit();\n const [step, setStep] = useState(1);\n const copy = [\n '2 items · ₹2,798',\n 'Delivering to 14 Rua das Flores, Lisbon',\n 'Visa ending 4242',\n 'Arrives Thursday, 16 October',\n ];\n return (\n <View style={{ gap: 22 }}>\n <ProgressSteps\n steps={['Cart', 'Shipping', 'Payment', 'Review']}\n current={step}\n pressableSteps=\"completed\"\n onStepPress={setStep}\n />\n <Text style={{ fontSize: 14, textAlign: 'center', color: colors.mutedForeground }}>\n {step >= 4 ? 'Order #4821 placed — receipt sent to amelia@hart.studio' : copy[step]}\n </Text>\n <Buttons step={step} max={4} set={setStep} />\n </View>\n );\n}\n"
94
94
  },
95
95
  {
96
96
  "name": "Onboarding",
97
97
  "title": "Onboarding",
98
- "code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, useKit } from '@/lib/kit';\n\nfunction Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n height: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: alpha(colors.foreground, pressed ? 0.1 : 0.05),\n alignItems: 'center',\n justifyContent: 'center',\n opacity: step === 0 ? 0.4 : 1,\n })}\n >\n <Text style={{ fontSize: 15, color: colors.foreground }}>Back</Text>\n </Pressable>\n <Pressable\n onPress={() => set(done ? 0 : step + 1)}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1.5,\n height: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n transform: [{ scale: pressed ? 0.98 : 1 }],\n })}\n >\n <Text style={{ fontSize: 15, color: colors.accentForeground }}>\n {done ? 'Start over' : step === max - 1 ? last : 'Continue'}\n </Text>\n </Pressable>\n </View>\n );\n}\n\nexport default function Onboarding() {\n const [step, setStep] = useState(2);\n return (\n <View style={{ gap: 20 }}>\n <ProgressSteps\n variant=\"vertical\"\n current={step}\n pressableSteps=\"completed\"\n onStepPress={setStep}\n steps={[\n { label: 'Create your account', description: 'Signed up with amelia@hart.studio' },\n { label: 'Verify your email', description: 'Confirmed with a 6-digit code.' },\n { label: 'Connect your bank', description: 'Read-only access. You can disconnect any time.' },\n { label: 'Set your first budget', description: 'Pick categories and a monthly limit.' },\n ]}\n />\n <Buttons step={step} max={4} set={setStep} last=\"Finish setup\" />\n </View>\n );\n}\n"
98
+ "code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, 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 Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: alpha(colors.foreground, pressed ? 0.1 : 0.05),\n alignItems: 'center',\n justifyContent: 'center',\n opacity: step === 0 ? 0.4 : 1,\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.foreground }}>Back</Text>\n </Pressable>\n <Pressable\n onPress={() => set(done ? 0 : step + 1)}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1.5,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n transform: [{ scale: pressed ? 0.98 : 1 }],\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.accentForeground }}>\n {done ? 'Start over' : step === max - 1 ? last : 'Continue'}\n </Text>\n </Pressable>\n </View>\n );\n}\n\nexport default function Onboarding() {\n const [step, setStep] = useState(2);\n return (\n <View style={{ gap: 20 }}>\n <ProgressSteps\n variant=\"vertical\"\n current={step}\n pressableSteps=\"completed\"\n onStepPress={setStep}\n steps={[\n { label: 'Create your account', description: 'Signed up with amelia@hart.studio' },\n { label: 'Verify your email', description: 'Confirmed with a 6-digit code.' },\n { label: 'Connect your bank', description: 'Read-only access. You can disconnect any time.' },\n { label: 'Set your first budget', description: 'Pick categories and a monthly limit.' },\n ]}\n />\n <Buttons step={step} max={4} set={setStep} last=\"Finish setup\" />\n </View>\n );\n}\n"
99
99
  },
100
100
  {
101
101
  "name": "Compact",
102
102
  "title": "Compact",
103
- "code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, useKit } from '@/lib/kit';\n\nfunction Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n height: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: alpha(colors.foreground, pressed ? 0.1 : 0.05),\n alignItems: 'center',\n justifyContent: 'center',\n opacity: step === 0 ? 0.4 : 1,\n })}\n >\n <Text style={{ fontSize: 15, color: colors.foreground }}>Back</Text>\n </Pressable>\n <Pressable\n onPress={() => set(done ? 0 : step + 1)}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1.5,\n height: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n transform: [{ scale: pressed ? 0.98 : 1 }],\n })}\n >\n <Text style={{ fontSize: 15, color: colors.accentForeground }}>\n {done ? 'Start over' : step === max - 1 ? last : 'Continue'}\n </Text>\n </Pressable>\n </View>\n );\n}\n\nexport default function Compact() {\n const [step, setStep] = useState(1);\n return (\n <View style={{ gap: 20 }}>\n <ProgressSteps variant=\"bar\" steps={['Shipping address', 'Delivery options', 'Payment', 'Review order']} current={step} />\n <Buttons step={step} max={4} set={setStep} />\n </View>\n );\n}\n"
103
+ "code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, 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 Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: alpha(colors.foreground, pressed ? 0.1 : 0.05),\n alignItems: 'center',\n justifyContent: 'center',\n opacity: step === 0 ? 0.4 : 1,\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.foreground }}>Back</Text>\n </Pressable>\n <Pressable\n onPress={() => set(done ? 0 : step + 1)}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1.5,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n transform: [{ scale: pressed ? 0.98 : 1 }],\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.accentForeground }}>\n {done ? 'Start over' : step === max - 1 ? last : 'Continue'}\n </Text>\n </Pressable>\n </View>\n );\n}\n\nexport default function Compact() {\n const [step, setStep] = useState(1);\n return (\n <View style={{ gap: 20 }}>\n <ProgressSteps variant=\"bar\" steps={['Shipping address', 'Delivery options', 'Payment', 'Review order']} current={step} />\n <Buttons step={step} max={4} set={setStep} />\n </View>\n );\n}\n"
104
104
  },
105
105
  {
106
106
  "name": "WithDescriptions",
107
107
  "title": "With Descriptions",
108
- "code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, useKit } from '@/lib/kit';\n\nfunction Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n height: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: alpha(colors.foreground, pressed ? 0.1 : 0.05),\n alignItems: 'center',\n justifyContent: 'center',\n opacity: step === 0 ? 0.4 : 1,\n })}\n >\n <Text style={{ fontSize: 15, color: colors.foreground }}>Back</Text>\n </Pressable>\n <Pressable\n onPress={() => set(done ? 0 : step + 1)}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1.5,\n height: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n transform: [{ scale: pressed ? 0.98 : 1 }],\n })}\n >\n <Text style={{ fontSize: 15, color: colors.accentForeground }}>\n {done ? 'Start over' : step === max - 1 ? last : 'Continue'}\n </Text>\n </Pressable>\n </View>\n );\n}\n\nexport default function WithDescriptions() {\n const [step, setStep] = useState(1);\n return (\n <View style={{ gap: 22 }}>\n <ProgressSteps\n current={step}\n steps={[\n { label: 'Profile', description: 'Name, photo' },\n { label: 'Workspace', description: 'Invite team' },\n { label: 'Plan', description: 'Free or Pro' },\n ]}\n />\n <Buttons step={step} max={3} set={setStep} last=\"Create workspace\" />\n </View>\n );\n}\n"
108
+ "code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, 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 Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: alpha(colors.foreground, pressed ? 0.1 : 0.05),\n alignItems: 'center',\n justifyContent: 'center',\n opacity: step === 0 ? 0.4 : 1,\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.foreground }}>Back</Text>\n </Pressable>\n <Pressable\n onPress={() => set(done ? 0 : step + 1)}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1.5,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n transform: [{ scale: pressed ? 0.98 : 1 }],\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.accentForeground }}>\n {done ? 'Start over' : step === max - 1 ? last : 'Continue'}\n </Text>\n </Pressable>\n </View>\n );\n}\n\nexport default function WithDescriptions() {\n const [step, setStep] = useState(1);\n return (\n <View style={{ gap: 22 }}>\n <ProgressSteps\n current={step}\n steps={[\n { label: 'Profile', description: 'Name, photo' },\n { label: 'Workspace', description: 'Invite team' },\n { label: 'Plan', description: 'Free or Pro' },\n ]}\n />\n <Buttons step={step} max={3} set={setStep} last=\"Create workspace\" />\n </View>\n );\n}\n"
109
109
  },
110
110
  {
111
111
  "name": "WithError",
@@ -116,7 +116,7 @@
116
116
  {
117
117
  "name": "Linear",
118
118
  "title": "Linear",
119
- "code": "import React, { useEffect, useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { Progress } from '@/components/ui/progress';\n\nexport default function Linear() {\n const [value, setValue] = useState(32);\n return (\n <View style={{ gap: 18 }}>\n <Progress value={value} label=\"Uploading trip-to-lisbon.mov\" showValue />\n <Progress value={value} size=\"sm\" />\n <Progress value={value} size=\"lg\" />\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Button size=\"sm\" variant=\"outline\" onPress={() => setValue((v) => Math.max(0, v - 20))}>\n − 20\n </Button>\n <Button size=\"sm\" variant=\"outline\" onPress={() => setValue((v) => Math.min(100, v + 20))}>\n + 20\n </Button>\n </View>\n </View>\n );\n}\n"
119
+ "code": "import React, { useEffect, useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { Progress } from '@/components/ui/progress';\n\nexport default function Linear() {\n const [value, setValue] = useState(32);\n return (\n <View style={{ gap: 18 }}>\n <Progress value={value} label=\"Uploading trip-to-lisbon.mov\" showValue />\n <Progress value={value} size=\"sm\" />\n <Progress value={value} size=\"lg\" />\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 10 }}>\n <Button size=\"sm\" variant=\"outline\" onPress={() => setValue((v) => Math.max(0, v - 20))}>\n − 20\n </Button>\n <Button size=\"sm\" variant=\"outline\" onPress={() => setValue((v) => Math.min(100, v + 20))}>\n + 20\n </Button>\n </View>\n </View>\n );\n}\n"
120
120
  },
121
121
  {
122
122
  "name": "Download",
@@ -146,7 +146,7 @@
146
146
  {
147
147
  "name": "Steps",
148
148
  "title": "Steps",
149
- "code": "import React, { useEffect, useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { Progress } from '@/components/ui/progress';\n\nexport default function Steps() {\n const [step, setStep] = useState(2);\n return (\n <View style={{ gap: 16 }}>\n <Progress\n variant=\"steps\"\n steps={5}\n value={step}\n label={step >= 5 ? 'Profile ready' : 'Set up your profile'}\n showValue\n formatValue={(v, max) => `Step ${Math.min(max, v + 1)} of ${max}`}\n />\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Button size=\"sm\" variant=\"outline\" onPress={() => setStep((s) => Math.max(0, s - 1))}>\n Back\n </Button>\n <Button size=\"sm\" variant=\"accent\" onPress={() => setStep((s) => Math.min(5, s + 1))}>\n Continue\n </Button>\n </View>\n </View>\n );\n}\n"
149
+ "code": "import React, { useEffect, useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { Progress } from '@/components/ui/progress';\n\nexport default function Steps() {\n const [step, setStep] = useState(2);\n return (\n <View style={{ gap: 16 }}>\n <Progress\n variant=\"steps\"\n steps={5}\n value={step}\n label={step >= 5 ? 'Profile ready' : 'Set up your profile'}\n showValue\n formatValue={(v, max) => `Step ${Math.min(max, v + 1)} of ${max}`}\n />\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 10 }}>\n <Button size=\"sm\" variant=\"outline\" onPress={() => setStep((s) => Math.max(0, s - 1))}>\n Back\n </Button>\n <Button size=\"sm\" variant=\"accent\" onPress={() => setStep((s) => Math.min(5, s + 1))}>\n Continue\n </Button>\n </View>\n </View>\n );\n}\n"
150
150
  }
151
151
  ]
152
152
  }
@@ -101,7 +101,7 @@
101
101
  {
102
102
  "name": "CardsHorizontal",
103
103
  "title": "Cards Horizontal",
104
- "code": "import { Text } from 'react-native';\nimport { RadioGroup } from '@/components/ui/radio-group';\n\nexport default function CardsHorizontal() {\n return (\n <RadioGroup\n variant=\"card\"\n direction=\"horizontal\"\n defaultValue=\"card\"\n options={[\n { value: 'card', label: 'Card', description: 'Visa ·· 4242', icon: <Text style={{ fontSize: 20 }}>💳</Text> },\n { value: 'wallet', label: 'Wallet', description: 'Apple Pay', icon: <Text style={{ fontSize: 20 }}>📱</Text> },\n ]}\n />\n );\n}\n"
104
+ "code": "import { Text } from 'react-native';\nimport { RadioGroup } from '@/components/ui/radio-group';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nexport default function CardsHorizontal() {\n return (\n <RadioGroup\n variant=\"card\"\n direction=\"horizontal\"\n defaultValue=\"card\"\n options={[\n { value: 'card', label: 'Card', description: 'Visa ·· 4242', icon: <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 20 }}>💳</Text> },\n { value: 'wallet', label: 'Wallet', description: 'Apple Pay', icon: <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 20 }}>📱</Text> },\n ]}\n />\n );\n}\n"
105
105
  },
106
106
  {
107
107
  "name": "GroupedList",
@@ -139,17 +139,17 @@
139
139
  {
140
140
  "name": "LiveResults",
141
141
  "title": "Live Results",
142
- "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { SearchBar, type SearchSuggestion } from '@/components/ui/search-bar';\nimport { alpha, 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 { padding: 16, gap: 12 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : { borderRadius: radius.xl, borderWidth: StyleSheet.hairlineWidth, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst CONTACTS = [\n { name: 'Aarav Mehta', role: 'Design lead' },\n { name: 'Priya Nair', role: 'Product manager' },\n { name: 'Rohan Kapoor', role: 'iOS engineer' },\n { name: 'Sara Ali', role: 'Marketing' },\n { name: 'Vikram Rao', role: 'Backend engineer' },\n];\n\nexport default function LiveResults({ screen }: DemoProps) {\n const { colors } = useKit();\n const [query, setQuery] = useState('');\n const [loading, setLoading] = useState(false);\n useEffect(() => {\n if (!query) return setLoading(false);\n setLoading(true);\n const id = setTimeout(() => setLoading(false), 600);\n return () => clearTimeout(id);\n }, [query]);\n const q = query.trim().toLowerCase();\n const results = CONTACTS.filter((c) => c.name.toLowerCase().includes(q) || c.role.toLowerCase().includes(q));\n return (\n <Card screen={screen}>\n <SearchBar variant=\"filled\" value={query} onChangeText={setQuery} loading={loading} placeholder=\"Search people\" />\n {loading ? (\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>Searching your team…</Text>\n ) : results.length ? (\n results.slice(0, 4).map((c) => (\n <View key={c.name} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ width: 34, height: 34, borderRadius: 17, backgroundColor: alpha(colors.accent, 0.16), alignItems: 'center', justifyContent: 'center' }}>\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{c.name.split(' ').map((p) => p[0]).join('')}</Text>\n </View>\n <View>\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.foreground }}>{c.name}</Text>\n <Text style={{ fontSize: 12.5, color: colors.mutedForeground }}>{c.role}</Text>\n </View>\n </View>\n ))\n ) : (\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>No one matches “{query}”</Text>\n )}\n </Card>\n );\n}\n"
142
+ "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { SearchBar, type SearchSuggestion } from '@/components/ui/search-bar';\nimport { alpha, 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 { padding: 16, gap: 12 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : { borderRadius: radius.xl, borderWidth: StyleSheet.hairlineWidth, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst CONTACTS = [\n { name: 'Aarav Mehta', role: 'Design lead' },\n { name: 'Priya Nair', role: 'Product manager' },\n { name: 'Rohan Kapoor', role: 'iOS engineer' },\n { name: 'Sara Ali', role: 'Marketing' },\n { name: 'Vikram Rao', role: 'Backend engineer' },\n];\n\nexport default function LiveResults({ screen }: DemoProps) {\n const { colors } = useKit();\n const [query, setQuery] = useState('');\n const [loading, setLoading] = useState(false);\n useEffect(() => {\n if (!query) return setLoading(false);\n setLoading(true);\n const id = setTimeout(() => setLoading(false), 600);\n return () => clearTimeout(id);\n }, [query]);\n const q = query.trim().toLowerCase();\n const results = CONTACTS.filter((c) => c.name.toLowerCase().includes(q) || c.role.toLowerCase().includes(q));\n return (\n <Card screen={screen}>\n <SearchBar variant=\"filled\" value={query} onChangeText={setQuery} loading={loading} placeholder=\"Search people\" />\n {loading ? (\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>Searching your team…</Text>\n ) : results.length ? (\n results.slice(0, 4).map((c) => (\n <View key={c.name} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ width: 34, height: 34, borderRadius: 17, backgroundColor: alpha(colors.accent, 0.16), alignItems: 'center', justifyContent: 'center' }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{c.name.split(' ').map((p) => p[0]).join('')}</Text>\n </View>\n <View style={{ flexShrink: 1 }}>\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.foreground }}>{c.name}</Text>\n <Text style={{ fontSize: 12.5, color: colors.mutedForeground }}>{c.role}</Text>\n </View>\n </View>\n ))\n ) : (\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>No one matches “{query}”</Text>\n )}\n </Card>\n );\n}\n"
143
143
  },
144
144
  {
145
145
  "name": "Minimal",
146
146
  "title": "Minimal",
147
- "code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { SearchBar, type SearchSuggestion } from '@/components/ui/search-bar';\nimport { alpha, 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 { padding: 16, gap: 12 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : { borderRadius: radius.xl, borderWidth: StyleSheet.hairlineWidth, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Minimal({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <Text style={{ fontSize: 22, fontWeight: '400', letterSpacing: -0.4, color: colors.foreground }}>Inbox</Text>\n <SearchBar variant=\"minimal\" align=\"right\" placeholder=\"Search mail\" style={{ flex: 1 }} />\n </Card>\n );\n}\n"
147
+ "code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { SearchBar, type SearchSuggestion } from '@/components/ui/search-bar';\nimport { alpha, 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 { padding: 16, gap: 12 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : { borderRadius: radius.xl, borderWidth: StyleSheet.hairlineWidth, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Minimal({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <Text style={{ flexShrink: 1, fontSize: 22, fontWeight: '400', letterSpacing: -0.4, color: colors.foreground }}>Inbox</Text>\n <SearchBar variant=\"minimal\" align=\"right\" placeholder=\"Search mail\" style={{ flex: 1 }} />\n </Card>\n );\n}\n"
148
148
  },
149
149
  {
150
150
  "name": "WithFilter",
151
151
  "title": "With Filter",
152
- "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { SearchBar, type SearchSuggestion } from '@/components/ui/search-bar';\nimport { alpha, useKit } from '@/lib/kit';\n\nfunction FilterButton({ count, onPress }: { count: number; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n onPress={onPress}\n hitSlop={8}\n accessibilityRole=\"button\"\n accessibilityLabel={`Filters, ${count} active`}\n style={{ height: 32, paddingHorizontal: 10, borderRadius: radius.capsule(32), backgroundColor: count ? colors.accent : colors.muted, flexDirection: 'row', alignItems: 'center', gap: 6 }}\n >\n <View style={{ gap: 3, alignItems: 'center' }}>\n {[14, 10, 6].map((w) => (\n <View key={w} style={{ width: w, height: 2, borderRadius: 1, backgroundColor: count ? colors.accentForeground : colors.mutedForeground }} />\n ))}\n </View>\n {count ? <Text style={{ fontSize: 12.5, fontWeight: '400', color: colors.accentForeground }}>{count}</Text> : null}\n </Pressable>\n );\n}\n\nexport default function WithFilter() {\n const { colors } = useKit();\n const [count, setCount] = useState(2);\n return (\n <View style={{ gap: 10 }}>\n <SearchBar\n placeholder=\"Stays in Goa\"\n showCancel={false}\n trailing={<FilterButton count={count} onPress={() => setCount((c) => (c + 1) % 4)} />}\n />\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{count ? `${count} filters · Pool, Under ₹8,000` : 'No filters applied'}</Text>\n </View>\n );\n}\n"
152
+ "code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { SearchBar, type SearchSuggestion } from '@/components/ui/search-bar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\nfunction FilterButton({ count, onPress }: { count: number; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n onPress={onPress}\n hitSlop={8}\n accessibilityRole=\"button\"\n accessibilityLabel={`Filters, ${count} active`}\n style={{ minHeight: 32, paddingHorizontal: 10, borderRadius: radius.capsule(32), backgroundColor: count ? colors.accent : colors.muted, flexDirection: 'row', alignItems: 'center', gap: 6 }}\n >\n <View style={{ gap: 3, alignItems: 'center' }}>\n {[14, 10, 6].map((w) => (\n <View key={w} style={{ width: w, height: 2, borderRadius: 1, backgroundColor: count ? colors.accentForeground : colors.mutedForeground }} />\n ))}\n </View>\n {count ? <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 12.5, fontWeight: '400', color: colors.accentForeground }}>{count}</Text> : null}\n </Pressable>\n );\n}\n\nexport default function WithFilter() {\n const { colors } = useKit();\n const [count, setCount] = useState(2);\n return (\n <View style={{ gap: 10 }}>\n <SearchBar\n placeholder=\"Stays in Goa\"\n showCancel={false}\n trailing={<FilterButton count={count} onPress={() => setCount((c) => (c + 1) % 4)} />}\n />\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{count ? `${count} filters · Pool, Under ₹8,000` : 'No filters applied'}</Text>\n </View>\n );\n}\n"
153
153
  },
154
154
  {
155
155
  "name": "Sizes",
@@ -107,7 +107,7 @@
107
107
  {
108
108
  "name": "WithIcons",
109
109
  "title": "With Icons",
110
- "code": "import { Text, View } from 'react-native';\nimport { SegmentedControl } from '@/components/ui/segmented-control';\n\nexport default function WithIcons() {\n const glyph = (g: string) => ({ color }: { color: string }) => <Text style={{ fontSize: 15, color }}>{g}</Text>;\n return (\n <SegmentedControl\n defaultValue=\"grid\"\n options={[\n { value: 'list', label: 'List', icon: glyph('☰') },\n { value: 'grid', label: 'Grid', icon: glyph('▦') },\n { value: 'map', label: 'Map', icon: glyph('◎') },\n ]}\n />\n );\n}\n"
110
+ "code": "import { Text, View } from 'react-native';\nimport { SegmentedControl } from '@/components/ui/segmented-control';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nexport default function WithIcons() {\n const glyph = (g: string) => ({ color }: { color: string }) => <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color }}>{g}</Text>;\n return (\n <SegmentedControl\n defaultValue=\"grid\"\n options={[\n { value: 'list', label: 'List', icon: glyph('☰') },\n { value: 'grid', label: 'Grid', icon: glyph('▦') },\n { value: 'map', label: 'Map', icon: glyph('◎') },\n ]}\n />\n );\n}\n"
111
111
  },
112
112
  {
113
113
  "name": "Accent",
@@ -241,27 +241,27 @@
241
241
  {
242
242
  "name": "Validation",
243
243
  "title": "Validation",
244
- "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Validation({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n const [size, setSize] = useState<string | null>(null);\n const [touched, setTouched] = useState(false);\n const missing = touched && !size;\n return (\n <Card screen={screen}>\n <Heading title=\"Merino crew sweater\" sub=\"₹3,499 · Oatmeal\" />\n <Select\n label=\"Size\"\n floatingLabel={false}\n placeholder=\"Choose a size\"\n size=\"sm\"\n value={size}\n surface={colors.card}\n onValueChange={(v) => {\n setSize(v);\n setTouched(true);\n }}\n options={[\n { label: 'XS', value: 'XS', disabled: true, description: 'Sold out' },\n { label: 'S', value: 'S', description: 'Chest 34–36\"' },\n { label: 'M', value: 'M', description: 'Chest 38–40\"' },\n { label: 'L', value: 'L', description: 'Chest 42–44\"' },\n { label: 'XL', value: 'XL', description: 'Chest 46–48\"' },\n ]}\n error={missing ? 'Pick a size to add this to your bag' : undefined}\n helper={missing ? undefined : 'Runs true to size · free exchanges'}\n />\n <Pressable\n onPress={() => setTouched(true)}\n accessibilityRole=\"button\"\n style={({ pressed }) => [s.button, { backgroundColor: colors.primary, borderRadius: radius.md, opacity: pressed ? 0.88 : 1 }]}\n >\n <Text style={[s.buttonText, { color: colors.primaryForeground }]}>Add to bag</Text>\n </Pressable>\n </Card>\n );\n}\n"
244
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\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 screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text maxFontSizeMultiplier={FONT_CAP} style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { minHeight: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Validation({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n const [size, setSize] = useState<string | null>(null);\n const [touched, setTouched] = useState(false);\n const missing = touched && !size;\n return (\n <Card screen={screen}>\n <Heading title=\"Merino crew sweater\" sub=\"₹3,499 · Oatmeal\" />\n <Select\n label=\"Size\"\n floatingLabel={false}\n placeholder=\"Choose a size\"\n size=\"sm\"\n value={size}\n surface={colors.card}\n onValueChange={(v) => {\n setSize(v);\n setTouched(true);\n }}\n options={[\n { label: 'XS', value: 'XS', disabled: true, description: 'Sold out' },\n { label: 'S', value: 'S', description: 'Chest 34–36\"' },\n { label: 'M', value: 'M', description: 'Chest 38–40\"' },\n { label: 'L', value: 'L', description: 'Chest 42–44\"' },\n { label: 'XL', value: 'XL', description: 'Chest 46–48\"' },\n ]}\n error={missing ? 'Pick a size to add this to your bag' : undefined}\n helper={missing ? undefined : 'Runs true to size · free exchanges'}\n />\n <Pressable\n onPress={() => setTouched(true)}\n accessibilityRole=\"button\"\n style={({ pressed }) => [s.button, { backgroundColor: colors.primary, borderRadius: radius.md, opacity: pressed ? 0.88 : 1 }]}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.buttonText, { color: colors.primaryForeground }]}>Add to bag</Text>\n </Pressable>\n </Card>\n );\n}\n"
245
245
  },
246
246
  {
247
247
  "name": "Plan",
248
248
  "title": "Plan",
249
- "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport Svg, { Circle, Path } from 'react-native-svg';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\n/** Small icon tile for option rows. */\nfunction Glyph({ d, tone }: { d: string; tone?: 'accent' | 'success' | 'warning' }) {\n const { colors, radius } = useKit();\n const c = tone ? colors[tone] : colors.mutedForeground;\n return (\n <View style={[s.glyph, { borderRadius: radius.scale(8), backgroundColor: tone ? alpha(c, 0.14) : colors.muted }]}>\n <Svg width={15} height={15} viewBox=\"0 0 24 24\">\n <Path d={d} stroke={c} strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" fill=\"none\" />\n </Svg>\n </View>\n );\n}\n\nconst ICON = {\n leaf: 'M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.5 19 2c1 2 2 4.2 2 8 0 5.5-4.8 10-10 10ZM2 21c0-3 1.9-5.4 5.2-6',\n bolt: 'M13 2 3 14h9l-1 8 10-12h-9l1-8Z',\n users: 'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8ZM22 21v-2a4 4 0 0 0-3-3.9M16 3.1a4 4 0 0 1 0 7.8',\n building: 'M6 22V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18ZM6 12H4a2 2 0 0 0-2 2v8h4M18 9h2a2 2 0 0 1 2 2v11h-4M10 6h4M10 10h4M10 14h4M10 18h4',\n};\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Plan({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Workspace plan\" sub=\"Billed monthly · change anytime\" />\n <Select\n label=\"Plan\"\n floatingLabel={false}\n placeholder=\"Select a plan\"\n size=\"sm\"\n defaultValue=\"pro\"\n surface={colors.card}\n helper=\"You can switch plans later from billing.\"\n options={[\n { label: 'Hobby', value: 'hobby', description: 'Free · 1 project', icon: <Glyph d={ICON.leaf} tone=\"success\" /> },\n { label: 'Pro', value: 'pro', description: '₹499 / seat · unlimited projects', icon: <Glyph d={ICON.bolt} tone=\"accent\" /> },\n { label: 'Team', value: 'team', description: '₹1,499 / seat · SSO and audit log', icon: <Glyph d={ICON.users} tone=\"warning\" /> },\n { label: 'Enterprise', value: 'enterprise', description: 'Talk to sales', icon: <Glyph d={ICON.building} />, disabled: true },\n ]}\n />\n </Card>\n );\n}\n"
249
+ "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport Svg, { Circle, Path } from 'react-native-svg';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\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 screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\n/** Small icon tile for option rows. */\nfunction Glyph({ d, tone }: { d: string; tone?: 'accent' | 'success' | 'warning' }) {\n const { colors, radius } = useKit();\n const c = tone ? colors[tone] : colors.mutedForeground;\n return (\n <View style={[s.glyph, { borderRadius: radius.scale(8), backgroundColor: tone ? alpha(c, 0.14) : colors.muted }]}>\n <Svg width={15} height={15} viewBox=\"0 0 24 24\">\n <Path d={d} stroke={c} strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" fill=\"none\" />\n </Svg>\n </View>\n );\n}\n\nconst ICON = {\n leaf: 'M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.5 19 2c1 2 2 4.2 2 8 0 5.5-4.8 10-10 10ZM2 21c0-3 1.9-5.4 5.2-6',\n bolt: 'M13 2 3 14h9l-1 8 10-12h-9l1-8Z',\n users: 'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8ZM22 21v-2a4 4 0 0 0-3-3.9M16 3.1a4 4 0 0 1 0 7.8',\n building: 'M6 22V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18ZM6 12H4a2 2 0 0 0-2 2v8h4M18 9h2a2 2 0 0 1 2 2v11h-4M10 6h4M10 10h4M10 14h4M10 18h4',\n};\n\nconst flag = (f: string) => <Text maxFontSizeMultiplier={FONT_CAP} style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { minHeight: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Plan({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Workspace plan\" sub=\"Billed monthly · change anytime\" />\n <Select\n label=\"Plan\"\n floatingLabel={false}\n placeholder=\"Select a plan\"\n size=\"sm\"\n defaultValue=\"pro\"\n surface={colors.card}\n helper=\"You can switch plans later from billing.\"\n options={[\n { label: 'Hobby', value: 'hobby', description: 'Free · 1 project', icon: <Glyph d={ICON.leaf} tone=\"success\" /> },\n { label: 'Pro', value: 'pro', description: '₹499 / seat · unlimited projects', icon: <Glyph d={ICON.bolt} tone=\"accent\" /> },\n { label: 'Team', value: 'team', description: '₹1,499 / seat · SSO and audit log', icon: <Glyph d={ICON.users} tone=\"warning\" /> },\n { label: 'Enterprise', value: 'enterprise', description: 'Talk to sales', icon: <Glyph d={ICON.building} />, disabled: true },\n ]}\n />\n </Card>\n );\n}\n"
250
250
  },
251
251
  {
252
252
  "name": "Country",
253
253
  "title": "Country",
254
- "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Country({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Shipping address\" sub=\"Where should we send this order?\" />\n <Select\n searchable\n label=\"Country\"\n floatingLabel={false}\n placeholder=\"Select country\"\n searchPlaceholder=\"Search countries\"\n size=\"sm\"\n defaultValue=\"DE\"\n surface={colors.card}\n options={[\n { label: 'United States', value: 'US', icon: flag('🇺🇸'), group: 'Americas' },\n { label: 'Canada', value: 'CA', icon: flag('🇨🇦'), group: 'Americas' },\n { label: 'Brazil', value: 'BR', icon: flag('🇧🇷'), group: 'Americas' },\n { label: 'United Kingdom', value: 'GB', icon: flag('🇬🇧'), group: 'Europe' },\n { label: 'Germany', value: 'DE', icon: flag('🇩🇪'), group: 'Europe' },\n { label: 'France', value: 'FR', icon: flag('🇫🇷'), group: 'Europe' },\n { label: 'India', value: 'IN', icon: flag('🇮🇳'), group: 'Asia Pacific' },\n { label: 'Japan', value: 'JP', icon: flag('🇯🇵'), group: 'Asia Pacific' },\n { label: 'Australia', value: 'AU', icon: flag('🇦🇺'), group: 'Asia Pacific' },\n ]}\n />\n </Card>\n );\n}\n"
254
+ "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\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 screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text maxFontSizeMultiplier={FONT_CAP} style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { minHeight: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Country({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Shipping address\" sub=\"Where should we send this order?\" />\n <Select\n searchable\n label=\"Country\"\n floatingLabel={false}\n placeholder=\"Select country\"\n searchPlaceholder=\"Search countries\"\n size=\"sm\"\n defaultValue=\"DE\"\n surface={colors.card}\n options={[\n { label: 'United States', value: 'US', icon: flag('🇺🇸'), group: 'Americas' },\n { label: 'Canada', value: 'CA', icon: flag('🇨🇦'), group: 'Americas' },\n { label: 'Brazil', value: 'BR', icon: flag('🇧🇷'), group: 'Americas' },\n { label: 'United Kingdom', value: 'GB', icon: flag('🇬🇧'), group: 'Europe' },\n { label: 'Germany', value: 'DE', icon: flag('🇩🇪'), group: 'Europe' },\n { label: 'France', value: 'FR', icon: flag('🇫🇷'), group: 'Europe' },\n { label: 'India', value: 'IN', icon: flag('🇮🇳'), group: 'Asia Pacific' },\n { label: 'Japan', value: 'JP', icon: flag('🇯🇵'), group: 'Asia Pacific' },\n { label: 'Australia', value: 'AU', icon: flag('🇦🇺'), group: 'Asia Pacific' },\n ]}\n />\n </Card>\n );\n}\n"
255
255
  },
256
256
  {
257
257
  "name": "Assignees",
258
258
  "title": "Assignees",
259
- "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Assignees({ screen }: DemoProps) {\n const { colors } = useKit();\n const [people, setPeople] = useState(['maya', 'leo']);\n return (\n <Card screen={screen}>\n <Heading title=\"Fix checkout crash on Android 14\" sub=\"ENG-2142 · In progress\" />\n <Select\n multiple\n searchable\n label=\"Assignees\"\n floatingLabel={false}\n placeholder=\"Add teammates\"\n searchPlaceholder=\"Search teammates\"\n size=\"sm\"\n values={people}\n onValuesChange={setPeople}\n surface={colors.card}\n options={[\n { label: 'Maya Chen', value: 'maya', description: 'Design', icon: avatar('MC', '#f472b6') },\n { label: 'Leo Martins', value: 'leo', description: 'iOS', icon: avatar('LM', '#60a5fa') },\n { label: 'Ana Ruiz', value: 'ana', description: 'Android', icon: avatar('AR', '#34d399') },\n { label: 'Sam Okafor', value: 'sam', description: 'Backend', icon: avatar('SO', '#fbbf24') },\n { label: 'Priya Nair', value: 'priya', description: 'QA', icon: avatar('PN', '#a78bfa') },\n ]}\n />\n </Card>\n );\n}\n"
259
+ "code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\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 screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text maxFontSizeMultiplier={FONT_CAP} style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { minHeight: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Assignees({ screen }: DemoProps) {\n const { colors } = useKit();\n const [people, setPeople] = useState(['maya', 'leo']);\n return (\n <Card screen={screen}>\n <Heading title=\"Fix checkout crash on Android 14\" sub=\"ENG-2142 · In progress\" />\n <Select\n multiple\n searchable\n label=\"Assignees\"\n floatingLabel={false}\n placeholder=\"Add teammates\"\n searchPlaceholder=\"Search teammates\"\n size=\"sm\"\n values={people}\n onValuesChange={setPeople}\n surface={colors.card}\n options={[\n { label: 'Maya Chen', value: 'maya', description: 'Design', icon: avatar('MC', '#f472b6') },\n { label: 'Leo Martins', value: 'leo', description: 'iOS', icon: avatar('LM', '#60a5fa') },\n { label: 'Ana Ruiz', value: 'ana', description: 'Android', icon: avatar('AR', '#34d399') },\n { label: 'Sam Okafor', value: 'sam', description: 'Backend', icon: avatar('SO', '#fbbf24') },\n { label: 'Priya Nair', value: 'priya', description: 'QA', icon: avatar('PN', '#a78bfa') },\n ]}\n />\n </Card>\n );\n}\n"
260
260
  },
261
261
  {
262
262
  "name": "Sheet",
263
263
  "title": "Sheet",
264
- "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Sheet({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Notifications\" sub=\"Opens as a sheet instead of a dropdown\" />\n <Select\n presentation=\"sheet\"\n label=\"Email digest\"\n floatingLabel={false}\n placeholder=\"How often?\"\n title=\"How often?\"\n size=\"sm\"\n defaultValue=\"weekly\"\n surface={colors.card}\n options={[\n { label: 'Daily', value: 'daily', description: 'Every morning at 8:00' },\n { label: 'Weekly', value: 'weekly', description: 'Mondays at 8:00' },\n { label: 'Monthly', value: 'monthly', description: 'First of the month' },\n { label: 'Never', value: 'never', description: 'Only security alerts' },\n ]}\n />\n </Card>\n );\n}\n"
264
+ "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\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 screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text maxFontSizeMultiplier={FONT_CAP} style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { minHeight: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Sheet({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Notifications\" sub=\"Opens as a sheet instead of a dropdown\" />\n <Select\n presentation=\"sheet\"\n label=\"Email digest\"\n floatingLabel={false}\n placeholder=\"How often?\"\n title=\"How often?\"\n size=\"sm\"\n defaultValue=\"weekly\"\n surface={colors.card}\n options={[\n { label: 'Daily', value: 'daily', description: 'Every morning at 8:00' },\n { label: 'Weekly', value: 'weekly', description: 'Mondays at 8:00' },\n { label: 'Monthly', value: 'monthly', description: 'First of the month' },\n { label: 'Never', value: 'never', description: 'Only security alerts' },\n ]}\n />\n </Card>\n );\n}\n"
265
265
  },
266
266
  {
267
267
  "name": "Sizes",
@@ -271,7 +271,7 @@
271
271
  {
272
272
  "name": "Colors",
273
273
  "title": "Colors",
274
- "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport Svg, { Circle, Path } from 'react-native-svg';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nfunction Dot({ color }: { color: string }) {\n return (\n <Svg width={14} height={14} viewBox=\"0 0 14 14\">\n <Circle cx={7} cy={7} r={5} fill={color} />\n </Svg>\n );\n}\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { height: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Colors({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Project labels\" sub=\"Accent colour drives focus, checks and chips\" />\n <Select\n multiple\n label=\"Labels\"\n floatingLabel={false}\n placeholder=\"Add labels\"\n size=\"sm\"\n accent={colors.success}\n defaultValues={['frontend', 'perf']}\n surface={colors.card}\n options={[\n { label: 'Frontend', value: 'frontend' },\n { label: 'Performance', value: 'perf' },\n { label: 'Accessibility', value: 'a11y' },\n { label: 'Docs', value: 'docs' },\n ]}\n />\n <Select\n label=\"Priority\"\n floatingLabel={false}\n placeholder=\"Set priority\"\n size=\"sm\"\n accent={colors.warning}\n defaultValue=\"p1\"\n surface={colors.card}\n options={[\n { label: 'Urgent', value: 'p0', icon: <Dot color={colors.danger} /> },\n { label: 'High', value: 'p1', icon: <Dot color={colors.warning} /> },\n { label: 'Medium', value: 'p2', icon: <Dot color={colors.accent} /> },\n { label: 'Low', value: 'p3', icon: <Dot color={colors.mutedForeground} /> },\n ]}\n />\n </Card>\n );\n}\n"
274
+ "code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport Svg, { Circle, Path } from 'react-native-svg';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\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 screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text maxFontSizeMultiplier={FONT_CAP} style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nfunction Dot({ color }: { color: string }) {\n return (\n <Svg width={14} height={14} viewBox=\"0 0 14 14\">\n <Circle cx={7} cy={7} r={5} fill={color} />\n </Svg>\n );\n}\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { minHeight: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Colors({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Project labels\" sub=\"Accent colour drives focus, checks and chips\" />\n <Select\n multiple\n label=\"Labels\"\n floatingLabel={false}\n placeholder=\"Add labels\"\n size=\"sm\"\n accent={colors.success}\n defaultValues={['frontend', 'perf']}\n surface={colors.card}\n options={[\n { label: 'Frontend', value: 'frontend' },\n { label: 'Performance', value: 'perf' },\n { label: 'Accessibility', value: 'a11y' },\n { label: 'Docs', value: 'docs' },\n ]}\n />\n <Select\n label=\"Priority\"\n floatingLabel={false}\n placeholder=\"Set priority\"\n size=\"sm\"\n accent={colors.warning}\n defaultValue=\"p1\"\n surface={colors.card}\n options={[\n { label: 'Urgent', value: 'p0', icon: <Dot color={colors.danger} /> },\n { label: 'High', value: 'p1', icon: <Dot color={colors.warning} /> },\n { label: 'Medium', value: 'p2', icon: <Dot color={colors.accent} /> },\n { label: 'Low', value: 'p3', icon: <Dot color={colors.mutedForeground} /> },\n ]}\n />\n </Card>\n );\n}\n"
275
275
  }
276
276
  ]
277
277
  }
@@ -141,7 +141,7 @@
141
141
  {
142
142
  "name": "LoadingSwap",
143
143
  "title": "Loading Swap",
144
- "code": "import React, { useEffect, useRef, useState } from 'react';\nimport { Text, View, type DimensionValue } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { Skeleton } from '@/components/ui/skeleton';\nimport { useKit } from '@/lib/kit';\n\nexport default function LoadingSwap() {\n const { colors, radius, text } = useKit();\n const [loading, setLoading] = useState(true);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const reload = () => {\n setLoading(true);\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => setLoading(false), 1600);\n };\n useEffect(() => {\n timer.current = setTimeout(() => setLoading(false), 1600);\n return () => {\n if (timer.current) clearTimeout(timer.current);\n };\n }, []);\n return (\n <View style={{ gap: 16 }}>\n <Skeleton.Reveal\n loading={loading}\n skeleton={\n <View style={{ gap: 12 }}>\n <Skeleton.List rows={1} trailing={false} />\n <Skeleton.Text lines={2} />\n </View>\n }\n >\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12, paddingVertical: 10 }}>\n <View style={{ width: 44, height: 44, borderRadius: radius.full, backgroundColor: colors.accent, alignItems: 'center', justifyContent: 'center' }}>\n <Text style={{ color: colors.accentForeground, fontSize: 16, fontWeight: '400' }}>MC</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text style={[text.md, { color: colors.foreground, fontWeight: '400' }]}>Maya Chen</Text>\n <Text style={[text.sm, { color: colors.mutedForeground, fontWeight: '400' }]}>Product designer · Lisbon</Text>\n </View>\n </View>\n <Text style={[text.sm, { color: colors.foreground, fontWeight: '400', marginTop: 4 }]}>\n Shipped the new onboarding flow today. Activation is up 18% in the first week — thanks to everyone who tested the beta.\n </Text>\n <Text style={[text.xs, { color: colors.mutedForeground, fontWeight: '400', marginTop: 10 }]}>\n 124 likes · 18 comments\n </Text>\n </Skeleton.Reveal>\n <Button size=\"sm\" variant=\"outline\" style={{ alignSelf: 'flex-start' }} disabled={loading} onPress={reload}>\n {loading ? 'Loading…' : 'Reload'}\n </Button>\n </View>\n );\n}\n"
144
+ "code": "import React, { useEffect, useRef, useState } from 'react';\nimport { Text, View, type DimensionValue } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { Skeleton } from '@/components/ui/skeleton';\nimport { useKit } from '@/lib/kit';\n\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\nexport default function LoadingSwap() {\n const { colors, radius, text } = useKit();\n const [loading, setLoading] = useState(true);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const reload = () => {\n setLoading(true);\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => setLoading(false), 1600);\n };\n useEffect(() => {\n timer.current = setTimeout(() => setLoading(false), 1600);\n return () => {\n if (timer.current) clearTimeout(timer.current);\n };\n }, []);\n return (\n <View style={{ gap: 16 }}>\n <Skeleton.Reveal\n loading={loading}\n skeleton={\n <View style={{ gap: 12 }}>\n <Skeleton.List rows={1} trailing={false} />\n <Skeleton.Text lines={2} />\n </View>\n }\n >\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12, paddingVertical: 10 }}>\n <View style={{ width: 44, height: 44, borderRadius: radius.full, backgroundColor: colors.accent, alignItems: 'center', justifyContent: 'center' }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.accentForeground, fontSize: 16, fontWeight: '400' }}>MC</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text style={[text.md, { color: colors.foreground, fontWeight: '400' }]}>Maya Chen</Text>\n <Text style={[text.sm, { color: colors.mutedForeground, fontWeight: '400' }]}>Product designer · Lisbon</Text>\n </View>\n </View>\n <Text style={[text.sm, { color: colors.foreground, fontWeight: '400', marginTop: 4 }]}>\n Shipped the new onboarding flow today. Activation is up 18% in the first week — thanks to everyone who tested the beta.\n </Text>\n <Text style={[text.xs, { color: colors.mutedForeground, fontWeight: '400', marginTop: 10 }]}>\n 124 likes · 18 comments\n </Text>\n </Skeleton.Reveal>\n <Button size=\"sm\" variant=\"outline\" style={{ alignSelf: 'flex-start' }} disabled={loading} onPress={reload}>\n {loading ? 'Loading…' : 'Reload'}\n </Button>\n </View>\n );\n}\n"
145
145
  },
146
146
  {
147
147
  "name": "Wave",