@expokit/cli 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) 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/assistant-app.json +47 -0
  7. package/registry/_docs/avatar.json +1 -1
  8. package/registry/_docs/badge.json +1 -1
  9. package/registry/_docs/bottom-nav.json +6 -6
  10. package/registry/_docs/button-3d.json +2 -2
  11. package/registry/_docs/calendar.json +5 -5
  12. package/registry/_docs/checkout.json +7 -1
  13. package/registry/_docs/confetti.json +3 -3
  14. package/registry/_docs/date-picker.json +4 -4
  15. package/registry/_docs/donut-shop.json +88 -0
  16. package/registry/_docs/drawer.json +4 -4
  17. package/registry/_docs/fintech-app.json +49 -0
  18. package/registry/_docs/floating-input.json +5 -5
  19. package/registry/_docs/food-app.json +47 -0
  20. package/registry/_docs/glass.json +1 -1
  21. package/registry/_docs/grain.json +80 -0
  22. package/registry/_docs/morphing-button-group.json +96 -0
  23. package/registry/_docs/orb-reveal.json +132 -0
  24. package/registry/_docs/otp-input.json +4 -4
  25. package/registry/_docs/password-input.json +3 -3
  26. package/registry/_docs/qr-code-button.json +122 -0
  27. package/registry/_docs/search-bar.json +3 -3
  28. package/registry/_docs/select.json +7 -7
  29. package/registry/_docs/settings.json +6 -0
  30. package/registry/_docs/skeleton.json +2 -2
  31. package/registry/_docs/slider.json +6 -6
  32. package/registry/_docs/spinner.json +1 -1
  33. package/registry/_docs/stepper.json +7 -7
  34. package/registry/_docs/textarea.json +2 -2
  35. package/registry/_docs/ticket-shape.json +1 -1
  36. package/registry/_docs/tilt-card.json +4 -4
  37. package/registry/_docs/time-picker.json +6 -6
  38. package/registry/_docs/timeline.json +2 -2
  39. package/registry/_docs/toast.json +8 -8
  40. package/registry/accordion/files/src/components/ui/accordion.tsx +5 -4
  41. package/registry/accordion/files/src/lib/kit.tsx +8 -5
  42. package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +9 -8
  43. package/registry/action-sheet/files/src/lib/kit.tsx +8 -5
  44. package/registry/alert/files/src/components/ui/alert.tsx +7 -4
  45. package/registry/alert/files/src/components/ui/button.tsx +4 -3
  46. package/registry/alert/files/src/lib/kit.tsx +8 -5
  47. package/registry/assistant-app.json +49 -0
  48. package/registry/avatar/files/src/components/ui/avatar.tsx +7 -4
  49. package/registry/avatar/files/src/lib/kit.tsx +8 -5
  50. package/registry/badge/files/src/components/ui/badge.tsx +21 -7
  51. package/registry/badge/files/src/lib/kit.tsx +8 -5
  52. package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +14 -7
  53. package/registry/bottom-nav/files/src/lib/kit.tsx +8 -5
  54. package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +7 -6
  55. package/registry/bottom-sheet/files/src/lib/kit.tsx +8 -5
  56. package/registry/button/files/src/components/ui/button.tsx +4 -3
  57. package/registry/button/files/src/lib/kit.tsx +8 -5
  58. package/registry/button-3d/files/src/components/ui/button-3d.tsx +3 -2
  59. package/registry/button-3d/files/src/lib/kit.tsx +8 -5
  60. package/registry/calendar/files/src/components/ui/calendar.tsx +9 -5
  61. package/registry/calendar/files/src/lib/kit.tsx +8 -5
  62. package/registry/card/files/src/components/ui/card.tsx +8 -6
  63. package/registry/card/files/src/lib/kit.tsx +8 -5
  64. package/registry/checkbox/files/src/components/ui/checkbox.tsx +6 -5
  65. package/registry/checkbox/files/src/lib/kit.tsx +8 -5
  66. package/registry/chip-group/files/src/components/ui/chip-group.tsx +4 -2
  67. package/registry/chip-group/files/src/lib/kit.tsx +8 -5
  68. package/registry/confetti/files/src/lib/kit.tsx +8 -5
  69. package/registry/coupon/files/src/components/ui/coupon.tsx +23 -20
  70. package/registry/coupon/files/src/components/ui/flip-card.tsx +1 -1
  71. package/registry/coupon/files/src/components/ui/tilt-card.tsx +2 -2
  72. package/registry/coupon/files/src/lib/kit.tsx +8 -5
  73. package/registry/date-picker/files/src/components/ui/calendar.tsx +9 -5
  74. package/registry/date-picker/files/src/components/ui/date-picker.tsx +2 -2
  75. package/registry/date-picker/files/src/lib/kit.tsx +8 -5
  76. package/registry/dialog/files/src/components/ui/dialog.tsx +12 -6
  77. package/registry/dialog/files/src/lib/kit.tsx +8 -5
  78. package/registry/donut-shop.json +18 -0
  79. package/registry/drawer/files/src/components/ui/drawer.tsx +25 -19
  80. package/registry/drawer/files/src/lib/kit.tsx +8 -5
  81. package/registry/fintech-app.json +57 -0
  82. package/registry/flip-card/files/src/components/ui/flip-card.tsx +1 -1
  83. package/registry/flip-card/files/src/lib/kit.tsx +8 -5
  84. package/registry/floating-input/files/src/components/ui/floating-input.tsx +16 -6
  85. package/registry/floating-input/files/src/lib/kit.tsx +8 -5
  86. package/registry/food-app.json +55 -0
  87. package/registry/grain/files/src/components/ui/grain.tsx +113 -0
  88. package/registry/{tag-input → grain}/files/src/lib/kit.tsx +8 -5
  89. package/registry/grain.json +17 -0
  90. package/registry/kit/files/src/lib/kit.tsx +8 -5
  91. package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +1 -1
  92. package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +1 -1
  93. package/registry/list-item/files/src/components/ui/list-item.tsx +20 -16
  94. package/registry/list-item/files/src/lib/kit.tsx +8 -5
  95. package/registry/morphing-button-group.json +18 -0
  96. package/registry/orb-reveal.json +20 -0
  97. package/registry/otp-input/files/src/components/ui/otp-input.tsx +8 -6
  98. package/registry/otp-input/files/src/lib/kit.tsx +8 -5
  99. package/registry/password-input/files/src/components/ui/floating-input.tsx +16 -6
  100. package/registry/password-input/files/src/components/ui/password-input.tsx +2 -2
  101. package/registry/password-input/files/src/lib/kit.tsx +8 -5
  102. package/registry/payment-card/files/src/components/ui/flip-card.tsx +1 -1
  103. package/registry/payment-card/files/src/components/ui/payment-card.tsx +27 -14
  104. package/registry/payment-card/files/src/components/ui/tilt-card.tsx +2 -2
  105. package/registry/payment-card/files/src/lib/kit.tsx +8 -5
  106. package/registry/progress/files/src/components/ui/progress.tsx +8 -5
  107. package/registry/progress/files/src/lib/kit.tsx +8 -5
  108. package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +15 -11
  109. package/registry/progress-steps/files/src/lib/kit.tsx +8 -5
  110. package/registry/qr-code-button.json +18 -0
  111. package/registry/radio-group/files/src/components/ui/radio-group.tsx +10 -9
  112. package/registry/radio-group/files/src/lib/kit.tsx +8 -5
  113. package/registry/search-bar/files/src/components/ui/search-bar.tsx +13 -11
  114. package/registry/search-bar/files/src/lib/kit.tsx +8 -5
  115. package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +4 -2
  116. package/registry/segmented-control/files/src/lib/kit.tsx +8 -5
  117. package/registry/select/files/src/components/ui/select.tsx +3 -3
  118. package/registry/select/files/src/lib/kit.tsx +8 -5
  119. package/registry/skeleton/files/src/components/ui/skeleton.tsx +4 -4
  120. package/registry/skeleton/files/src/lib/kit.tsx +8 -5
  121. package/registry/slider/files/src/components/ui/slider.tsx +5 -4
  122. package/registry/slider/files/src/lib/kit.tsx +8 -5
  123. package/registry/spinner/files/src/components/ui/spinner.tsx +17 -12
  124. package/registry/spinner/files/src/lib/kit.tsx +8 -5
  125. package/registry/stateful-button/files/src/lib/kit.tsx +8 -5
  126. package/registry/stepper/files/src/components/ui/stepper.tsx +8 -4
  127. package/registry/stepper/files/src/lib/kit.tsx +8 -5
  128. package/registry/switch/files/src/components/ui/switch.tsx +4 -3
  129. package/registry/switch/files/src/lib/kit.tsx +8 -5
  130. package/registry/tabs/files/src/components/ui/tabs.tsx +10 -8
  131. package/registry/tabs/files/src/lib/kit.tsx +8 -5
  132. package/registry/text-animation/files/src/components/ui/text-animation.tsx +4 -4
  133. package/registry/text-animation/files/src/lib/kit.tsx +8 -5
  134. package/registry/textarea/files/src/components/ui/textarea.tsx +11 -9
  135. package/registry/textarea/files/src/lib/kit.tsx +8 -5
  136. package/registry/ticket-shape/files/src/lib/kit.tsx +8 -5
  137. package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +2 -2
  138. package/registry/tilt-card/files/src/lib/kit.tsx +8 -5
  139. package/registry/time-picker/files/src/components/ui/time-picker.tsx +2 -2
  140. package/registry/time-picker/files/src/lib/kit.tsx +8 -5
  141. package/registry/timeline/files/src/components/ui/avatar.tsx +7 -4
  142. package/registry/timeline/files/src/components/ui/timeline.tsx +12 -10
  143. package/registry/timeline/files/src/lib/kit.tsx +8 -5
  144. package/registry/toast/files/src/components/ui/button.tsx +4 -3
  145. package/registry/toast/files/src/components/ui/toast.tsx +13 -11
  146. package/registry/toast/files/src/lib/kit.tsx +8 -5
  147. package/schema/components.schema.json +1 -1
  148. package/registry/_docs/color-swatch.json +0 -82
  149. package/registry/_docs/kpi-card.json +0 -14
  150. package/registry/_docs/like-button.json +0 -92
  151. package/registry/_docs/morph-button.json +0 -128
  152. package/registry/_docs/pagination-dots.json +0 -108
  153. package/registry/_docs/shimmer-button.json +0 -98
  154. package/registry/_docs/tag-input.json +0 -148
  155. package/registry/color-swatch.json +0 -17
  156. package/registry/kpi-card/files/src/components/charts/kpi-card.tsx +0 -45
  157. package/registry/kpi-card.json +0 -14
  158. package/registry/like-button.json +0 -18
  159. package/registry/morph-button.json +0 -18
  160. package/registry/pagination-dots/files/src/components/ui/pagination-dots.tsx +0 -262
  161. package/registry/pagination-dots/files/src/lib/kit.tsx +0 -219
  162. package/registry/pagination-dots.json +0 -15
  163. package/registry/shimmer-button.json +0 -17
  164. package/registry/tag-input/files/src/components/ui/tag-input.tsx +0 -720
  165. package/registry/tag-input.json +0 -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
  };
@@ -131,7 +131,8 @@ export function Avatar({
131
131
  haptics = true,
132
132
  style,
133
133
  }: AvatarProps) {
134
- const { colors, radius, dark } = useKit();
134
+ const { colors, radius, dark, font } = useKit();
135
+ const ff = font.family ? { fontFamily: font.family } : null;
135
136
  const reduceMotion = useReduceMotion();
136
137
  const uid = useId().replace(/[^a-zA-Z0-9]/g, '');
137
138
  const px = resolveSize(size);
@@ -280,6 +281,7 @@ export function Avatar({
280
281
  numberOfLines={1}
281
282
  style={[
282
283
  styles.initials,
284
+ ff,
283
285
  {
284
286
  color: ink,
285
287
  fontSize: Math.max(9, px * (initials.length > 1 ? 0.37 : 0.42)),
@@ -448,7 +450,7 @@ export function Avatar({
448
450
  {badgeText ? (
449
451
  <Text
450
452
  allowFontScaling={false}
451
- style={[styles.badgeText, { color: colors.dangerForeground, fontSize: Math.max(9, badgeH * 0.6) }]}
453
+ style={[styles.badgeText, ff, { color: colors.dangerForeground, fontSize: Math.max(9, badgeH * 0.6) }]}
452
454
  >
453
455
  {badgeText}
454
456
  </Text>
@@ -623,7 +625,8 @@ export function AvatarGroup({
623
625
  onPressMore,
624
626
  style,
625
627
  }: AvatarGroupProps) {
626
- const { colors, radius, dark } = useKit();
628
+ const { colors, radius, dark, font } = useKit();
629
+ const ff = font.family ? { fontFamily: font.family } : null;
627
630
  const reduceMotion = useReduceMotion();
628
631
  const px = resolveSize(size);
629
632
  const bg = surface ?? colors.background;
@@ -699,7 +702,7 @@ export function AvatarGroup({
699
702
  <Text
700
703
  allowFontScaling={false}
701
704
  numberOfLines={1}
702
- style={[styles.moreText, { color: colors.foreground, fontSize: Math.max(8, px * (extraText.length <= 2 ? 0.34 : extraText.length === 3 ? 0.29 : 0.24)) }]}
705
+ style={[styles.moreText, ff, { color: colors.foreground, fontSize: Math.max(8, px * (extraText.length <= 2 ? 0.34 : extraText.length === 3 ? 0.29 : 0.24)) }]}
703
706
  >
704
707
  +{extraText}
705
708
  </Text>
@@ -244,7 +244,7 @@ function ScrollTimeline({
244
244
  const dottedFrom = Math.max(0, reach);
245
245
  const dottedH = measured && reach < n - 1 ? nodes[n - 1] - nodes[dottedFrom] : 0;
246
246
 
247
- let fillStyle: Animated.WithAnimatedObject<ViewStyle> | null = null;
247
+ let fillStyle: Animated.WithAnimatedValue<ViewStyle> | null = null;
248
248
  if (fillH > 0) {
249
249
  if (frozen) {
250
250
  fillStyle = {};
@@ -379,7 +379,8 @@ function Row({
379
379
  lit,
380
380
  onNode,
381
381
  }: RowProps) {
382
- const { colors, radius, dark } = useKit();
382
+ const { colors, radius, dark, font } = useKit();
383
+ const ff = font.family ? { fontFamily: font.family } : null;
383
384
  const compact = variant === 'compact';
384
385
  const card = variant === 'card';
385
386
  const scroll = variant === 'scroll';
@@ -454,21 +455,21 @@ function Row({
454
455
  <View style={[styles.titleRow, compact && styles.compactRow]}>
455
456
  <Text
456
457
  numberOfLines={compact ? 1 : 2}
457
- style={[styles.title, { fontSize: titleSize, lineHeight: titleLine, color: titleColor }]}
458
+ style={[styles.title, ff, { fontSize: titleSize, lineHeight: titleLine, color: titleColor }]}
458
459
  >
459
460
  {item.title}
460
461
  </Text>
461
462
  {item.time ? (
462
463
  <Text
463
464
  numberOfLines={1}
464
- style={[styles.time, { lineHeight: titleLine, color: current ? tint : colors.mutedForeground }]}
465
+ style={[styles.time, ff, { lineHeight: titleLine, color: current ? tint : colors.mutedForeground }]}
465
466
  >
466
467
  {item.time}
467
468
  </Text>
468
469
  ) : null}
469
470
  </View>
470
471
  {item.description && !compact ? (
471
- <Text style={[styles.desc, { color: colors.mutedForeground }]}>{item.description}</Text>
472
+ <Text style={[styles.desc, ff, { color: colors.mutedForeground }]}>{item.description}</Text>
472
473
  ) : null}
473
474
  {photos.length ? <TimelineGallery images={photos} dim={muted} /> : null}
474
475
  {item.tags?.length || hasLinks ? (
@@ -478,7 +479,7 @@ function Row({
478
479
  key={`t-${t}`}
479
480
  style={[styles.tag, { borderRadius: radius.capsule(22), backgroundColor: alpha(tint, dark ? 0.2 : 0.1) }]}
480
481
  >
481
- <Text style={[styles.tagText, { color: tint }]}>{t}</Text>
482
+ <Text style={[styles.tagText, ff, { color: tint }]}>{t}</Text>
482
483
  </View>
483
484
  ))}
484
485
  {item.links?.map((l) => (
@@ -490,14 +491,14 @@ function Row({
490
491
  hitSlop={12}
491
492
  style={({ pressed }) => [styles.link, { opacity: pressed ? 0.55 : 1 }]}
492
493
  >
493
- <Text style={[styles.linkText, { color: tint }]}>{l.label} →</Text>
494
+ <Text style={[styles.linkText, ff, { color: tint }]}>{l.label} →</Text>
494
495
  </Pressable>
495
496
  ))}
496
497
  </View>
497
498
  ) : null}
498
499
  {item.content ? (
499
500
  <View style={styles.content}>
500
- {isTextNode(item.content) ? <Text style={{ color: colors.foreground }}>{item.content}</Text> : item.content}
501
+ {isTextNode(item.content) ? <Text style={[ff, { color: colors.foreground }]}>{item.content}</Text> : item.content}
501
502
  </View>
502
503
  ) : null}
503
504
  </>
@@ -927,7 +928,8 @@ function TimelineGallery({ images, dim }: { images: TimelineImageSource[]; dim:
927
928
  }
928
929
 
929
930
  function Photo({ uri, delay, style, more }: { uri: string; delay: number; style: StyleProp<ViewStyle>; more: number }) {
930
- const { colors } = useKit();
931
+ const { colors, font } = useKit();
932
+ const ff = font.family ? { fontFamily: font.family } : null;
931
933
  const shown = useRef(new Animated.Value(0)).current;
932
934
  return (
933
935
  <View style={[styles.photo, { backgroundColor: colors.muted }, style]}>
@@ -939,7 +941,7 @@ function Photo({ uri, delay, style, more }: { uri: string; delay: number; style:
939
941
  />
940
942
  {more > 0 ? (
941
943
  <View style={[StyleSheet.absoluteFill, styles.more]}>
942
- <Text style={styles.moreText}>+{more}</Text>
944
+ <Text style={[styles.moreText, ff]}>+{more}</Text>
943
945
  </View>
944
946
  ) : null}
945
947
  </View>
@@ -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
  };
@@ -116,7 +116,8 @@ export function Button({
116
116
  onPressOut,
117
117
  ...props
118
118
  }: ButtonProps) {
119
- const { colors, radius, dark } = useKit();
119
+ const { colors, radius, dark, font } = useKit();
120
+ const ff = font.family ? { fontFamily: font.family } : null;
120
121
  const gid = 'btn' + useId().replace(/[^a-zA-Z0-9]/g, '');
121
122
  const scale = useRef(new Animated.Value(1)).current;
122
123
  const isDisabled = disabled || loading;
@@ -149,7 +150,7 @@ export function Button({
149
150
  const label = isTextNode(children) ? (
150
151
  <Text
151
152
  numberOfLines={1}
152
- style={[styles.label, { color: fg, fontSize: s.font }, variant === 'link' && styles.link, textStyle]}
153
+ style={[styles.label, ff, { color: fg, fontSize: s.font }, variant === 'link' && styles.link, textStyle]}
153
154
  >
154
155
  {children}
155
156
  </Text>
@@ -235,7 +236,7 @@ export function Button({
235
236
  <View style={styles.row}>
236
237
  <ActivityIndicator size="small" color={fg} />
237
238
  {size !== 'icon' && isTextNode(children) ? (
238
- <Text style={[styles.label, { color: fg, fontSize: s.font, opacity: 0.85 }]}>{children}</Text>
239
+ <Text style={[styles.label, ff, { color: fg, fontSize: s.font, opacity: 0.85 }]}>{children}</Text>
239
240
  ) : null}
240
241
  </View>
241
242
  ) : (
@@ -417,7 +417,8 @@ function ToastItem({
417
417
  onRemoved,
418
418
  }: ToastItemProps) {
419
419
  const toast = useToast();
420
- const { colors, radius, dark } = useKit();
420
+ const { colors, radius, dark, font } = useKit();
421
+ const ff = font.family ? { fontFamily: font.family } : null;
421
422
  const toward = edge === 'bottom' ? 1 : -1; // swipe direction that dismisses toward the edge
422
423
  const last = useRef(layout ?? { y: 0, scale: 1, hidden: false, faded: false });
423
424
  if (layout) last.current = layout;
@@ -505,8 +506,8 @@ function ToastItem({
505
506
  },
506
507
  onPanResponderTerminate: () => {
507
508
  setTouching(false);
508
- Animated.spring(dragX, { toValue: 0, useNativeDriver: true }).start();
509
- Animated.spring(dragY, { toValue: 0, useNativeDriver: true }).start();
509
+ Animated.spring(dragX, { toValue: 0, ...motion.snappy, useNativeDriver: true }).start();
510
+ Animated.spring(dragY, { toValue: 0, ...motion.snappy, useNativeDriver: true }).start();
510
511
  },
511
512
  });
512
513
  })(),
@@ -572,11 +573,11 @@ function ToastItem({
572
573
  <Animated.View style={[styles.row, minimal ? styles.padMinimal : icon ? styles.padIcon : styles.pad, { opacity: content }]}>
573
574
  {icon ? <View style={!minimal && data.description ? styles.iconTop : null}>{icon}</View> : null}
574
575
  <View style={minimal ? styles.textMinimal : styles.text}>
575
- <Text numberOfLines={minimal ? 1 : 3} style={[styles.title, { color: look.title }]}>
576
+ <Text numberOfLines={minimal ? 1 : 3} style={[styles.title, ff, { color: look.title }]}>
576
577
  {data.title}
577
578
  </Text>
578
579
  {data.description ? (
579
- <Text numberOfLines={minimal ? 2 : 4} style={[styles.desc, minimal && styles.descMinimal, { color: look.desc }]}>
580
+ <Text numberOfLines={minimal ? 2 : 4} style={[styles.desc, minimal && styles.descMinimal, ff, { color: look.desc }]}>
580
581
  {data.description}
581
582
  </Text>
582
583
  ) : null}
@@ -594,7 +595,7 @@ function ToastItem({
594
595
  { borderRadius: radius.capsule(32), backgroundColor: look.actionBg, opacity: pressed ? 0.7 : 1 },
595
596
  ]}
596
597
  >
597
- <Text style={[styles.actionText, { color: look.actionFg }]}>{data.action.label}</Text>
598
+ <Text style={[styles.actionText, ff, { color: look.actionFg }]}>{data.action.label}</Text>
598
599
  </Pressable>
599
600
  ) : null}
600
601
  {closeButton ? (
@@ -648,7 +649,8 @@ interface IslandToasterProps {
648
649
  * fade are native-driven on their own wrapper views.
649
650
  */
650
651
  function IslandToaster({ store, toasts, duration, offset, haptics, reduceMotion, style }: IslandToasterProps) {
651
- const { colors, radius, dark } = useKit();
652
+ const { colors, radius, dark, font } = useKit();
653
+ const ff = font.family ? { fontFamily: font.family } : null;
652
654
  const active = toasts.filter((t) => !t.dismissed);
653
655
  const front = active[0];
654
656
 
@@ -893,18 +895,18 @@ function IslandToaster({ store, toasts, duration, offset, haptics, reduceMotion,
893
895
  >
894
896
  {icon ? <View style={expanded && shown.description ? styles.iconTop : null}>{icon}</View> : null}
895
897
  <View style={styles.text}>
896
- <Text numberOfLines={expanded ? 3 : 1} style={[styles.title, { color: fg }]}>
898
+ <Text numberOfLines={expanded ? 3 : 1} style={[styles.title, ff, { color: fg }]}>
897
899
  {shown.title}
898
900
  </Text>
899
901
  {shown.description ? (
900
- <Text numberOfLines={expanded ? undefined : 1} style={[styles.desc, { color: alpha(fg, 0.62) }]}>
902
+ <Text numberOfLines={expanded ? undefined : 1} style={[styles.desc, ff, { color: alpha(fg, 0.62) }]}>
901
903
  {shown.description}
902
904
  </Text>
903
905
  ) : null}
904
906
  </View>
905
907
  {!expanded && shown.action ? (
906
908
  <View style={[styles.islandChip, { borderRadius: radius.capsule(24), backgroundColor: alpha(fg, 0.14) }]}>
907
- <Text style={[styles.islandChipText, { color: fg }]}>{shown.action.label}</Text>
909
+ <Text style={[styles.islandChipText, ff, { color: fg }]}>{shown.action.label}</Text>
908
910
  </View>
909
911
  ) : null}
910
912
  </Pressable>
@@ -921,7 +923,7 @@ function IslandToaster({ store, toasts, duration, offset, haptics, reduceMotion,
921
923
  { borderRadius: radius.capsule(44), backgroundColor: alpha(fg, pressed ? 0.24 : 0.16) },
922
924
  ]}
923
925
  >
924
- <Text style={[styles.title, { color: fg }]}>{shown.action.label}</Text>
926
+ <Text style={[styles.title, ff, { color: fg }]}>{shown.action.label}</Text>
925
927
  </Pressable>
926
928
  </View>
927
929
  ) : null}
@@ -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
  };
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "https://expokit.karandev.in/schema.json",
2
+ "$schema": "https://expokit.in/schema.json",
3
3
  "aliases": {
4
4
  "components": "@/components",
5
5
  "lib": "@/lib",
@@ -1,82 +0,0 @@
1
- {
2
- "name": "color-swatch",
3
- "category": "Selection",
4
- "aliases": [],
5
- "exportName": "ColorSwatch",
6
- "files": [
7
- "src/components/ui/color-swatch.tsx"
8
- ],
9
- "summary": "A colour picker where the chosen swatch settles in as a ring springs out around it.",
10
- "props": [
11
- [
12
- "colors",
13
- "(string | { color, label? })[]",
14
- "10-colour palette",
15
- "Your swatches (hex)."
16
- ],
17
- [
18
- "value / onValueChange",
19
- "string",
20
- "—",
21
- "Controlled selection (or use defaultValue)."
22
- ],
23
- [
24
- "defaultValue",
25
- "string",
26
- "—",
27
- "Initial selection when uncontrolled."
28
- ],
29
- [
30
- "variant",
31
- "\"circle\" | \"square\" | \"pill\"",
32
- "\"circle\"",
33
- "Swatch shape."
34
- ],
35
- [
36
- "size",
37
- "\"sm\" | \"md\" | \"lg\"",
38
- "\"md\"",
39
- "28 / 36 / 44pt swatches."
40
- ],
41
- [
42
- "scrollable",
43
- "boolean",
44
- "false",
45
- "One horizontally scrolling row instead of a wrapping grid."
46
- ],
47
- [
48
- "showLabels",
49
- "boolean",
50
- "false",
51
- "Colour names under each swatch."
52
- ],
53
- [
54
- "disabled",
55
- "boolean",
56
- "false",
57
- "Dims and blocks presses."
58
- ],
59
- [
60
- "style",
61
- "StyleProp<ViewStyle>",
62
- "—",
63
- "Style for the wrapping grid (the ScrollView when scrollable)."
64
- ]
65
- ],
66
- "behavior": [
67
- [
68
- "Touch",
69
- "Tap a swatch to choose it. Each swatch's hit area is padded out to 44pt."
70
- ],
71
- [
72
- "Motion",
73
- "The chosen swatch settles inward while a ring and check spring out. The check uses whichever ink reads best on that colour."
74
- ],
75
- [
76
- "Screen readers",
77
- "A radiogroup labelled \"Colour\" by default; each swatch is a radio named by its label or hex value."
78
- ]
79
- ],
80
- "usage": null,
81
- "examples": []
82
- }
@@ -1,14 +0,0 @@
1
- {
2
- "name": "kpi-card",
3
- "category": null,
4
- "aliases": [],
5
- "exportName": "KpiCard",
6
- "files": [
7
- "src/components/charts/kpi-card.tsx"
8
- ],
9
- "summary": null,
10
- "props": [],
11
- "behavior": [],
12
- "usage": null,
13
- "examples": []
14
- }
@@ -1,92 +0,0 @@
1
- {
2
- "name": "like-button",
3
- "category": "Buttons",
4
- "aliases": [],
5
- "exportName": "LikeButton",
6
- "files": [
7
- "src/components/ui/like-button.tsx"
8
- ],
9
- "summary": "A heart, star or bookmark that pops and bursts into particles when liked.",
10
- "props": [
11
- [
12
- "liked / onChange",
13
- "boolean",
14
- "—",
15
- "Controlled state (or use defaultLiked)."
16
- ],
17
- [
18
- "defaultLiked",
19
- "boolean",
20
- "false",
21
- "Initial state when uncontrolled; `count` is the count for this state."
22
- ],
23
- [
24
- "variant",
25
- "\"heart\" | \"star\" | \"bookmark\"",
26
- "\"heart\"",
27
- "Heart, star or bookmark icon; also picks the default liked colour and label."
28
- ],
29
- [
30
- "count",
31
- "number",
32
- "—",
33
- "Shows a rolling counter (1.2K formatting). Uncontrolled: the count for defaultLiked; the component adds the user's like."
34
- ],
35
- [
36
- "size",
37
- "number",
38
- "26",
39
- "Icon size."
40
- ],
41
- [
42
- "color",
43
- "string",
44
- "danger / warning / accent",
45
- "Liked colour (per variant default)."
46
- ],
47
- [
48
- "particleColors",
49
- "string[]",
50
- "liked colour + theme",
51
- "Colours of the burst dots."
52
- ],
53
- [
54
- "haptics",
55
- "boolean",
56
- "true",
57
- "Tap when liking."
58
- ],
59
- [
60
- "disabled",
61
- "boolean",
62
- "false",
63
- "Blocks presses."
64
- ],
65
- [
66
- "style",
67
- "StyleProp<ViewStyle>",
68
- "—",
69
- "Style for the pressable row holding the icon and counter."
70
- ]
71
- ],
72
- "behavior": [
73
- [
74
- "Motion",
75
- "Liking pops the icon, fills it on a bouncy spring and throws a ring and a burst of coloured dots. The counter rolls up or down to the new number."
76
- ],
77
- [
78
- "Haptics",
79
- "A light impact when it is liked; un-liking is silent."
80
- ],
81
- [
82
- "Screen readers",
83
- "A button labelled \"Like\", \"Favourite\" or \"Bookmark\" (or your label), with a selected state and the count as its value."
84
- ],
85
- [
86
- "Touch",
87
- "Small icons get a hitSlop that brings the target up to 44pt."
88
- ]
89
- ],
90
- "usage": null,
91
- "examples": []
92
- }
@@ -1,128 +0,0 @@
1
- {
2
- "name": "morph-button",
3
- "category": "Buttons",
4
- "aliases": [],
5
- "exportName": "MorphButton",
6
- "files": [
7
- "src/components/ui/morph-button.tsx"
8
- ],
9
- "summary": "An async button that shrinks to a spinner, then draws a tick or shakes a cross.",
10
- "props": [
11
- [
12
- "onPress",
13
- "() => Promise<unknown>",
14
- "—",
15
- "Async work. Resolve → tick, reject → cross + shake."
16
- ],
17
- [
18
- "variant",
19
- "\"primary\" | \"accent\" | \"soft\" | \"outline\"",
20
- "\"primary\"",
21
- "Look while idle."
22
- ],
23
- [
24
- "size",
25
- "\"sm\" | \"md\" | \"lg\"",
26
- "\"md\"",
27
- "40 / 48 / 56pt tall."
28
- ],
29
- [
30
- "color",
31
- "string",
32
- "theme accent",
33
- "Brand colour for accent, soft and outline."
34
- ],
35
- [
36
- "successColor / errorColor",
37
- "string",
38
- "theme success / danger",
39
- "Colours of the result circle."
40
- ],
41
- [
42
- "resetAfter",
43
- "number",
44
- "1300",
45
- "ms the result stays before morphing back."
46
- ],
47
- [
48
- "minLoading",
49
- "number",
50
- "600",
51
- "Minimum ms the spinner is shown."
52
- ],
53
- [
54
- "onSuccess / onError",
55
- "(value) => void",
56
- "—",
57
- "Called with the resolved value or the thrown error."
58
- ],
59
- [
60
- "onStatusChange",
61
- "(status) => void",
62
- "—",
63
- "\"idle\" | \"loading\" | \"success\" | \"error\"."
64
- ],
65
- [
66
- "pill",
67
- "boolean",
68
- "true",
69
- "Fully rounded ends while idle."
70
- ],
71
- [
72
- "block",
73
- "boolean",
74
- "false",
75
- "Full width; shrinks to the centre while busy."
76
- ],
77
- [
78
- "left",
79
- "ReactNode",
80
- "—",
81
- "Icon before the label."
82
- ],
83
- [
84
- "haptics",
85
- "boolean",
86
- "true",
87
- "Taps on start, success and error."
88
- ],
89
- [
90
- "disabled",
91
- "boolean",
92
- "false",
93
- "Blocks presses."
94
- ],
95
- [
96
- "textStyle",
97
- "StyleProp<TextStyle>",
98
- "—",
99
- "Style for the idle label text."
100
- ],
101
- [
102
- "style",
103
- "StyleProp<ViewStyle>",
104
- "—",
105
- "Style for the outer animated wrapper (margins, alignment)."
106
- ]
107
- ],
108
- "behavior": [
109
- [
110
- "Motion",
111
- "While the promise runs the button springs into a circle with a spinner. A resolve draws a success tick, a reject shakes with an error cross, then it springs back to its label."
112
- ],
113
- [
114
- "Touch",
115
- "Presses are ignored while it is busy, and the spinner shows for at least minLoading so fast requests still register."
116
- ],
117
- [
118
- "Haptics",
119
- "A light impact on start, then a success or error notification when the promise settles."
120
- ],
121
- [
122
- "Screen readers",
123
- "A button with busy and disabled states; while not idle its value reads \"Working\", \"Done\" or \"Failed\"."
124
- ]
125
- ],
126
- "usage": null,
127
- "examples": []
128
- }