@expokit/cli 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/README.md +8 -4
  2. package/bin/expo-kit.js +137 -24
  3. package/bin/lib.mjs +110 -1
  4. package/bin/mcp.mjs +40 -18
  5. package/package.json +2 -2
  6. package/registry/_docs/accordion.json +1 -1
  7. package/registry/_docs/assistant-app.json +47 -0
  8. package/registry/_docs/avatar.json +6 -6
  9. package/registry/_docs/badge.json +4 -4
  10. package/registry/_docs/bottom-nav.json +6 -6
  11. package/registry/_docs/bottom-sheet.json +3 -3
  12. package/registry/_docs/bullet.json +6 -0
  13. package/registry/_docs/button-3d.json +3 -3
  14. package/registry/_docs/button.json +2 -2
  15. package/registry/_docs/calendar.json +5 -5
  16. package/registry/_docs/card.json +1 -1
  17. package/registry/_docs/checkout.json +7 -1
  18. package/registry/_docs/chip-group.json +1 -1
  19. package/registry/_docs/confetti.json +3 -3
  20. package/registry/_docs/date-picker.json +4 -4
  21. package/registry/_docs/donut-shop.json +88 -0
  22. package/registry/_docs/drawer.json +4 -4
  23. package/registry/_docs/fintech-app.json +49 -0
  24. package/registry/_docs/flip-card.json +4 -4
  25. package/registry/_docs/floating-input.json +5 -5
  26. package/registry/_docs/food-app.json +47 -0
  27. package/registry/_docs/glass.json +1 -1
  28. package/registry/_docs/grain.json +80 -0
  29. package/registry/_docs/isometric-bar.json +6 -0
  30. package/registry/_docs/liquidglass-tab.json +1 -1
  31. package/registry/_docs/morphing-button-group.json +96 -0
  32. package/registry/_docs/orb-reveal.json +132 -0
  33. package/registry/_docs/otp-input.json +4 -4
  34. package/registry/_docs/password-input.json +3 -3
  35. package/registry/_docs/payment-card.json +3 -3
  36. package/registry/_docs/pixel-bar.json +6 -0
  37. package/registry/_docs/polar-area.json +6 -0
  38. package/registry/_docs/progress-steps.json +4 -4
  39. package/registry/_docs/progress.json +2 -2
  40. package/registry/_docs/qr-code-button.json +122 -0
  41. package/registry/_docs/radio-group.json +1 -1
  42. package/registry/_docs/search-bar.json +4 -4
  43. package/registry/_docs/segmented-control.json +1 -1
  44. package/registry/_docs/select.json +7 -7
  45. package/registry/_docs/settings.json +6 -0
  46. package/registry/_docs/skeleton.json +3 -3
  47. package/registry/_docs/slider.json +6 -6
  48. package/registry/_docs/spinner.json +3 -3
  49. package/registry/_docs/stateful-button.json +1 -1
  50. package/registry/_docs/stepper.json +7 -7
  51. package/registry/_docs/tabs.json +2 -2
  52. package/registry/_docs/text-animation.json +4 -4
  53. package/registry/_docs/textarea.json +3 -3
  54. package/registry/_docs/ticket-shape.json +1 -1
  55. package/registry/_docs/tilt-card.json +4 -4
  56. package/registry/_docs/time-picker.json +6 -6
  57. package/registry/_docs/timeline-chart.json +6 -0
  58. package/registry/_docs/timeline.json +6 -6
  59. package/registry/_docs/toast.json +8 -8
  60. package/registry/_docs/treemap.json +6 -0
  61. package/registry/_docs/waterfall.json +6 -0
  62. package/registry/accordion/files/src/components/ui/accordion.tsx +36 -7
  63. package/registry/accordion/files/src/lib/kit.tsx +8 -5
  64. package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +12 -8
  65. package/registry/action-sheet/files/src/lib/kit.tsx +8 -5
  66. package/registry/alert/files/src/components/ui/alert.tsx +11 -5
  67. package/registry/alert/files/src/components/ui/button.tsx +35 -6
  68. package/registry/alert/files/src/lib/kit.tsx +8 -5
  69. package/registry/assistant-app.json +49 -0
  70. package/registry/avatar/files/src/components/ui/avatar.tsx +7 -4
  71. package/registry/avatar/files/src/lib/kit.tsx +8 -5
  72. package/registry/badge/files/src/components/ui/badge.tsx +23 -9
  73. package/registry/badge/files/src/lib/kit.tsx +8 -5
  74. package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +21 -8
  75. package/registry/bottom-nav/files/src/lib/kit.tsx +8 -5
  76. package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +7 -6
  77. package/registry/bottom-sheet/files/src/lib/kit.tsx +8 -5
  78. package/registry/button/files/src/components/ui/button.tsx +35 -6
  79. package/registry/button/files/src/lib/kit.tsx +8 -5
  80. package/registry/button-3d/files/src/components/ui/button-3d.tsx +35 -7
  81. package/registry/button-3d/files/src/lib/kit.tsx +8 -5
  82. package/registry/calendar/files/src/components/ui/calendar.tsx +68 -19
  83. package/registry/calendar/files/src/lib/kit.tsx +8 -5
  84. package/registry/card/files/src/components/ui/card.tsx +32 -10
  85. package/registry/card/files/src/lib/kit.tsx +8 -5
  86. package/registry/checkbox/files/src/components/ui/checkbox.tsx +31 -8
  87. package/registry/checkbox/files/src/lib/kit.tsx +8 -5
  88. package/registry/chip-group/files/src/components/ui/chip-group.tsx +39 -7
  89. package/registry/chip-group/files/src/lib/kit.tsx +8 -5
  90. package/registry/confetti/files/src/lib/kit.tsx +8 -5
  91. package/registry/coupon/files/src/components/ui/coupon.tsx +55 -28
  92. package/registry/coupon/files/src/components/ui/flip-card.tsx +23 -4
  93. package/registry/coupon/files/src/components/ui/tilt-card.tsx +59 -18
  94. package/registry/coupon/files/src/lib/kit.tsx +8 -5
  95. package/registry/date-picker/files/src/components/ui/calendar.tsx +68 -19
  96. package/registry/date-picker/files/src/components/ui/date-picker.tsx +98 -44
  97. package/registry/date-picker/files/src/lib/kit.tsx +8 -5
  98. package/registry/dialog/files/src/components/ui/dialog.tsx +19 -9
  99. package/registry/dialog/files/src/lib/kit.tsx +8 -5
  100. package/registry/donut-shop.json +18 -0
  101. package/registry/drawer/files/src/components/ui/drawer.tsx +31 -21
  102. package/registry/drawer/files/src/lib/kit.tsx +8 -5
  103. package/registry/fintech-app.json +57 -0
  104. package/registry/flip-card/files/src/components/ui/flip-card.tsx +23 -4
  105. package/registry/flip-card/files/src/lib/kit.tsx +8 -5
  106. package/registry/floating-input/files/src/components/ui/floating-input.tsx +91 -32
  107. package/registry/floating-input/files/src/lib/kit.tsx +8 -5
  108. package/registry/food-app.json +55 -0
  109. package/registry/grain/files/src/components/ui/grain.tsx +113 -0
  110. package/registry/{tag-input → grain}/files/src/lib/kit.tsx +8 -5
  111. package/registry/grain.json +17 -0
  112. package/registry/kit/files/src/lib/kit.tsx +8 -5
  113. package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +2 -2
  114. package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +1 -1
  115. package/registry/list-item/files/src/components/ui/list-item.tsx +28 -17
  116. package/registry/list-item/files/src/lib/kit.tsx +8 -5
  117. package/registry/morphing-button-group.json +18 -0
  118. package/registry/orb-reveal.json +20 -0
  119. package/registry/otp-input/files/src/components/ui/otp-input.tsx +57 -24
  120. package/registry/otp-input/files/src/lib/kit.tsx +8 -5
  121. package/registry/password-input/files/src/components/ui/floating-input.tsx +91 -32
  122. package/registry/password-input/files/src/components/ui/password-input.tsx +55 -21
  123. package/registry/password-input/files/src/lib/kit.tsx +8 -5
  124. package/registry/payment-card/files/src/components/ui/flip-card.tsx +23 -4
  125. package/registry/payment-card/files/src/components/ui/payment-card.tsx +35 -18
  126. package/registry/payment-card/files/src/components/ui/tilt-card.tsx +59 -18
  127. package/registry/payment-card/files/src/lib/kit.tsx +8 -5
  128. package/registry/progress/files/src/components/ui/progress.tsx +16 -6
  129. package/registry/progress/files/src/lib/kit.tsx +8 -5
  130. package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +22 -11
  131. package/registry/progress-steps/files/src/lib/kit.tsx +8 -5
  132. package/registry/qr-code-button.json +18 -0
  133. package/registry/radio-group/files/src/components/ui/radio-group.tsx +48 -17
  134. package/registry/radio-group/files/src/lib/kit.tsx +8 -5
  135. package/registry/search-bar/files/src/components/ui/search-bar.tsx +81 -26
  136. package/registry/search-bar/files/src/lib/kit.tsx +8 -5
  137. package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +40 -10
  138. package/registry/segmented-control/files/src/lib/kit.tsx +8 -5
  139. package/registry/select/files/src/components/ui/select.tsx +161 -86
  140. package/registry/select/files/src/lib/kit.tsx +8 -5
  141. package/registry/skeleton/files/src/components/ui/skeleton.tsx +4 -4
  142. package/registry/skeleton/files/src/lib/kit.tsx +8 -5
  143. package/registry/slider/files/src/components/ui/slider.tsx +56 -27
  144. package/registry/slider/files/src/lib/kit.tsx +8 -5
  145. package/registry/spinner/files/src/components/ui/spinner.tsx +17 -12
  146. package/registry/spinner/files/src/lib/kit.tsx +8 -5
  147. package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +12 -4
  148. package/registry/stateful-button/files/src/lib/kit.tsx +8 -5
  149. package/registry/stepper/files/src/components/ui/stepper.tsx +83 -29
  150. package/registry/stepper/files/src/lib/kit.tsx +8 -5
  151. package/registry/switch/files/src/components/ui/switch.tsx +60 -22
  152. package/registry/switch/files/src/lib/kit.tsx +8 -5
  153. package/registry/tabs/files/src/components/ui/tabs.tsx +15 -10
  154. package/registry/tabs/files/src/lib/kit.tsx +8 -5
  155. package/registry/text-animation/files/src/components/ui/text-animation.tsx +11 -6
  156. package/registry/text-animation/files/src/lib/kit.tsx +8 -5
  157. package/registry/textarea/files/src/components/ui/textarea.tsx +115 -45
  158. package/registry/textarea/files/src/lib/kit.tsx +8 -5
  159. package/registry/ticket-shape/files/src/lib/kit.tsx +8 -5
  160. package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +59 -18
  161. package/registry/tilt-card/files/src/lib/kit.tsx +8 -5
  162. package/registry/time-picker/files/src/components/ui/time-picker.tsx +156 -75
  163. package/registry/time-picker/files/src/lib/kit.tsx +8 -5
  164. package/registry/timeline/files/src/components/ui/avatar.tsx +7 -4
  165. package/registry/timeline/files/src/components/ui/timeline.tsx +19 -12
  166. package/registry/timeline/files/src/lib/kit.tsx +8 -5
  167. package/registry/toast/files/src/components/ui/button.tsx +35 -6
  168. package/registry/toast/files/src/components/ui/toast.tsx +25 -14
  169. package/registry/toast/files/src/lib/kit.tsx +8 -5
  170. package/schema/components.schema.json +1 -1
  171. package/registry/_docs/color-swatch.json +0 -82
  172. package/registry/_docs/kpi-card.json +0 -14
  173. package/registry/_docs/like-button.json +0 -92
  174. package/registry/_docs/morph-button.json +0 -128
  175. package/registry/_docs/pagination-dots.json +0 -108
  176. package/registry/_docs/shimmer-button.json +0 -98
  177. package/registry/_docs/tag-input.json +0 -148
  178. package/registry/color-swatch.json +0 -17
  179. package/registry/kpi-card/files/src/components/charts/kpi-card.tsx +0 -45
  180. package/registry/kpi-card.json +0 -14
  181. package/registry/like-button.json +0 -18
  182. package/registry/morph-button.json +0 -18
  183. package/registry/pagination-dots/files/src/components/ui/pagination-dots.tsx +0 -262
  184. package/registry/pagination-dots/files/src/lib/kit.tsx +0 -219
  185. package/registry/pagination-dots.json +0 -15
  186. package/registry/shimmer-button.json +0 -17
  187. package/registry/tag-input/files/src/components/ui/tag-input.tsx +0 -720
  188. package/registry/tag-input.json +0 -17
@@ -0,0 +1,113 @@
1
+ import React, { useId } from 'react';
2
+ import { Image, StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';
3
+ import Svg, { Circle, Defs, LinearGradient, RadialGradient, Rect, Stop } from 'react-native-svg';
4
+
5
+ export type GrainPalette =
6
+ | 'sunset'
7
+ | 'ocean'
8
+ | 'aurora'
9
+ | 'dusk'
10
+ | 'peach'
11
+ | 'graphite'
12
+ | 'ember'
13
+ | 'mint'
14
+ | 'lavender'
15
+ | 'candy'
16
+ | 'forest'
17
+ | 'noir';
18
+
19
+ export interface GrainGradientProps {
20
+ /** A built-in colour set. */
21
+ palette?: GrainPalette;
22
+ /** Your own colours: the first is the base, the rest become soft blobs (up to 4). Overrides `palette`. */
23
+ colors?: string[];
24
+ /** Strength of the film grain, 0–1. */
25
+ grain?: number;
26
+ /** Shuffle where the blobs sit. */
27
+ seed?: number;
28
+ style?: StyleProp<ViewStyle>;
29
+ children?: React.ReactNode;
30
+ }
31
+
32
+ export interface GrainOverlayProps {
33
+ /** Strength of the grain, 0–1. */
34
+ grain?: number;
35
+ style?: StyleProp<ViewStyle>;
36
+ }
37
+
38
+ const PALETTES: Record<GrainPalette, string[]> = {
39
+ sunset: ['#2a1238', '#ff6a3d', '#ff3d8b', '#ffc15e', '#7b2ff7'],
40
+ ocean: ['#04203a', '#0fb5c9', '#2f6bff', '#7ce7d6', '#123f8f'],
41
+ aurora: ['#0b1324', '#2ee6a6', '#7b5cff', '#1fa2ff', '#b8ff6b'],
42
+ dusk: ['#1a1433', '#ff7aa8', '#6d5dfc', '#ffb38a', '#2b2a6b'],
43
+ peach: ['#ffe9dc', '#ffb48a', '#ff8fab', '#ffd6a5', '#c7b8ff'],
44
+ graphite: ['#16171b', '#3b3f4a', '#6b6f7d', '#2a2d35', '#8a8f9e'],
45
+ ember: ['#1c0806', '#ff4e1f', '#ff9a3c', '#b3122e', '#ffd36e'],
46
+ mint: ['#e6fff6', '#7ff0c8', '#58c7ff', '#c4ffde', '#9bf2a8'],
47
+ lavender: ['#efe9ff', '#b9a2ff', '#ff9ed8', '#a8c8ff', '#e0c3ff'],
48
+ candy: ['#2b0a3d', '#ff5fa2', '#4dd8ff', '#ffe066', '#a05bff'],
49
+ forest: ['#071a12', '#1f8a5b', '#7ccf6b', '#0f5c4a', '#d4e66b'],
50
+ noir: ['#050506', '#2b2b33', '#5b4bff', '#121216', '#ff3d6e'],
51
+ };
52
+
53
+ /** Every built-in palette, in order. */
54
+ export const GRAIN_PALETTES = Object.keys(PALETTES) as GrainPalette[];
55
+
56
+ // Where the blobs sit, in percent of the box: [cx, cy, r].
57
+ const SPOTS: [number, number, number][][] = [
58
+ [[18, 22, 62], [86, 30, 58], [30, 88, 64], [82, 86, 52]],
59
+ [[80, 16, 60], [14, 52, 58], [62, 70, 66], [24, 96, 48]],
60
+ [[50, 8, 64], [8, 78, 60], [92, 62, 58], [46, 50, 40]],
61
+ ];
62
+
63
+ // 128×128 tiling noise (grey speckle with varying alpha), drawn at 2× so each speck is one device pixel or so.
64
+ const NOISE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAQAAABpN6lAAAAyMklEQVR42k2dwZXdOAwEeVYADEABMACeFBpDY2a0UNX647fPa3s88yWRINBoNKDWrtWf3mab+2rX08/7p2fuvue6nrHup6+rjXav9+97vn+a75/mc7X368+1730/93r/fPrpz9j34etrvP+fq++x+/v589xnnFF/8ir1SXu+15jtOvd6v/95r/F+xrXm+zn3+xPv19/vfT+7vV+t69ZPPfd57+29xljv57X+Xuk67/0+9anvd7/fdde/13ft8X7l/b49Wv3+XpXnen/2qSe53k9977jtunTfPMp7S3Xx9yPfj7reW34/+Fz8qhuom3p/9Mx3We66UD1me7+r1eO/X3kX5X7G+7DvIr4XrM94v/L+6mvu92/16/1zr0d/byqPu3urJaiHeb/+3uZ7N3x6fx97vH+7699qM2obamPau0Dv/9/PXHX1UYu+ayHeJauNen+9D75qyUYt/nu1u676/undkDPqSqfutrb+XbnxPtb7IfWPtWavFdTjXmUF78O+l9isJ4/xPnSrPb9Prej7va12fby3/O7oe0tlK6N+7v2393K1A0/dRC1cffbAOuqh6hbrtt9Hr8V4l5lHPzzw+1hlVbUko/7ltQ7/dtfS/n3u+yDvDp+yk5v7rc+869qrbLasmTt7sINV+88TrfqZd0nfn+NCdel331wVDbpW+P2B+v+oh29lkKNu+F2g9/veh63HfG+wFmBhHXVcytBax8CxiNrNw4OVbb1fqX3A3k4t17t/tWetHrkWlYNSC1rfUVa0y9jfhXpv9soS9+OSl22NusP3uhzhd9PKUrjjp7a0lw0vrrA4PHVXLNpd21zHrtW+vz9WDz/4kDohZWqsd12yaYyvRZTxvTv8rvWjsdYxqZPZ63brUepnz41nwCreW60bv/ExZbi9fr17Ub6kTH/UgtWiPDzwxj/U76uWppaW4/d+Tq9db/7se7/4m7r6xVLX9TrGXxZQnzPxbnWIu16l1UOzCR6hOrLv870Pxv69j1Entx601r8MpswHu9j+GnWma21xLHXD72Vf863bfc8zFlQ3yUOfWpYy3bucW1lLubWyifcs4wyf2jusq8wVl1Q3XSZdOzQbJ1pnXP/6Pk797HtvuEO3rP7DwdYG1IPxu15m4Kuug3fAj9QSvE51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';
65
+
66
+ /** Film grain to lay over anything: a photo, a gradient, a flat colour. Fills its parent; never takes touches. */
67
+ export function GrainOverlay({ grain = 0.55, style }: GrainOverlayProps) {
68
+ return (
69
+ <View pointerEvents="none" style={[StyleSheet.absoluteFill, { opacity: Math.max(0, Math.min(1, grain)) }, style]}>
70
+ <Image source={{ uri: NOISE, scale: 2 }} alt="" resizeMode="repeat" style={StyleSheet.absoluteFill} accessibilityIgnoresInvertColors />
71
+ </View>
72
+ );
73
+ }
74
+
75
+ /**
76
+ * A soft mesh-style gradient — a base colour with blurred colour blobs — finished with real film
77
+ * grain. Use it as a screen or card background, or behind glass so there is something to refract.
78
+ * Pure react-native-svg plus a tiny tiling noise image: no native setup.
79
+ */
80
+ export function GrainGradient({ palette = 'sunset', colors, grain = 0.55, seed = 0, style, children }: GrainGradientProps) {
81
+ const id = useId().replace(/[^a-zA-Z0-9]/g, '');
82
+ const [base, ...blobs] = colors && colors.length ? colors : PALETTES[palette];
83
+ const spots = SPOTS[Math.abs(seed) % SPOTS.length];
84
+ return (
85
+ <View style={[styles.root, { backgroundColor: base }, style]}>
86
+ <Svg style={StyleSheet.absoluteFill} viewBox="0 0 100 100" preserveAspectRatio="none" pointerEvents="none">
87
+ <Defs>
88
+ <LinearGradient id={`base${id}`} x1="0" y1="0" x2="1" y2="1">
89
+ <Stop offset="0" stopColor={base} stopOpacity={1} />
90
+ <Stop offset="1" stopColor={blobs[blobs.length - 1] ?? base} stopOpacity={0.35} />
91
+ </LinearGradient>
92
+ {blobs.slice(0, 4).map((c, i) => (
93
+ <RadialGradient key={i} id={`b${i}${id}`} cx="50%" cy="50%" r="50%">
94
+ <Stop offset="0" stopColor={c} stopOpacity={0.95} />
95
+ <Stop offset="0.45" stopColor={c} stopOpacity={0.55} />
96
+ <Stop offset="1" stopColor={c} stopOpacity={0} />
97
+ </RadialGradient>
98
+ ))}
99
+ </Defs>
100
+ <Rect x={0} y={0} width={100} height={100} fill={`url(#base${id})`} />
101
+ {blobs.slice(0, 4).map((_, i) => (
102
+ <Circle key={i} cx={spots[i][0]} cy={spots[i][1]} r={spots[i][2]} fill={`url(#b${i}${id})`} />
103
+ ))}
104
+ </Svg>
105
+ <GrainOverlay grain={grain} />
106
+ {children}
107
+ </View>
108
+ );
109
+ }
110
+
111
+ const styles = StyleSheet.create({
112
+ root: { overflow: 'hidden' },
113
+ });
@@ -181,14 +181,17 @@ export function useKit(): Theme {
181
181
  return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
182
182
  }
183
183
 
184
- /** Shared motion presets so the whole kit moves the same way. */
184
+ /**
185
+ * Shared motion presets so the whole kit moves the same way. Every spring is critically damped
186
+ * (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
187
+ */
185
188
  export const motion = {
186
189
  /** Toggles, selection indicators. */
187
- snappy: { damping: 18, stiffness: 260, mass: 0.8 },
188
- /** Press feedback, pops. */
189
- bouncy: { damping: 11, stiffness: 220, mass: 0.7 },
190
+ snappy: { damping: 30, stiffness: 260, mass: 0.8 },
191
+ /** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
192
+ bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
190
193
  /** Sheets, layout changes. */
191
- gentle: { damping: 22, stiffness: 160, mass: 1 },
194
+ gentle: { damping: 27, stiffness: 160, mass: 1 },
192
195
  fast: 160,
193
196
  normal: 240,
194
197
  };
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "grain",
3
+ "type": "component",
4
+ "title": "Grain Gradient",
5
+ "description": "A soft mesh-style gradient — a base colour with blurred colour blobs — finished with real film grain. Twelve palettes or your own colours. Use it as a screen or card background, or behind glass so there is something to refract. GrainOverlay adds the same grain to anything, photos included.",
6
+ "tier": "free",
7
+ "dependencies": [
8
+ "react-native-svg"
9
+ ],
10
+ "registryDependencies": [
11
+ "kit"
12
+ ],
13
+ "sharedFiles": [
14
+ "lib/kit.tsx"
15
+ ],
16
+ "filesDir": "grain/files"
17
+ }
@@ -181,14 +181,17 @@ export function useKit(): Theme {
181
181
  return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
182
182
  }
183
183
 
184
- /** Shared motion presets so the whole kit moves the same way. */
184
+ /**
185
+ * Shared motion presets so the whole kit moves the same way. Every spring is critically damped
186
+ * (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
187
+ */
185
188
  export const motion = {
186
189
  /** Toggles, selection indicators. */
187
- snappy: { damping: 18, stiffness: 260, mass: 0.8 },
188
- /** Press feedback, pops. */
189
- bouncy: { damping: 11, stiffness: 220, mass: 0.7 },
190
+ snappy: { damping: 30, stiffness: 260, mass: 0.8 },
191
+ /** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
192
+ bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
190
193
  /** Sheets, layout changes. */
191
- gentle: { damping: 22, stiffness: 160, mass: 1 },
194
+ gentle: { damping: 27, stiffness: 160, mass: 1 },
192
195
  fast: 160,
193
196
  normal: 240,
194
197
  };
@@ -44,7 +44,7 @@ export function GlassTabBar({ state, navigation, insets }: BottomTabBarProps) {
44
44
  Animated.spring(bubbleX, {
45
45
  toValue: activeIndex * itemW,
46
46
  useNativeDriver: true,
47
- damping: 18,
47
+ damping: 29,
48
48
  stiffness: 240,
49
49
  mass: 0.8,
50
50
  }).start();
@@ -157,7 +157,7 @@ function TabButton({
157
157
  }) {
158
158
  const press = useRef(new Animated.Value(1)).current;
159
159
  const to = (v: number) =>
160
- Animated.spring(press, { toValue: v, useNativeDriver: true, speed: 40, bounciness: 10 }).start();
160
+ Animated.spring(press, { toValue: v, useNativeDriver: true, speed: 40, bounciness: 0 }).start();
161
161
 
162
162
  return (
163
163
  <Pressable
@@ -33,7 +33,7 @@ export function setTabBarCollapsed(next: boolean) {
33
33
  Animated.spring(tabBarCollapse, {
34
34
  toValue: next ? 1 : 0,
35
35
  useNativeDriver: false,
36
- damping: 20,
36
+ damping: 29,
37
37
  stiffness: 220,
38
38
  mass: 0.9,
39
39
  }).start();
@@ -104,6 +104,9 @@ const SIZES: Record<ListItemSize, { minH: number; tile: number; glyph: number; a
104
104
  lg: { minH: 68, tile: 38, glyph: 21, avatar: 48, title: 17, sub: 14, padY: 14 },
105
105
  };
106
106
 
107
+ // Controls stop growing at iOS's largest standard text size (xxxL ≈ 1.35×) so they keep their shape;
108
+ // content text scales all the way.
109
+ const FONT_CAP = 1.35;
107
110
  const PAD_X = 16;
108
111
  const GAP = 12;
109
112
  const AVATAR_COLORS = ['#f97316', '#0ea5e9', '#22c55e', '#a855f7', '#ec4899', '#14b8a6', '#6366f1', '#eab308'];
@@ -140,7 +143,8 @@ export function ListItem({
140
143
  accessibilityLabel,
141
144
  style,
142
145
  }: ListItemProps) {
143
- const { colors, radius } = useKit();
146
+ const { colors, radius, font } = useKit();
147
+ const ff = font.family ? { fontFamily: font.family } : null;
144
148
  const tint = accent ?? colors.accent;
145
149
  const s = SIZES[size];
146
150
  const pressable = !!(onPress || onLongPress);
@@ -192,18 +196,18 @@ export function ListItem({
192
196
  ) : null}
193
197
  <View style={styles.body}>
194
198
  <View style={styles.titleRow}>
195
- <Text numberOfLines={1} style={[styles.title, { fontSize: s.title, color: titleColor }, titleStyle]}>
199
+ <Text numberOfLines={1} style={[styles.title, ff, { fontSize: s.title, color: titleColor }, titleStyle]}>
196
200
  {title}
197
201
  </Text>
198
- {meta ? <Text style={[styles.meta, { color: colors.mutedForeground }]}>{meta}</Text> : null}
202
+ {meta ? <Text style={[styles.meta, ff, { color: colors.mutedForeground }]}>{meta}</Text> : null}
199
203
  </View>
200
204
  {subtitle ? (
201
- <Text numberOfLines={1} style={[styles.sub, { fontSize: s.sub, color: colors.mutedForeground }]}>
205
+ <Text numberOfLines={1} style={[styles.sub, ff, { fontSize: s.sub, color: colors.mutedForeground }]}>
202
206
  {subtitle}
203
207
  </Text>
204
208
  ) : null}
205
209
  {description ? (
206
- <Text numberOfLines={numberOfLines} style={[styles.desc, { fontSize: s.sub, lineHeight: s.sub * 1.36, color: colors.mutedForeground }]}>
210
+ <Text numberOfLines={numberOfLines} style={[styles.desc, ff, { fontSize: s.sub, lineHeight: s.sub * 1.36, color: colors.mutedForeground }]}>
207
211
  {description}
208
212
  </Text>
209
213
  ) : null}
@@ -211,17 +215,19 @@ export function ListItem({
211
215
  {value || badge || checked || trailing !== undefined || showChevron ? (
212
216
  <View style={styles.trailing}>
213
217
  {value ? (
214
- <Text numberOfLines={1} style={[styles.value, { color: colors.mutedForeground }]}>
218
+ <Text numberOfLines={1} style={[styles.value, ff, { color: colors.mutedForeground }]}>
215
219
  {value}
216
220
  </Text>
217
221
  ) : null}
218
222
  {badge === true ? <View style={[styles.dot, { backgroundColor: tint }]} /> : null}
219
223
  {badgeText ? (
220
224
  <View style={[styles.badge, { borderRadius: radius.capsule(22), backgroundColor: tint }]}>
221
- <Text style={[styles.badgeText, { color: readableOn(tint) }]}>{badgeText}</Text>
225
+ <Text maxFontSizeMultiplier={FONT_CAP} style={[styles.badgeText, ff, { color: readableOn(tint) }]}>
226
+ {badgeText}
227
+ </Text>
222
228
  </View>
223
229
  ) : null}
224
- {isTextNode(trailing) ? <Text style={[styles.value, { color: colors.mutedForeground }]}>{trailing}</Text> : trailing}
230
+ {isTextNode(trailing) ? <Text style={[styles.value, ff, { color: colors.mutedForeground }]}>{trailing}</Text> : trailing}
225
231
  {checked ? <Check color={tint} /> : null}
226
232
  {showChevron ? <Chevron color={alpha(colors.mutedForeground, 0.7)} /> : null}
227
233
  </View>
@@ -282,11 +288,12 @@ export interface ListGroupProps {
282
288
  * between rows that start past the icon so they line up with the text.
283
289
  */
284
290
  export function ListGroup({ header, footer, background, children, style }: ListGroupProps) {
285
- const { colors, radius } = useKit();
291
+ const { colors, radius, font } = useKit();
292
+ const ff = font.family ? { fontFamily: font.family } : null;
286
293
  return (
287
294
  <View style={[styles.group, style]}>
288
295
  {header ? (
289
- <Text accessibilityRole="header" style={[styles.header, { color: colors.mutedForeground }]}>
296
+ <Text accessibilityRole="header" style={[styles.header, ff, { color: colors.mutedForeground }]}>
290
297
  {header}
291
298
  </Text>
292
299
  ) : null}
@@ -298,7 +305,7 @@ export function ListGroup({ header, footer, background, children, style }: ListG
298
305
  >
299
306
  {withDividers(children)}
300
307
  </View>
301
- {footer ? <Text style={[styles.footer, { color: colors.mutedForeground }]}>{footer}</Text> : null}
308
+ {footer ? <Text style={[styles.footer, ff, { color: colors.mutedForeground }]}>{footer}</Text> : null}
302
309
  </View>
303
310
  );
304
311
  }
@@ -315,16 +322,17 @@ export interface ListSectionProps {
315
322
 
316
323
  /** Plain full-width section: header text, then rows with inset separators. */
317
324
  export function ListSection({ header, footer, dividerAfterLast, children, style }: ListSectionProps) {
318
- const { colors } = useKit();
325
+ const { colors, font } = useKit();
326
+ const ff = font.family ? { fontFamily: font.family } : null;
319
327
  return (
320
328
  <View style={style}>
321
329
  {header ? (
322
- <Text accessibilityRole="header" style={[styles.sectionHeader, { color: colors.mutedForeground }]}>
330
+ <Text accessibilityRole="header" style={[styles.sectionHeader, ff, { color: colors.mutedForeground }]}>
323
331
  {header}
324
332
  </Text>
325
333
  ) : null}
326
334
  {withDividers(children, dividerAfterLast)}
327
- {footer ? <Text style={[styles.footer, { color: colors.mutedForeground }]}>{footer}</Text> : null}
335
+ {footer ? <Text style={[styles.footer, ff, { color: colors.mutedForeground }]}>{footer}</Text> : null}
328
336
  </View>
329
337
  );
330
338
  }
@@ -341,7 +349,8 @@ function withDividers(children: React.ReactNode, last = false) {
341
349
  }
342
350
 
343
351
  function AvatarCircle({ src, name = '', color, online, size, ring }: ListItemAvatar & { size: number; ring: string }) {
344
- const { colors } = useKit();
352
+ const { colors, font } = useKit();
353
+ const ff = font.family ? { fontFamily: font.family } : null;
345
354
  const [failed, setFailed] = useState(false);
346
355
  const initials = name
347
356
  .split(/\s+/)
@@ -356,7 +365,9 @@ function AvatarCircle({ src, name = '', color, online, size, ring }: ListItemAva
356
365
  return (
357
366
  <View style={{ width: size, height: size }}>
358
367
  <View style={[styles.avatar, { width: size, height: size, borderRadius: size / 2, backgroundColor: bg }]}>
359
- <Text style={{ color: readableOn(bg), fontSize: size * 0.38, fontWeight: '400' }}>{initials}</Text>
368
+ <Text maxFontSizeMultiplier={FONT_CAP} style={[{ color: readableOn(bg), fontSize: size * 0.38, fontWeight: '400' }, ff]}>
369
+ {initials}
370
+ </Text>
360
371
  {src && !failed ? (
361
372
  <Image source={{ uri: src }} onError={() => setFailed(true)} style={[StyleSheet.absoluteFill, { borderRadius: size / 2 }]} />
362
373
  ) : null}
@@ -495,7 +506,7 @@ const styles = StyleSheet.create({
495
506
  trailing: { flexDirection: 'row', alignItems: 'center', gap: 8, flexShrink: 0, maxWidth: '50%' },
496
507
  value: { fontSize: 15, flexShrink: 1 },
497
508
  dot: { width: 9, height: 9, borderRadius: 5 },
498
- badge: { minWidth: 22, height: 22, paddingHorizontal: 7, alignItems: 'center', justifyContent: 'center' },
509
+ badge: { minWidth: 22, minHeight: 22, paddingHorizontal: 7, alignItems: 'center', justifyContent: 'center' },
499
510
  badgeText: { fontSize: 12, fontWeight: '400', fontVariant: ['tabular-nums'] },
500
511
  divider: { position: 'absolute', right: 0, bottom: 0, height: StyleSheet.hairlineWidth },
501
512
  avatar: { alignItems: 'center', justifyContent: 'center', overflow: 'hidden' },
@@ -181,14 +181,17 @@ export function useKit(): Theme {
181
181
  return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
182
182
  }
183
183
 
184
- /** Shared motion presets so the whole kit moves the same way. */
184
+ /**
185
+ * Shared motion presets so the whole kit moves the same way. Every spring is critically damped
186
+ * (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
187
+ */
185
188
  export const motion = {
186
189
  /** Toggles, selection indicators. */
187
- snappy: { damping: 18, stiffness: 260, mass: 0.8 },
188
- /** Press feedback, pops. */
189
- bouncy: { damping: 11, stiffness: 220, mass: 0.7 },
190
+ snappy: { damping: 30, stiffness: 260, mass: 0.8 },
191
+ /** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
192
+ bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
190
193
  /** Sheets, layout changes. */
191
- gentle: { damping: 22, stiffness: 160, mass: 1 },
194
+ gentle: { damping: 27, stiffness: 160, mass: 1 },
192
195
  fast: 160,
193
196
  normal: 240,
194
197
  };
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "morphing-button-group",
3
+ "type": "component",
4
+ "title": "Morphing Button Group",
5
+ "description": "A row of buttons that morphs between sets: one button splits into two, two join into one, and a button that changes its label cross-fades it while it stretches. Ships with SignatureSheet, the pattern it was made for — a \"Sign\" pill that grows into a signing card whose Cancel splits into Reset + Confirm once you draw, and Confirm stretches into \"Confirm Signature\" before the card glides back down.",
6
+ "tier": "pro",
7
+ "dependencies": [
8
+ "react-native-svg",
9
+ "expo-haptics"
10
+ ],
11
+ "registryDependencies": [
12
+ "kit"
13
+ ],
14
+ "sharedFiles": [
15
+ "lib/kit.tsx"
16
+ ],
17
+ "filesDir": null
18
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "orb-reveal",
3
+ "type": "component",
4
+ "title": "Orb Reveal",
5
+ "description": "An onboarding hero built around a thick liquid-glass orb. Hold it and it swells while a deep blue smile of light glows through it; drag it up and it shrinks into a glass button, showing your title through the lens on the way — magnified in the middle, mirrored around the bevel, with a rainbow fringe — then glass marbles fountain out of it into a heart. Drag it back down and the marbles float away.",
6
+ "tier": "pro",
7
+ "dependencies": [
8
+ "@shopify/react-native-skia",
9
+ "react-native-reanimated",
10
+ "react-native-worklets",
11
+ "expo-haptics"
12
+ ],
13
+ "registryDependencies": [
14
+ "kit"
15
+ ],
16
+ "sharedFiles": [
17
+ "lib/kit.tsx"
18
+ ],
19
+ "filesDir": null
20
+ }