@heyramzi/ui 6.1.0 → 8.0.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 (332) hide show
  1. package/COMPONENTS.md +32 -40
  2. package/README.md +10 -4
  3. package/package.json +31 -29
  4. package/scripts/sync-shadcn.mjs +29 -16
  5. package/scripts/sync-transitions.mjs +11 -11
  6. package/src/ai/ai-button/AiButton.svelte +86 -0
  7. package/src/ai/ai-button/index.ts +4 -0
  8. package/src/ai/confirmation/Confirmation.svelte +8 -8
  9. package/src/ai/confirmation/confirmation-context.svelte.ts +2 -2
  10. package/src/ai/generation-progress/GenerationProgress.svelte +354 -0
  11. package/src/ai/generation-progress/index.ts +7 -0
  12. package/src/ai/index.ts +16 -24
  13. package/src/ai/prompt-input/PromptInput.svelte +19 -1
  14. package/src/ai/prompt-input/PromptInputSubmit.svelte +9 -3
  15. package/src/ai/reasoning/Reasoning.svelte +11 -8
  16. package/src/ambient.d.ts +4 -0
  17. package/src/animations/README.md +17 -17
  18. package/src/animations/index.css +3 -14
  19. package/src/animations/upstream-coverage.test.ts +1 -1
  20. package/src/components/copy/clipboard.test.ts +206 -0
  21. package/src/components/copy/clipboard.ts +49 -0
  22. package/src/components/copy/copied.svelte.ts +39 -0
  23. package/src/components/copy/copy-button.svelte +175 -0
  24. package/src/components/copy/copy-button.test.ts +110 -0
  25. package/src/components/copy/index.ts +3 -0
  26. package/src/components/data-table/data-table.test.ts +134 -0
  27. package/src/components/data-table/data-table.ts +77 -0
  28. package/src/components/data-table/index.ts +20 -0
  29. package/src/components/data-table/infinite-scroll.svelte +69 -0
  30. package/src/components/data-table/pagination.svelte +62 -0
  31. package/src/components/data-table/row-arrow.svelte +28 -0
  32. package/src/components/data-table/row-context-menu.svelte +106 -0
  33. package/src/components/data-table/row-context-menu.test.ts +18 -0
  34. package/src/components/data-table/sortable-head.svelte +56 -0
  35. package/src/components/data-table/table-skeleton.svelte +83 -0
  36. package/src/components/hold-to-confirm/hold-to-confirm.svelte +232 -0
  37. package/src/components/hold-to-confirm/index.ts +1 -0
  38. package/src/components/icon/icon.svelte +23 -0
  39. package/src/components/icon/index.ts +1 -0
  40. package/src/components/language-flag/index.ts +1 -0
  41. package/src/components/language-flag/language-flag.svelte +60 -0
  42. package/src/components/list-page/detail-empty.svelte +50 -0
  43. package/src/components/list-page/detail-header.svelte +107 -0
  44. package/src/components/list-page/detail-panel.svelte +90 -0
  45. package/src/components/list-page/detail-row.svelte +36 -0
  46. package/src/components/list-page/detail-stack.svelte +33 -0
  47. package/src/components/list-page/empty-state.svelte +91 -0
  48. package/src/components/list-page/filter-item.svelte +49 -0
  49. package/src/components/list-page/filter-popover.svelte +88 -0
  50. package/src/components/list-page/filter-section.ts +12 -0
  51. package/src/components/list-page/grouped-list.svelte +116 -0
  52. package/src/components/list-page/index.ts +28 -0
  53. package/src/components/list-page/list-page-shell.svelte +195 -0
  54. package/src/components/list-page/list-table.ts +19 -0
  55. package/src/components/list-page/master-detail.svelte +206 -0
  56. package/src/components/list-page/master-detail.test.ts +37 -0
  57. package/src/components/list-page/notice-bar.svelte +68 -0
  58. package/src/components/list-page/prev-next.svelte +104 -0
  59. package/src/components/list-page/queue-rail.ts +25 -0
  60. package/src/components/list-page/rail-body.svelte +29 -0
  61. package/src/components/list-page/rail-empty.svelte +51 -0
  62. package/src/components/list-page/rail-head.svelte +49 -0
  63. package/src/components/list-page/rail-row.svelte +163 -0
  64. package/src/components/list-page/section-host.ts +24 -0
  65. package/src/components/list-page/section-label.svelte +39 -0
  66. package/src/components/list-page/step.test.ts +21 -0
  67. package/src/components/list-page/step.ts +10 -0
  68. package/src/components/list-page/toolbar-search.svelte +63 -0
  69. package/src/components/list-page/toolbar-trigger.ts +23 -0
  70. package/src/components/list-page/types.ts +1 -0
  71. package/src/components/locale-switch/index.ts +1 -0
  72. package/src/components/locale-switch/locale-switch.svelte +59 -0
  73. package/src/components/mark/index.ts +1 -0
  74. package/src/components/mark/mark.svelte +70 -0
  75. package/src/components/open-in/index.ts +6 -0
  76. package/src/components/open-in/open-in.svelte +106 -0
  77. package/src/components/picker/index.ts +4 -0
  78. package/src/components/picker/mark-picker.svelte +84 -0
  79. package/src/components/picker/picker.svelte +143 -0
  80. package/src/components/picker/trigger.ts +42 -0
  81. package/src/components/picker/types.ts +26 -0
  82. package/src/components/segmented/animated-tabs.svelte +175 -0
  83. package/src/components/segmented/collapse.test.ts +49 -0
  84. package/src/components/segmented/collapse.ts +39 -0
  85. package/src/components/segmented/index.ts +41 -0
  86. package/src/components/segmented/section-runs.svelte +79 -0
  87. package/src/components/segmented/segment.svelte +106 -0
  88. package/src/components/segmented/segmented-filter.svelte +125 -0
  89. package/src/components/segmented/segmented-nav.svelte +82 -0
  90. package/src/components/segmented/segmented-overflow.svelte +85 -0
  91. package/src/components/segmented/segmented-track.svelte +181 -0
  92. package/src/components/segmented/segmented.test.ts +20 -0
  93. package/src/components/segmented/segmented.ts +80 -0
  94. package/src/components/star-rating-input/index.ts +9 -0
  95. package/src/components/star-rating-input/star-rating-input.svelte +256 -0
  96. package/src/components/svg/index.ts +1 -0
  97. package/src/components/svg/svg.svelte +9 -0
  98. package/src/effects/border-beam/BorderBeam.svelte +1 -1
  99. package/src/effects/image-comparison/ImageComparison.css +42 -8
  100. package/src/effects/image-comparison/ImageComparison.svelte +138 -21
  101. package/src/effects/image-comparison/compare.test.ts +17 -0
  102. package/src/effects/image-comparison/compare.ts +5 -0
  103. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +338 -0
  104. package/src/effects/magic-card/MagicCard.css +47 -0
  105. package/src/effects/magic-card/MagicCard.svelte +57 -0
  106. package/src/effects/marquee/Marquee.css +27 -1
  107. package/src/effects/marquee/Marquee.svelte +67 -3
  108. package/src/effects/reveal/index.ts +2 -1
  109. package/src/effects/reveal/observe.test.ts +60 -0
  110. package/src/effects/reveal/observe.ts +33 -1
  111. package/src/effects/reveal/types.ts +7 -0
  112. package/src/effects/theme-toggle/ThemeToggle.css +199 -6
  113. package/src/effects/theme-toggle/ThemeToggle.svelte +51 -26
  114. package/src/gallery.test.ts +52 -0
  115. package/src/index.ts +5 -1
  116. package/src/lib/panel.ts +7 -1
  117. package/src/lib/sliding-pill.ts +68 -0
  118. package/src/lib/theme-transition.ts +41 -13
  119. package/src/lib/tone.ts +48 -0
  120. package/src/shadcn/accordion/accordion-trigger.svelte +15 -6
  121. package/src/shadcn/alert/alert.svelte +3 -1
  122. package/src/shadcn/badge/badge.svelte +8 -7
  123. package/src/shadcn/breadcrumb/breadcrumb-ellipsis.svelte +2 -2
  124. package/src/shadcn/breadcrumb/breadcrumb-separator.svelte +2 -2
  125. package/src/shadcn/calendar/calendar-month-select.svelte +2 -2
  126. package/src/shadcn/calendar/calendar-next-button.svelte +2 -2
  127. package/src/shadcn/calendar/calendar-prev-button.svelte +2 -2
  128. package/src/shadcn/calendar/calendar-year-select.svelte +2 -2
  129. package/src/shadcn/calendar/calendar.svelte +6 -0
  130. package/src/shadcn/code-block/code-block-actions.svelte +23 -0
  131. package/src/shadcn/code-block/code-block-content.svelte +55 -0
  132. package/src/shadcn/code-block/code-block-copy-button.svelte +10 -0
  133. package/src/shadcn/code-block/code-block-footer.svelte +20 -0
  134. package/src/shadcn/code-block/code-block-header.svelte +23 -0
  135. package/src/shadcn/code-block/code-block-line.svelte +71 -0
  136. package/src/shadcn/code-block/code-block-title.svelte +23 -0
  137. package/src/shadcn/code-block/code-block.svelte +47 -0
  138. package/src/shadcn/code-block/index.ts +42 -0
  139. package/src/shadcn/command/index.ts +3 -0
  140. package/src/shadcn/field/index.ts +3 -0
  141. package/src/shadcn/field/simple-field.svelte +71 -0
  142. package/src/shadcn/field/simple-field.test.ts +47 -0
  143. package/src/shadcn/hooks/highlighter.ts +132 -0
  144. package/src/shadcn/hooks/use-highlighted-code.svelte.ts +98 -0
  145. package/src/shadcn/kbd/index.ts +10 -0
  146. package/src/shadcn/kbd/kbd-group.svelte +20 -0
  147. package/src/shadcn/kbd/kbd.svelte +23 -0
  148. package/src/shadcn/message-scroller/index.ts +28 -0
  149. package/src/shadcn/message-scroller/message-scroller-button.svelte +52 -0
  150. package/src/shadcn/message-scroller/message-scroller-content.svelte +17 -0
  151. package/src/shadcn/message-scroller/message-scroller-item.svelte +22 -0
  152. package/src/shadcn/message-scroller/message-scroller-provider.svelte +7 -0
  153. package/src/shadcn/message-scroller/message-scroller-viewport.svelte +20 -0
  154. package/src/shadcn/message-scroller/message-scroller.svelte +20 -0
  155. package/src/shadcn/radio-group/radio-group-item.svelte +3 -2
  156. package/src/shadcn/sheet/sheet-content.svelte +1 -0
  157. package/src/shadcn/sheet/sheet.css +48 -0
  158. package/src/shadcn/sonner/sonner.svelte +7 -10
  159. package/src/shadcn/tabs/tabs-list.svelte +13 -2
  160. package/src/shadcn/tabs/tabs-trigger.svelte +1 -1
  161. package/src/shadcn/toggle-group/toggle-group.svelte +10 -3
  162. package/src/shadcn/trace/index.ts +29 -0
  163. package/src/shadcn/trace/trace-collapsible-content.svelte +29 -0
  164. package/src/shadcn/trace/trace-collapsible-trigger.svelte +29 -0
  165. package/src/shadcn/trace/trace-collapsible.svelte +20 -0
  166. package/src/shadcn/trace/trace-description.svelte +20 -0
  167. package/src/shadcn/trace/trace-icon.svelte +24 -0
  168. package/src/shadcn/trace/trace-line.svelte +31 -0
  169. package/src/shadcn/trace/trace-title.svelte +20 -0
  170. package/src/shadcn/trace/trace.svelte +23 -0
  171. package/src/ai/composer/Composer.css +0 -108
  172. package/src/ai/composer/Composer.svelte +0 -111
  173. package/src/ai/composer/index.ts +0 -10
  174. package/src/ai/conversation/Conversation.svelte +0 -28
  175. package/src/ai/conversation/ConversationContent.svelte +0 -35
  176. package/src/ai/conversation/ConversationEmptyState.svelte +0 -46
  177. package/src/ai/conversation/ConversationScrollButton.svelte +0 -51
  178. package/src/ai/conversation/index.ts +0 -33
  179. package/src/ai/conversation/stick-to-bottom-context.svelte.ts +0 -131
  180. package/src/animations/accordion.css +0 -58
  181. package/src/animations/icon-swap.css +0 -43
  182. package/src/animations/notification-badge.css +0 -81
  183. package/src/animations/panel.css +0 -86
  184. package/src/animations/reasoning-stream.css +0 -49
  185. package/src/animations/skeleton-reveal.css +0 -97
  186. package/src/animations/streaming-text.css +0 -31
  187. package/src/animations/tabs-sliding.css +0 -83
  188. package/src/animations/texts-reveal.css +0 -70
  189. package/src/animations/tooltip.css +0 -72
  190. package/src/animations/view-transition-reveal.ts +0 -74
  191. package/src/charts/animated-value.css +0 -7
  192. package/src/charts/animated-value.svelte +0 -110
  193. package/src/charts/area-chart.css +0 -29
  194. package/src/charts/area-chart.svelte +0 -195
  195. package/src/charts/bar-chart.css +0 -119
  196. package/src/charts/bar-chart.svelte +0 -269
  197. package/src/charts/charts.css +0 -201
  198. package/src/charts/crosshair.svelte +0 -35
  199. package/src/charts/donut-chart.css +0 -56
  200. package/src/charts/donut-chart.svelte +0 -158
  201. package/src/charts/edge-fade.svelte +0 -43
  202. package/src/charts/edge-fade.ts +0 -16
  203. package/src/charts/figure.css +0 -100
  204. package/src/charts/figure.svelte +0 -122
  205. package/src/charts/gauge-chart.css +0 -94
  206. package/src/charts/gauge-chart.svelte +0 -303
  207. package/src/charts/hover-band.test.ts +0 -30
  208. package/src/charts/hover-band.ts +0 -21
  209. package/src/charts/hover-focus.svelte +0 -85
  210. package/src/charts/index.ts +0 -30
  211. package/src/charts/legend.css +0 -131
  212. package/src/charts/legend.svelte +0 -112
  213. package/src/charts/line-chart.css +0 -26
  214. package/src/charts/line-chart.svelte +0 -256
  215. package/src/charts/motion.ts +0 -19
  216. package/src/charts/number-chart.css +0 -168
  217. package/src/charts/number-chart.svelte +0 -177
  218. package/src/charts/primitives/chart-container.svelte +0 -57
  219. package/src/charts/primitives/chart-style.svelte +0 -36
  220. package/src/charts/primitives/chart-tooltip.svelte +0 -161
  221. package/src/charts/primitives/chart-utils.ts +0 -54
  222. package/src/charts/primitives/index.ts +0 -11
  223. package/src/charts/reveal.svelte +0 -61
  224. package/src/charts/ring-chart.css +0 -96
  225. package/src/charts/ring-chart.svelte +0 -158
  226. package/src/charts/sparkline.css +0 -45
  227. package/src/charts/sparkline.svelte +0 -140
  228. package/src/charts/stat-card.css +0 -103
  229. package/src/charts/stat-card.svelte +0 -267
  230. package/src/charts/time-series-chart.css +0 -28
  231. package/src/charts/time-series-chart.svelte +0 -179
  232. package/src/charts/trend-pill.css +0 -38
  233. package/src/charts/trend-pill.svelte +0 -48
  234. package/src/lib/hooks/is-mobile.svelte.ts +0 -11
  235. package/src/lib/utils.ts +0 -45
  236. package/src/showcase/AiSection.svelte +0 -157
  237. package/src/showcase/AnimationsSection.svelte +0 -1658
  238. package/src/showcase/ChartsSection.svelte +0 -458
  239. package/src/showcase/EffectsSection.svelte +0 -152
  240. package/src/showcase/ShadcnSection.svelte +0 -27
  241. package/src/showcase/ShowcaseImport.svelte +0 -126
  242. package/src/showcase/ShowcaseList.svelte +0 -132
  243. package/src/showcase/ShowcaseNav.svelte +0 -247
  244. package/src/showcase/ShowcaseSection.svelte +0 -112
  245. package/src/showcase/ShowcaseShell.svelte +0 -55
  246. package/src/showcase/ShowcaseStage.svelte +0 -342
  247. package/src/showcase/ShowcaseVariant.svelte +0 -109
  248. package/src/showcase/assets/image-reveal/art-1.jpg +0 -0
  249. package/src/showcase/assets/image-reveal/art-2.jpg +0 -0
  250. package/src/showcase/assets/image-reveal/art-3.jpg +0 -0
  251. package/src/showcase/check.svg +0 -1
  252. package/src/showcase/chevron-arms.svg +0 -2
  253. package/src/showcase/chevron-down.svg +0 -1
  254. package/src/showcase/copy.svg +0 -2
  255. package/src/showcase/demos/ai/confirmation.svelte +0 -59
  256. package/src/showcase/demos/ai/conversation.svelte +0 -59
  257. package/src/showcase/demos/ai/loading-state.svelte +0 -19
  258. package/src/showcase/demos/ai/prompt-input.svelte +0 -57
  259. package/src/showcase/demos/ai/reasoning.svelte +0 -19
  260. package/src/showcase/demos/ai/thinking-dots.svelte +0 -15
  261. package/src/showcase/demos/ai/voice-waveform.svelte +0 -24
  262. package/src/showcase/demos/effects/animated-beam.svelte +0 -50
  263. package/src/showcase/demos/effects/animated-group.svelte +0 -47
  264. package/src/showcase/demos/effects/border-beam-default.svelte +0 -20
  265. package/src/showcase/demos/effects/border-beam-line.svelte +0 -20
  266. package/src/showcase/demos/effects/border-beam-mono.svelte +0 -20
  267. package/src/showcase/demos/effects/border-beam-sunset.svelte +0 -20
  268. package/src/showcase/demos/effects/image-comparison.svelte +0 -25
  269. package/src/showcase/demos/effects/image-reveal.svelte +0 -49
  270. package/src/showcase/demos/effects/in-view.svelte +0 -41
  271. package/src/showcase/demos/effects/integrations-hub.svelte +0 -82
  272. package/src/showcase/demos/effects/magnetic.svelte +0 -31
  273. package/src/showcase/demos/effects/marquee.svelte +0 -33
  274. package/src/showcase/demos/effects/progressive-blur.svelte +0 -47
  275. package/src/showcase/demos/effects/spotlight.svelte +0 -40
  276. package/src/showcase/demos/effects/text-effect.svelte +0 -55
  277. package/src/showcase/demos/shadcn/accordion.svelte +0 -45
  278. package/src/showcase/demos/shadcn/alert-dialog.svelte +0 -23
  279. package/src/showcase/demos/shadcn/alert.svelte +0 -30
  280. package/src/showcase/demos/shadcn/attachment.svelte +0 -71
  281. package/src/showcase/demos/shadcn/avatar.svelte +0 -30
  282. package/src/showcase/demos/shadcn/badge.svelte +0 -26
  283. package/src/showcase/demos/shadcn/breadcrumb.svelte +0 -34
  284. package/src/showcase/demos/shadcn/bubble.svelte +0 -36
  285. package/src/showcase/demos/shadcn/button.svelte +0 -11
  286. package/src/showcase/demos/shadcn/calendar.svelte +0 -8
  287. package/src/showcase/demos/shadcn/card.svelte +0 -39
  288. package/src/showcase/demos/shadcn/checkbox.svelte +0 -39
  289. package/src/showcase/demos/shadcn/collapsible.svelte +0 -20
  290. package/src/showcase/demos/shadcn/command.svelte +0 -48
  291. package/src/showcase/demos/shadcn/context-menu.svelte +0 -54
  292. package/src/showcase/demos/shadcn/demo-harness.svelte +0 -12
  293. package/src/showcase/demos/shadcn/demos.test.ts +0 -20
  294. package/src/showcase/demos/shadcn/dialog.svelte +0 -38
  295. package/src/showcase/demos/shadcn/dropdown-menu.svelte +0 -59
  296. package/src/showcase/demos/shadcn/empty.svelte +0 -29
  297. package/src/showcase/demos/shadcn/field.svelte +0 -115
  298. package/src/showcase/demos/shadcn/hover-card.svelte +0 -32
  299. package/src/showcase/demos/shadcn/index.ts +0 -358
  300. package/src/showcase/demos/shadcn/input-otp.svelte +0 -19
  301. package/src/showcase/demos/shadcn/input.svelte +0 -5
  302. package/src/showcase/demos/shadcn/label.svelte +0 -11
  303. package/src/showcase/demos/shadcn/marker.svelte +0 -30
  304. package/src/showcase/demos/shadcn/message.svelte +0 -75
  305. package/src/showcase/demos/shadcn/popover.svelte +0 -36
  306. package/src/showcase/demos/shadcn/progress.svelte +0 -13
  307. package/src/showcase/demos/shadcn/radio-group.svelte +0 -19
  308. package/src/showcase/demos/shadcn/scroll-area.svelte +0 -18
  309. package/src/showcase/demos/shadcn/select.svelte +0 -29
  310. package/src/showcase/demos/shadcn/separator.svelte +0 -12
  311. package/src/showcase/demos/shadcn/sheet.svelte +0 -48
  312. package/src/showcase/demos/shadcn/skeleton.svelte +0 -11
  313. package/src/showcase/demos/shadcn/slider.svelte +0 -7
  314. package/src/showcase/demos/shadcn/sonner.svelte +0 -18
  315. package/src/showcase/demos/shadcn/spinner.svelte +0 -11
  316. package/src/showcase/demos/shadcn/switch.svelte +0 -9
  317. package/src/showcase/demos/shadcn/table.svelte +0 -76
  318. package/src/showcase/demos/shadcn/tabs.svelte +0 -62
  319. package/src/showcase/demos/shadcn/textarea.svelte +0 -5
  320. package/src/showcase/demos/shadcn/toggle-group.svelte +0 -18
  321. package/src/showcase/demos/shadcn/toggle.svelte +0 -14
  322. package/src/showcase/demos/shadcn/tooltip.svelte +0 -13
  323. package/src/showcase/heart.svg +0 -1
  324. package/src/showcase/index.ts +0 -38
  325. package/src/showcase/kit.test.ts +0 -72
  326. package/src/showcase/kit.ts +0 -17
  327. package/src/showcase/plus.svg +0 -1
  328. package/src/showcase/showcase-shell.css +0 -91
  329. package/src/showcase/stacked-section.css +0 -15
  330. package/src/showcase/success-check.svg +0 -1
  331. package/src/showcase/types.ts +0 -29
  332. package/src/svg-raw.d.ts +0 -7
@@ -1,1658 +0,0 @@
1
- <!-- ========================== -->
2
- <!-- IMPORTS -->
3
- <!-- ========================== -->
4
- <script lang="ts">
5
- import { untrack } from "svelte";
6
- import { swapText } from "../animations/text-swap.js";
7
- import Bell from "@lucide/svelte/icons/bell";
8
- import Info from "@lucide/svelte/icons/info";
9
- import Menu from "@lucide/svelte/icons/menu";
10
- import X from "@lucide/svelte/icons/x";
11
- import ShowcaseStage from "./ShowcaseStage.svelte";
12
- import chevronArms from "./chevron-arms.svg?raw";
13
- import chevronDown from "./chevron-down.svg?raw";
14
- import heart from "./heart.svg?raw";
15
- import plus from "./plus.svg?raw";
16
- import successCheck from "./success-check.svg?raw";
17
-
18
- // =========================
19
- // CONST
20
- // =========================
21
- const CLEAR_TEXT = "project deadline";
22
-
23
- // =========================
24
- // STATE
25
- // =========================
26
- let target = null;
27
- const phrases = [
28
- "Build the system.",
29
- "Ship the route.",
30
- "Verify the output.",
31
- "Repeat tomorrow.",
32
- ];
33
- let cursor = 0;
34
-
35
- let digitGroup = null;
36
- let digitAnimating = $state(false);
37
- let digitValue = $state("1234");
38
-
39
- let resizeSmall = $state(false);
40
- let badgeOpen = $state(true);
41
- let badgeCount = $state(3);
42
- let activePage = $state<"1" | "2">("1");
43
-
44
- let modalOpen = $state(true);
45
- let dropdownOpen = $state(true);
46
- let panelOpen = $state(true);
47
- let iconState = $state<"a" | "b">("a");
48
-
49
- let checkEl = null;
50
-
51
- let avatarRoot = null;
52
- // Dummy team members so the hover-lift reads as a real avatar stack.
53
- const avatars = [
54
- { initials: "RK", hue: 24 },
55
- { initials: "KA", hue: 158 },
56
- { initials: "AM", hue: 268 },
57
- { initials: "MT", hue: 334 },
58
- { initials: "ZL", hue: 200 },
59
- ];
60
-
61
- let shakeWrap = null;
62
- let shakeInput = null;
63
- let shakeRevert;
64
-
65
- let clearWrap = null;
66
- let clearInput = null;
67
- let clearMirror = null;
68
- let clearPhold = null;
69
- let clearGlow = null;
70
- let clearBusy = false;
71
-
72
- let skelEl = null;
73
- let skelSkeleton = null;
74
-
75
- let tabsBar = null;
76
- let tabsPill = null;
77
- const tabs = ["Plan", "Debug", "Ask"];
78
- let activeTab = $state(0);
79
-
80
- let staggerEl = null;
81
-
82
- // =========================
83
- // EFFECTS
84
- // =========================
85
- $effect(() => {
86
- const checkPath = checkEl?.querySelector("path");
87
- if (!checkPath) return;
88
- const len = Math.ceil(checkPath.getTotalLength());
89
- checkPath.style.strokeDasharray = String(len);
90
- checkPath.style.strokeDashoffset = String(len);
91
- });
92
-
93
- $effect(() => {
94
- untrack(() => positionTabsPill(false));
95
- const onResize = () => untrack(() => positionTabsPill(false));
96
- window.addEventListener("resize", onResize);
97
- return () => window.removeEventListener("resize", onResize);
98
- });
99
-
100
- // =========================
101
- // FUNCTIONS
102
- // =========================
103
- function cssNum(name: string, fallback: number) {
104
- const v = parseFloat(getComputedStyle(document.documentElement).getPropertyValue(name));
105
- return Number.isFinite(v) ? v : fallback;
106
- }
107
- function nextPhrase() {
108
- if (!target) return;
109
- cursor = (cursor + 1) % phrases.length;
110
- swapText(target, phrases[cursor]);
111
- }
112
-
113
- function replayDigits() {
114
- if (!digitGroup) return;
115
- digitAnimating = false;
116
- digitValue = String(Math.floor(1000 + Math.random() * 8999));
117
- // WHY: force reflow so removing+re-adding .is-animating restarts the keyframes
118
- void digitGroup.offsetWidth;
119
- digitAnimating = true;
120
- }
121
-
122
- function bumpBadge() {
123
- badgeOpen = false;
124
- setTimeout(() => {
125
- badgeCount += 1;
126
- badgeOpen = true;
127
- }, 220);
128
- }
129
-
130
- function togglePage() {
131
- activePage = activePage === "1" ? "2" : "1";
132
- }
133
-
134
- function replayCheck() {
135
- if (!checkEl) return;
136
- checkEl.setAttribute("data-state", "out");
137
- // WHY: force reflow so the appear keyframes restart from offset 0
138
- void checkEl.offsetWidth;
139
- checkEl.setAttribute("data-state", "in");
140
- }
141
-
142
- function liftAvatars(activeIdx: number | null) {
143
- if (!avatarRoot) return;
144
- const lift = cssNum("--avatar-lift", -4);
145
- const falloff = cssNum("--avatar-falloff", 0.45);
146
- const scale = cssNum("--avatar-scale", 1.05);
147
- const ease = "cubic-bezier(0.22, 1, 0.36, 1)";
148
- avatarRoot.querySelectorAll(".t-avatar").forEach((el, i) => {
149
- el.style.transitionTimingFunction = ease;
150
- if (activeIdx === null) {
151
- el.style.setProperty("--shift", "0px");
152
- el.style.setProperty("--scale-active", "1");
153
- return;
154
- }
155
- const d = Math.abs(i - activeIdx);
156
- el.style.setProperty("--shift", `${(lift * Math.pow(falloff, d)).toFixed(3)}px`);
157
- el.style.setProperty("--scale-active", i === activeIdx ? String(scale) : "1");
158
- });
159
- }
160
-
161
- function showError() {
162
- if (!shakeWrap || !shakeInput) return;
163
- shakeWrap.classList.add("is-error");
164
- shakeInput.classList.add("is-error");
165
- // Replay the shake from a clean baseline.
166
- shakeInput.classList.remove("is-shaking");
167
- void shakeInput.offsetWidth;
168
- shakeInput.classList.add("is-shaking");
169
- const shakeMs = cssNum("--shake-dur-a", 80) * 2 + cssNum("--shake-dur-b", 60) * 2;
170
- setTimeout(() => shakeInput?.classList.remove("is-shaking"), shakeMs + 20);
171
- if (shakeRevert) clearTimeout(shakeRevert);
172
- const hold = cssNum("--revert-hold", 3000);
173
- shakeRevert = setTimeout(() => {
174
- shakeWrap?.classList.remove("is-error");
175
- shakeInput?.classList.remove("is-error");
176
- }, shakeMs + hold);
177
- }
178
-
179
- // Minimal cubic-bezier sampler so the JS easing matches the CSS curves.
180
- function cubicBezier(str: string) {
181
- const m = str.match(/cubic-bezier\(([-\d.]+),\s*([-\d.]+),\s*([-\d.]+),\s*([-\d.]+)\)/);
182
- if (!m) return (t: number) => t;
183
- const [x1, y1, x2, y2] = m.slice(1).map(Number);
184
- const cx = 3 * x1;
185
- const bx = 3 * (x2 - x1) - cx;
186
- const ax = 1 - cx - bx;
187
- const cy = 3 * y1;
188
- const by = 3 * (y2 - y1) - cy;
189
- const ay = 1 - cy - by;
190
- return (t: number) => {
191
- if (t <= 0) return 0;
192
- if (t >= 1) return 1;
193
- let s = t;
194
- for (let i = 0; i < 8; i++) {
195
- const dx = ((ax * s + bx) * s + cx) * s - t;
196
- const d = (3 * ax * s + 2 * bx) * s + cx;
197
- if (Math.abs(dx) < 1e-6 || d === 0) break;
198
- s -= dx / d;
199
- }
200
- return ((ay * s + by) * s + cy) * s;
201
- };
202
- }
203
-
204
- function buildClearGlow(text: string, isDark: boolean) {
205
- if (!clearInput || !clearWrap) return "";
206
- const ctx = document.createElement("canvas").getContext("2d");
207
- if (!ctx) return "";
208
- ctx.font = getComputedStyle(clearInput).font;
209
- const rgb = isDark ? "255,255,255" : "0,0,0";
210
- const w = clearWrap.clientWidth || 280;
211
- const padLeft = parseFloat(getComputedStyle(clearInput).paddingLeft) || 12;
212
- const spread = cssNum("--glow-spread", 1.5);
213
- const layers: string[] = [];
214
- let x = 0;
215
- text.split(/(\s+)/).forEach((seg) => {
216
- const segW = ctx.measureText(seg).width;
217
- if (seg.trim()) {
218
- const cx = padLeft + x + segW / 2;
219
- const hw = Math.max(segW * 0.45, 8) * spread;
220
- (
221
- [
222
- [0, 0.8, 7, 0.22],
223
- [hw * 0.45, 0.55, 8, 0.18],
224
- [-hw * 0.4, 0.65, 6, 0.16],
225
- [hw * 0.15, 0.9, 5, 0.14],
226
- ] as const
227
- ).forEach(([dx, rwm, rh, a]) => {
228
- const lx = (((cx + dx) / w) * 100).toFixed(2);
229
- layers.push(
230
- `radial-gradient(ellipse ${Math.max(hw * rwm, 2).toFixed(1)}px ${rh}px at ${lx}% 100%, rgba(${rgb},${a}), transparent)`,
231
- );
232
- });
233
- }
234
- x += segW;
235
- });
236
- return layers.join(", ");
237
- }
238
-
239
- function triggerClear() {
240
- if (clearBusy || !clearWrap || !clearInput || !clearMirror || !clearPhold || !clearGlow) return;
241
- // Repeatable demo: an empty field refills instantly instead of clearing.
242
- if (!clearInput.value) {
243
- clearInput.value = CLEAR_TEXT;
244
- clearMirror.textContent = CLEAR_TEXT;
245
- clearWrap.classList.add("has-value");
246
- return;
247
- }
248
- clearBusy = true;
249
- const isDark = !document.documentElement.classList.contains("light");
250
- const total = cssNum("--clear-dur", 1000);
251
- const outDur = cssNum("--clear-out-dur", 400);
252
- const inDur = cssNum("--clear-in-dur", 400);
253
- const outFly = cssNum("--clear-out-fly", 12);
254
- const inFly = cssNum("--clear-in-fly", 12);
255
- const blur = cssNum("--clear-blur", 2);
256
- const delay = cssNum("--glow-delay", 50);
257
- const peakAt = cssNum("--glow-peak-at", 0.15);
258
- const gOp = isDark ? 0.85 : cssNum("--glow-opacity", 0.42);
259
- const cs = getComputedStyle(document.documentElement);
260
- const easeOut = cubicBezier(cs.getPropertyValue("--clear-out-ease"));
261
- const easeIn = cubicBezier(cs.getPropertyValue("--clear-in-ease"));
262
-
263
- const text = clearInput.value;
264
- clearMirror.textContent = text;
265
- clearInput.value = "";
266
- clearWrap.classList.remove("has-value");
267
- clearWrap.classList.add("is-clearing");
268
- clearGlow.style.mixBlendMode = isDark ? "screen" : "multiply";
269
- clearGlow.style.background = buildClearGlow(text, isDark);
270
- clearGlow.style.opacity = "0";
271
- clearPhold.style.transform = `translateY(-${inFly}px)`;
272
- clearPhold.style.opacity = "0.9";
273
- clearPhold.style.filter = `blur(${blur}px)`;
274
-
275
- const start = performance.now();
276
- const tick = (now: number) => {
277
- if (!clearWrap || !clearMirror || !clearPhold || !clearGlow) return;
278
- const el = now - start;
279
- const eo = easeOut(Math.min(1, el / outDur));
280
- clearMirror.style.transform = `translateY(${(eo * outFly).toFixed(1)}px)`;
281
- clearMirror.style.opacity = (1 - eo).toFixed(3);
282
- clearMirror.style.filter = `blur(${(eo * blur).toFixed(1)}px)`;
283
-
284
- const ei = easeIn(Math.min(1, el / inDur));
285
- clearPhold.style.transform = `translateY(${(-inFly + ei * inFly).toFixed(1)}px)`;
286
- clearPhold.style.opacity = (0.9 + ei * 0.1).toFixed(3);
287
- clearPhold.style.filter = `blur(${(blur - ei * blur).toFixed(1)}px)`;
288
-
289
- let g = 0;
290
- if (el > delay) {
291
- const gp = Math.min(1, (el - delay) / Math.max(1, total - delay));
292
- g = gp < peakAt ? gp / peakAt : 1 - (gp - peakAt) / (1 - peakAt);
293
- }
294
- clearGlow.style.opacity = (g * gOp).toFixed(3);
295
-
296
- if (el < total) {
297
- requestAnimationFrame(tick);
298
- } else {
299
- clearWrap.classList.remove("is-clearing");
300
- clearMirror.style.cssText = "";
301
- clearPhold.style.cssText = "";
302
- clearMirror.textContent = "";
303
- clearGlow.style.opacity = "0";
304
- clearGlow.style.background = "";
305
- clearBusy = false;
306
- }
307
- };
308
- requestAnimationFrame(tick);
309
- }
310
-
311
- function replaySkeleton() {
312
- if (!skelEl || !skelSkeleton) return;
313
- skelEl.classList.add("is-resetting");
314
- skelEl.classList.remove("is-revealed");
315
- skelSkeleton.classList.remove("is-pulsing");
316
- void skelSkeleton.offsetWidth;
317
- skelEl.classList.remove("is-resetting");
318
- skelSkeleton.classList.add("is-pulsing");
319
- const total = cssNum("--pulse-dur", 1000) * cssNum("--pulse-count", 1);
320
- setTimeout(() => skelEl?.classList.add("is-revealed"), total);
321
- }
322
-
323
- function positionTabsPill(animate: boolean) {
324
- if (!tabsBar || !tabsPill) return;
325
- const tab = tabsBar.querySelectorAll(".t-tab")[activeTab];
326
- if (!tab) return;
327
- if (animate) {
328
- tabsPill.style.transform = `translateX(${tab.offsetLeft}px)`;
329
- tabsPill.style.width = `${tab.offsetWidth}px`;
330
- } else {
331
- const prev = tabsPill.style.transition;
332
- tabsPill.style.transition = "none";
333
- tabsPill.style.transform = `translateX(${tab.offsetLeft}px)`;
334
- tabsPill.style.width = `${tab.offsetWidth}px`;
335
- void tabsPill.offsetWidth;
336
- tabsPill.style.transition = prev;
337
- }
338
- }
339
-
340
- function selectTab(i: number) {
341
- activeTab = i;
342
- positionTabsPill(true);
343
- }
344
-
345
- function replayStagger() {
346
- if (!staggerEl) return;
347
- staggerEl.classList.remove("is-hiding");
348
- staggerEl.classList.remove("is-shown");
349
- void staggerEl.offsetHeight;
350
- staggerEl.classList.add("is-shown");
351
- }
352
-
353
- // =========================
354
- // THE NINE THE SECTION USED TO SKIP
355
- // =========================
356
- // Four are pure CSS. The rest carry the small JS each upstream usage note
357
- // calls for; the markup follows `.upstream/transitions/<slug>.css`.
358
- let accOpen = $state(true);
359
- let morphOpen = $state(false);
360
- let liked = $state(false);
361
- let bursting = $state(false);
362
-
363
- let tiltEl = null;
364
- let tiltCard = null;
365
- let reelEl = null;
366
- let streamEl = null;
367
- let reasonScroll = null;
368
- let thinkEl = null;
369
-
370
- const STREAM_TEXT =
371
- "The parser reads the diff, keeps the hunks it can apply, and hands the rest back with a reason.";
372
- const THINK_STATES = ["Thinking...", "Reading the repository...", "Drafting the patch..."];
373
- const REASON_LINES = [
374
- "Checking the lockfile for a version that moved.",
375
- "Two primitives changed upstream since the last sync.",
376
- "One of them is retuned here, so it stays ours.",
377
- "Writing the other and formatting what landed.",
378
- ];
379
-
380
- function tiltMove(event: PointerEvent) {
381
- const rect = tiltEl.getBoundingClientRect();
382
- const x = (event.clientX - rect.left) / rect.width;
383
- const y = (event.clientY - rect.top) / rect.height;
384
- tiltCard.classList.add("is-tilting");
385
- tiltEl.classList.add("is-hover");
386
- tiltEl.style.setProperty("--tilt-rx", `${(0.5 - y) * 14}deg`);
387
- tiltEl.style.setProperty("--tilt-ry", `${(x - 0.5) * 14}deg`);
388
- tiltEl.style.setProperty("--tilt-gx", `${x * 100}%`);
389
- tiltEl.style.setProperty("--tilt-gy", `${y * 100}%`);
390
- }
391
-
392
- function tiltLeave() {
393
- tiltCard.classList.remove("is-tilting");
394
- tiltEl.classList.remove("is-hover");
395
- tiltEl.style.setProperty("--tilt-rx", "0deg");
396
- tiltEl.style.setProperty("--tilt-ry", "0deg");
397
- }
398
-
399
- // A spray nobody can predict: every dot takes its own angle, distance and
400
- // clock, which is what keeps two likes from looking like one animation.
401
- function toggleLike(event: MouseEvent) {
402
- liked = !liked;
403
- if (!liked) return;
404
- const root = event.currentTarget as HTMLElement;
405
- for (const dot of root.querySelectorAll<HTMLElement>(".t-like-particles i")) {
406
- const angle = Math.random() * Math.PI * 2;
407
- const dist = 14 + Math.random() * 14;
408
- dot.style.setProperty("--px", `${Math.cos(angle) * dist}px`);
409
- dot.style.setProperty("--py", `${Math.sin(angle) * dist}px`);
410
- dot.style.setProperty("--pdur", `${480 + Math.random() * 240}ms`);
411
- dot.style.setProperty("--pdelay", `${Math.random() * 60}ms`);
412
- dot.style.setProperty("--psize", `${0.7 + Math.random() * 0.8}`);
413
- }
414
- bursting = false;
415
- void root.offsetHeight;
416
- bursting = true;
417
- setTimeout(() => (bursting = false), 700);
418
- }
419
-
420
- /** One column per digit, each clipping a 0-9 strip it spins past. */
421
- function spinReel(value = "482") {
422
- reelEl.replaceChildren();
423
- value.split("").forEach((digit, index) => {
424
- const col = document.createElement("div");
425
- col.className = "t-reel-col";
426
- const strip = document.createElement("div");
427
- strip.className = "t-reel-strip";
428
- for (let n = 0; n < 30; n++) {
429
- const cell = document.createElement("div");
430
- cell.className = "t-reel-digit";
431
- cell.textContent = String(n % 10);
432
- strip.append(cell);
433
- }
434
- col.append(strip);
435
- reelEl.append(col);
436
- const cells = 20 + Number(digit);
437
- strip.style.transform = "translateY(0)";
438
- strip.style.filter = "blur(0px)";
439
- void strip.offsetHeight;
440
- strip.style.transition = `transform var(--reel-dur) var(--reel-ease) ${index * 90}ms, filter var(--reel-dur) linear ${index * 90}ms`;
441
- strip.style.transform = `translateY(calc(var(--reel-cell) * -${cells}))`;
442
- });
443
- }
444
-
445
- /** Wrap each word once, then let them in one --stream-gap apart. */
446
- function replayStream() {
447
- const words = streamEl.querySelectorAll<HTMLElement>(".t-stream-w");
448
- for (const word of words) {
449
- word.style.transition = "none";
450
- word.classList.remove("is-in");
451
- }
452
- void streamEl.offsetHeight;
453
- words.forEach((word, index) => {
454
- word.style.transition = "";
455
- setTimeout(() => word.classList.add("is-in"), index * 60);
456
- });
457
- }
458
-
459
- // The two that run on their own: a transcript stepping up while a model
460
- // thinks, and the line under it swapping state.
461
- $effect(() => {
462
- if (!reasonScroll || !thinkEl) return;
463
- replayStream();
464
- spinReel();
465
-
466
- let offset = 0;
467
- const step = setInterval(() => {
468
- offset = (offset + 2) % REASON_LINES.length;
469
- reasonScroll.style.transition = "transform var(--reason-step) var(--reason-ease)";
470
- reasonScroll.style.transform = `translateY(calc(var(--reason-line) * -${offset}))`;
471
- }, 840);
472
-
473
- let state = 0;
474
- const swap = setInterval(() => {
475
- state = (state + 1) % THINK_STATES.length;
476
- thinkEl.classList.add("is-exit");
477
- setTimeout(() => {
478
- thinkEl.textContent = THINK_STATES[state];
479
- thinkEl.dataset.text = THINK_STATES[state];
480
- thinkEl.classList.remove("is-exit");
481
- thinkEl.classList.add("is-enter-start");
482
- void thinkEl.offsetHeight;
483
- thinkEl.classList.remove("is-enter-start");
484
- }, 200);
485
- }, 2000);
486
-
487
- return () => {
488
- clearInterval(step);
489
- clearInterval(swap);
490
- };
491
- });
492
- </script>
493
-
494
- <!-- ========================== -->
495
- <!-- MARKUP -->
496
- <!-- ========================== -->
497
- <div class="grid">
498
- <ShowcaseStage
499
- title="Text states swap"
500
- note="Text swap with translate and blur"
501
- importPath={"import { swapText } from '@heyramzi/ui/animations/text-swap';"}
502
- onaction={nextPhrase}
503
- >
504
- <span bind:this={target} class="phrase">{phrases[0]}</span>
505
- </ShowcaseStage>
506
-
507
- <ShowcaseStage
508
- title="Number pop-in"
509
- note="Digit flip with blur and stagger"
510
- importPath={"import '@heyramzi/ui/animations/digit-pop-in.css';"}
511
- onaction={replayDigits}
512
- >
513
- <span
514
- bind:this={digitGroup}
515
- class="t-digit-group digit-display"
516
- class:is-animating={digitAnimating}
517
- >
518
- {#each digitValue.split("") as ch, i (i + ch)}
519
- <span class="t-digit" data-stagger={i}>{ch}</span>
520
- {/each}
521
- </span>
522
- </ShowcaseStage>
523
-
524
- <ShowcaseStage
525
- title="Card resize"
526
- note="Smooth width and height transition"
527
- importPath={"import '@heyramzi/ui/animations/resize.css';"}
528
- onaction={() => (resizeSmall = !resizeSmall)}
529
- >
530
- <div class="t-resize sk-card" class:is-small={resizeSmall}>
531
- <span class="sk sk-1"></span>
532
- <span class="sk sk-2"></span>
533
- <span class="sk sk-3"></span>
534
- <span class="sk sk-4"></span>
535
- <span class="sk sk-5"></span>
536
- <span class="sk sk-6"></span>
537
- </div>
538
- </ShowcaseStage>
539
-
540
- <ShowcaseStage
541
- title="Notification badge"
542
- note="Diagonal slide with spring pop-in"
543
- importPath={"import '@heyramzi/ui/animations/notification-badge.css';"}
544
- onaction={bumpBadge}
545
- >
546
- <button type="button" class="bell-trigger" onclick={bumpBadge} aria-label="Notifications">
547
- <Bell class="size-5" />
548
- <span class="t-badge" data-open={badgeOpen ? "true" : "false"}>
549
- <span class="t-badge-dot">{badgeCount}</span>
550
- </span>
551
- </button>
552
- </ShowcaseStage>
553
-
554
- <ShowcaseStage
555
- title="Modal open / close"
556
- note="Scale and blur transition"
557
- importPath={"import '@heyramzi/ui/animations/modal.css';"}
558
- onaction={() => (modalOpen = !modalOpen)}
559
- >
560
- <div class="t-modal sk-modal" class:is-open={modalOpen}>
561
- <div class="sk sk-mtitle"></div>
562
- <div class="sk sk-mclose"></div>
563
- <div class="sk sk-line full"></div>
564
- <div class="sk sk-line full"></div>
565
- <div class="sk sk-line three-q"></div>
566
- <div class="sk-pills">
567
- <div class="sk sk-pill"></div>
568
- <div class="sk sk-pill"></div>
569
- </div>
570
- </div>
571
- </ShowcaseStage>
572
-
573
- <ShowcaseStage
574
- title="Menu dropdown"
575
- note="Origin-aware open and close"
576
- importPath={"import '@heyramzi/ui/animations/dropdown.css';"}
577
- onaction={() => (dropdownOpen = !dropdownOpen)}
578
- >
579
- <div class="dropdown-stack">
580
- <div class="sk-trigger"></div>
581
- <div class="t-dropdown sk-dropdown" class:is-open={dropdownOpen}>
582
- <div class="sk sk-row full"></div>
583
- <div class="sk sk-row three-q"></div>
584
- <div class="sk sk-row half"></div>
585
- </div>
586
- </div>
587
- </ShowcaseStage>
588
-
589
- <ShowcaseStage
590
- title="Panel reveal"
591
- note="Translate and blur entrance"
592
- importPath={"import '@heyramzi/ui/animations/panel.css';"}
593
- onaction={() => (panelOpen = !panelOpen)}
594
- >
595
- <div class="panel-clip">
596
- <div class="t-panel-slide sk-panel" data-open={panelOpen ? "true" : "false"}>
597
- <div class="sk sk-line full"></div>
598
- <div class="sk sk-line three-q"></div>
599
- <div class="sk-tiles">
600
- <div class="sk sk-tile"></div>
601
- <div class="sk sk-tile"></div>
602
- <div class="sk sk-tile"></div>
603
- <div class="sk sk-tile"></div>
604
- </div>
605
- </div>
606
- </div>
607
- </ShowcaseStage>
608
-
609
- <ShowcaseStage
610
- title="Page side-by-side"
611
- note="Forward and back page slide"
612
- importPath={"import '@heyramzi/ui/animations/page-side-by-side.css';"}
613
- actionLabel={activePage === "1" ? "Next" : "Back"}
614
- onaction={togglePage}
615
- >
616
- <div class="t-page-slide sk-page-stage" data-page={activePage}>
617
- <section class="t-page sk-page" data-page-id="1">
618
- <div class="sk sk-row full"></div>
619
- <div class="sk sk-row three-q"></div>
620
- <div class="sk sk-row half"></div>
621
- </section>
622
- <section class="t-page sk-page sk-page-2" data-page-id="2">
623
- <div class="sk-amount">$10</div>
624
- <div class="sk-pill-row">
625
- <div class="sk sk-mini-pill"></div>
626
- <div class="sk sk-mini-pill"></div>
627
- <div class="sk sk-mini-pill"></div>
628
- </div>
629
- </section>
630
- </div>
631
- </ShowcaseStage>
632
-
633
- <ShowcaseStage
634
- title="Icon swap"
635
- note="Crossfade with blur between icons"
636
- importPath={"import '@heyramzi/ui/animations/icon-swap.css';"}
637
- onaction={() => (iconState = iconState === "a" ? "b" : "a")}
638
- >
639
- <div class="t-icon-swap icon-stack" data-state={iconState}>
640
- <div class="t-icon" data-icon="a"><Menu class="size-7" /></div>
641
- <div class="t-icon" data-icon="b"><X class="size-7" /></div>
642
- </div>
643
- </ShowcaseStage>
644
-
645
- <ShowcaseStage
646
- title="Success check"
647
- note="Fade, rotate, bob, and stroke draw"
648
- importPath={"import '@heyramzi/ui/animations/success-check.css';"}
649
- onaction={replayCheck}
650
- >
651
- <div class="check-badge">
652
- <span bind:this={checkEl} class="t-success-check" data-state="in" aria-hidden="true">
653
- {@html successCheck}
654
- </span>
655
- </div>
656
- </ShowcaseStage>
657
-
658
- <ShowcaseStage
659
- title="Avatar group hover"
660
- note="Distance falloff lift with spring return"
661
- importPath={"import '@heyramzi/ui/animations/avatar-group-hover.css';"}
662
- >
663
- <div
664
- bind:this={avatarRoot}
665
- class="t-avatar-group avatar-row"
666
- role="group"
667
- aria-label="Team"
668
- onmouseleave={() => liftAvatars(null)}
669
- >
670
- {#each avatars as person, i (person.initials)}
671
- <div
672
- class="t-avatar avatar-chip"
673
- style="z-index: {avatars.length -
674
- i}; background: linear-gradient(145deg, hsl({person.hue} 52% 52%), hsl({person.hue} 56% 38%)); color: #fff;"
675
- onmouseenter={() => liftAvatars(i)}
676
- >
677
- {person.initials}
678
- </div>
679
- {/each}
680
- </div>
681
- </ShowcaseStage>
682
-
683
- <ShowcaseStage
684
- title="Error state shake"
685
- note="Percussive shake with auto-revert"
686
- importPath={"import '@heyramzi/ui/animations/input-shake.css';"}
687
- actionLabel="Submit"
688
- onaction={showError}
689
- >
690
- <div bind:this={shakeWrap} class="t-input-wrap shake-wrap">
691
- <div bind:this={shakeInput} class="t-input shake-input">name@email</div>
692
- <p class="t-error-msg shake-msg">Enter a valid email address.</p>
693
- </div>
694
- </ShowcaseStage>
695
-
696
- <ShowcaseStage
697
- title="Input clear"
698
- note="Fly-out text with per-word streak"
699
- importPath={"import '@heyramzi/ui/animations/input-clear-dissolve.css';"}
700
- actionLabel="Clear"
701
- onaction={triggerClear}
702
- >
703
- <div bind:this={clearWrap} class="t-clear clear-field has-value">
704
- <input
705
- bind:this={clearInput}
706
- class="clear-input"
707
- type="text"
708
- value={CLEAR_TEXT}
709
- readonly
710
- aria-label="Search"
711
- />
712
- <div bind:this={clearMirror} class="t-clear-mirror clear-text" aria-hidden="true">
713
- {CLEAR_TEXT}
714
- </div>
715
- <div
716
- bind:this={clearPhold}
717
- class="t-clear-placeholder clear-text clear-ph"
718
- aria-hidden="true"
719
- >
720
- Search
721
- </div>
722
- <div bind:this={clearGlow} class="t-clear-glow" aria-hidden="true"></div>
723
- <button type="button" class="t-clear-btn clear-x" onclick={triggerClear} aria-label="Clear">
724
- <X class="size-3.5" />
725
- </button>
726
- </div>
727
- </ShowcaseStage>
728
-
729
- <ShowcaseStage
730
- title="Skeleton loader"
731
- note="Pulse, then cross-blur to content"
732
- importPath={"import '@heyramzi/ui/animations/skeleton-reveal.css';"}
733
- actionLabel="Reload"
734
- onaction={replaySkeleton}
735
- >
736
- <div bind:this={skelEl} class="t-skel skel-card is-revealed">
737
- <div bind:this={skelSkeleton} class="t-skel-skeleton skel-layer">
738
- <span class="sk skel-avatar"></span>
739
- <span class="skel-bars">
740
- <span class="sk skel-l1"></span>
741
- <span class="sk skel-l2"></span>
742
- </span>
743
- </div>
744
- <div class="t-skel-content skel-layer">
745
- <div class="skel-avatar-real">R</div>
746
- <div class="skel-lines">
747
- <div class="skel-name">Ramzi K.</div>
748
- <div class="skel-sub">Shipped 14 routes</div>
749
- </div>
750
- </div>
751
- </div>
752
- </ShowcaseStage>
753
-
754
- <ShowcaseStage
755
- title="Shimmer text"
756
- note="Sweeping highlight on a loop"
757
- importPath={"import '@heyramzi/ui/animations/shimmer-text.css';"}
758
- >
759
- <span class="t-shimmer shimmer-demo" data-text="Generating response">Generating response</span>
760
- </ShowcaseStage>
761
-
762
- <ShowcaseStage
763
- title="Tabs sliding"
764
- note="Active pill slides between options"
765
- importPath={"import '@heyramzi/ui/animations/tabs-sliding.css';"}
766
- >
767
- <div bind:this={tabsBar} class="t-tabs tabs-demo" role="tablist" aria-label="View">
768
- <span bind:this={tabsPill} class="t-tabs-pill" aria-hidden="true"></span>
769
- {#each tabs as label, i (label)}
770
- <button
771
- type="button"
772
- class="t-tab"
773
- role="tab"
774
- aria-selected={i === activeTab}
775
- onclick={() => selectTab(i)}
776
- >
777
- {label}
778
- </button>
779
- {/each}
780
- </div>
781
- </ShowcaseStage>
782
-
783
- <ShowcaseStage
784
- title="Tooltip"
785
- note="Delayed fade-in, instant out"
786
- importPath={"import '@heyramzi/ui/animations/tooltip.css';"}
787
- >
788
- <span class="t-tt-wrap tt-demo">
789
- <button type="button" class="t-tt-trigger tt-trigger" aria-describedby="tt-anim">
790
- <Info class="size-5" />
791
- </button>
792
- <span class="t-tt tt-bubble" id="tt-anim" role="tooltip">Keyboard shortcuts</span>
793
- </span>
794
- </ShowcaseStage>
795
-
796
- <ShowcaseStage
797
- title="Texts reveal"
798
- note="Staggered blurred rise"
799
- importPath={"import '@heyramzi/ui/animations/texts-reveal.css';"}
800
- actionLabel="Replay"
801
- onaction={replayStagger}
802
- >
803
- <div bind:this={staggerEl} class="t-stagger stagger-demo is-shown">
804
- <strong class="t-stagger-line t-stagger-line--1 stagger-head">Ship with rhythm</strong>
805
- <span class="t-stagger-line t-stagger-line--2 stagger-sub"
806
- >Headline first, the rest follows.</span
807
- >
808
- </div>
809
- </ShowcaseStage>
810
-
811
- <ShowcaseStage
812
- title="Accordion"
813
- note="Grid-row expand, no measured height"
814
- importPath={"import '@heyramzi/ui/animations/accordion.css';"}
815
- >
816
- <div class="t-acc acc-demo" data-open={accOpen ? "true" : "false"}>
817
- <button
818
- type="button"
819
- class="t-acc-head acc-head"
820
- aria-expanded={accOpen}
821
- onclick={() => (accOpen = !accOpen)}
822
- >
823
- Shipping details
824
- <span class="t-acc-chevron">
825
- {@html chevronDown}
826
- </span>
827
- </button>
828
- <div class="t-acc-panel">
829
- <div class="t-acc-panel-inner">
830
- <p class="acc-body">
831
- Standard delivery takes three to five days. Express arrives the next morning, tracked
832
- end to end.
833
- </p>
834
- </div>
835
- </div>
836
- </div>
837
- </ShowcaseStage>
838
-
839
- <ShowcaseStage
840
- title="Card tilt"
841
- note="Pointer-tracked 3D tilt with a glare"
842
- importPath={"import '@heyramzi/ui/animations/card-tilt.css';"}
843
- >
844
- <div bind:this={tiltEl} class="t-tilt" onpointermove={tiltMove} onpointerleave={tiltLeave}>
845
- <div bind:this={tiltCard} class="t-tilt-card tilt-card">
846
- <strong>Pro</strong>
847
- <span class="tilt-price">39 EUR</span>
848
- <span class="tilt-note">Move the pointer across the card.</span>
849
- <div class="t-tilt-glare"></div>
850
- </div>
851
- </div>
852
- </ShowcaseStage>
853
-
854
- <ShowcaseStage
855
- title="Learn more hover"
856
- note="Chevron shifts, its arms open"
857
- importPath={"import '@heyramzi/ui/animations/learn-more-hover.css';"}
858
- >
859
- <button type="button" class="t-learn learn-demo">
860
- Learn more
861
- <span class="t-learn-chevron">
862
- {@html chevronArms}
863
- </span>
864
- </button>
865
- </ShowcaseStage>
866
-
867
- <ShowcaseStage
868
- title="Like button"
869
- note="Heart fills, particles spray"
870
- importPath={"import '@heyramzi/ui/animations/like-button.css';"}
871
- >
872
- <button
873
- type="button"
874
- class="t-like like-demo"
875
- class:is-bursting={bursting}
876
- data-liked={liked ? "true" : "false"}
877
- aria-pressed={liked}
878
- onclick={toggleLike}
879
- >
880
- <span class="t-like-icon">
881
- {@html heart}
882
- </span>
883
- <span class="t-like-particles" aria-hidden="true">
884
- {#each { length: 8 } as _, i (i)}
885
- <i></i>
886
- {/each}
887
- </span>
888
- Like
889
- </button>
890
- </ShowcaseStage>
891
-
892
- <ShowcaseStage
893
- title="Plus menu morph"
894
- note="The button becomes the surface"
895
- importPath={"import '@heyramzi/ui/animations/plus-menu-morph.css';"}
896
- >
897
- <div class="morph-anchor">
898
- <div class="t-morph morph-demo" data-open={morphOpen ? "true" : "false"}>
899
- <div class="t-morph-menu morph-menu">
900
- <span>Duplicate</span>
901
- <span>Move to...</span>
902
- <span>Archive</span>
903
- </div>
904
- <button
905
- type="button"
906
- class="t-morph-plus morph-plus"
907
- aria-expanded={morphOpen}
908
- aria-label={morphOpen ? "Close menu" : "Open menu"}
909
- onclick={() => (morphOpen = !morphOpen)}
910
- >
911
- {@html plus}
912
- </button>
913
- </div>
914
- </div>
915
- </ShowcaseStage>
916
-
917
- <ShowcaseStage
918
- title="Spinning counter"
919
- note="One reel per digit, staggered landing"
920
- importPath={"import '@heyramzi/ui/animations/spinning-counter.css';"}
921
- onaction={() => spinReel(String(100 + Math.floor(Math.random() * 900)))}
922
- >
923
- <div bind:this={reelEl} class="t-reel reel-demo"></div>
924
- </ShowcaseStage>
925
-
926
- <ShowcaseStage
927
- title="Streaming text"
928
- note="Word by word, opacity and blur"
929
- importPath={"import '@heyramzi/ui/animations/streaming-text.css';"}
930
- onaction={replayStream}
931
- >
932
- <p bind:this={streamEl} class="stream-demo">
933
- {#each STREAM_TEXT.split(" ") as word, i (i)}
934
- <span class="t-stream-w">{word}&nbsp;</span>
935
- {/each}
936
- </p>
937
- </ShowcaseStage>
938
-
939
- <ShowcaseStage
940
- title="Reasoning stream"
941
- note="A transcript stepping under a mask"
942
- importPath={"import '@heyramzi/ui/animations/reasoning-stream.css';"}
943
- >
944
- <div class="t-reason reason-demo">
945
- <div class="t-reason-viewport">
946
- <div bind:this={reasonScroll} class="t-reason-scroll">
947
- {#each [...REASON_LINES, ...REASON_LINES] as line, i (i)}
948
- <p class="reason-line">{line}</p>
949
- {/each}
950
- </div>
951
- </div>
952
- </div>
953
- </ShowcaseStage>
954
-
955
- <ShowcaseStage
956
- title="Thinking states"
957
- note="Shimmering line, swapped on a hold"
958
- importPath={"import '@heyramzi/ui/animations/thinking-states.css';"}
959
- >
960
- <span class="t-think think-demo" role="status">
961
- <span class="t-think-sizer" aria-hidden="true">Reading the repository...</span>
962
- <span bind:this={thinkEl} class="t-think-text" data-text="Thinking...">Thinking...</span>
963
- </span>
964
- </ShowcaseStage>
965
- </div>
966
-
967
- <!-- ========================== -->
968
- <!-- STYLE -->
969
- <!-- ========================== -->
970
- <style>
971
- /* One demo per row, the rule ShowcaseSection states for `.sc-stack`. An
972
- animation is read by watching it run, and a third-width stage clips it. */
973
- .grid {
974
- display: grid;
975
- grid-template-columns: minmax(0, 1fr);
976
- gap: 1rem;
977
-
978
- /* transitions.dev demo material mapped onto host tokens. Nothing defined
979
- the names, so eleven of eighteen drew transparent until 10 Sep 2026.
980
- Every value reads the app palette. */
981
- --td-material-bg: var(--color-card, var(--card));
982
- --td-material-shadow:
983
- 0 0 0 1px color-mix(in oklab, var(--color-border, var(--border)) 80%, transparent),
984
- 0 6px 18px -8px
985
- color-mix(in oklab, var(--color-foreground, var(--foreground)) 22%, transparent);
986
- --td-skeleton: color-mix(
987
- in oklab,
988
- var(--color-muted-foreground, var(--muted-foreground)) 22%,
989
- transparent
990
- );
991
- --td-stage-bg: var(--color-background, var(--background));
992
- --td-text: var(--color-foreground, var(--foreground));
993
- --td-text-subtle: var(--color-muted-foreground, var(--muted-foreground));
994
- --td-icon-color: var(--color-foreground, var(--foreground));
995
- --td-icon-color-muted: var(--color-muted-foreground, var(--muted-foreground));
996
- --td-copy-bg: color-mix(in oklab, var(--color-muted, var(--muted)) 80%, transparent);
997
- --td-copy-bg-hover: var(--color-muted, var(--muted));
998
- }
999
-
1000
- .sk {
1001
- background: var(--td-skeleton);
1002
- border-radius: 4px;
1003
- }
1004
-
1005
- .phrase {
1006
- font-size: 1.0625rem;
1007
- font-weight: 500;
1008
- color: var(--td-text, var(--foreground));
1009
- --text-swap-dur: 240ms;
1010
- }
1011
-
1012
- .digit-display {
1013
- font-size: 2.25rem;
1014
- font-weight: 600;
1015
- font-family: ui-monospace, SFMono-Regular, monospace;
1016
- color: var(--td-text, var(--foreground));
1017
- font-feature-settings: "tnum";
1018
- letter-spacing: -0.02em;
1019
- }
1020
-
1021
- .sk-card {
1022
- position: relative;
1023
- width: 12.5rem;
1024
- height: 8.75rem;
1025
- background: var(--td-material-bg);
1026
- box-shadow: var(--td-material-shadow);
1027
- border-radius: 12px;
1028
- /* WHY: clip the skeleton lines so they don't spill past the card edge as it shrinks */
1029
- overflow: hidden;
1030
- }
1031
- .sk-card.is-small {
1032
- width: 9rem;
1033
- height: 6rem;
1034
- }
1035
- .sk-card .sk {
1036
- position: absolute;
1037
- left: 14px;
1038
- right: 14px;
1039
- }
1040
- .sk-card .sk-1 {
1041
- top: 16px;
1042
- height: 10px;
1043
- right: auto;
1044
- width: 56%;
1045
- }
1046
- .sk-card .sk-2 {
1047
- top: 32px;
1048
- height: 6px;
1049
- }
1050
- .sk-card .sk-3 {
1051
- top: 44px;
1052
- height: 6px;
1053
- }
1054
- .sk-card .sk-4 {
1055
- top: 56px;
1056
- height: 6px;
1057
- right: auto;
1058
- width: 70%;
1059
- }
1060
- .sk-card .sk-5 {
1061
- top: 84px;
1062
- height: 6px;
1063
- }
1064
- .sk-card .sk-6 {
1065
- top: 96px;
1066
- height: 6px;
1067
- }
1068
-
1069
- .bell-trigger {
1070
- position: relative;
1071
- display: inline-flex;
1072
- align-items: center;
1073
- justify-content: center;
1074
- width: 3rem;
1075
- height: 3rem;
1076
- border-radius: 12px;
1077
- border: 0;
1078
- background: var(--td-material-bg);
1079
- box-shadow: var(--td-material-shadow);
1080
- color: var(--td-icon-color, var(--foreground));
1081
- cursor: pointer;
1082
- }
1083
- .bell-trigger:hover {
1084
- filter: brightness(1.05);
1085
- }
1086
- :global(.bell-trigger .t-badge-dot) {
1087
- display: inline-flex;
1088
- align-items: center;
1089
- justify-content: center;
1090
- min-width: 1.125rem;
1091
- height: 1.125rem;
1092
- padding: 0 0.3125rem;
1093
- font-size: 0.6875rem;
1094
- font-weight: 600;
1095
- color: var(--primary-foreground);
1096
- background: var(--primary);
1097
- border-radius: 999px;
1098
- line-height: 1;
1099
- }
1100
-
1101
- .sk-modal {
1102
- position: relative;
1103
- width: 16rem;
1104
- height: 10rem;
1105
- background: var(--td-material-bg);
1106
- box-shadow: var(--td-material-shadow);
1107
- border: 0;
1108
- border-radius: 12px;
1109
- padding: 1rem;
1110
- display: flex;
1111
- flex-direction: column;
1112
- gap: 0.4375rem;
1113
- }
1114
- .sk-modal .sk-mtitle {
1115
- width: 60%;
1116
- height: 12px;
1117
- }
1118
- .sk-modal .sk-mclose {
1119
- position: absolute;
1120
- top: 0.875rem;
1121
- right: 0.875rem;
1122
- width: 14px;
1123
- height: 14px;
1124
- border-radius: 50%;
1125
- }
1126
- .sk-modal .sk-line {
1127
- height: 8px;
1128
- }
1129
- .sk-modal .sk-line.full {
1130
- width: 100%;
1131
- }
1132
- .sk-modal .sk-line.three-q {
1133
- width: 75%;
1134
- }
1135
- .sk-modal .sk-pills {
1136
- margin-top: auto;
1137
- display: flex;
1138
- gap: 0.5rem;
1139
- }
1140
- .sk-modal .sk-pill {
1141
- width: 4.25rem;
1142
- height: 1.375rem;
1143
- border-radius: 999px;
1144
- }
1145
-
1146
- .dropdown-stack {
1147
- position: relative;
1148
- display: flex;
1149
- flex-direction: column;
1150
- align-items: center;
1151
- gap: 0.5rem;
1152
- }
1153
- .sk-trigger {
1154
- width: 7rem;
1155
- height: 2rem;
1156
- background: var(--td-material-bg);
1157
- box-shadow: var(--td-material-shadow);
1158
- border-radius: 8px;
1159
- }
1160
- .sk-dropdown {
1161
- width: 11rem;
1162
- padding: 0.625rem;
1163
- background: var(--td-material-bg);
1164
- box-shadow: var(--td-material-shadow);
1165
- border: 0;
1166
- border-radius: 10px;
1167
- display: flex;
1168
- flex-direction: column;
1169
- gap: 0.4375rem;
1170
- }
1171
- .sk-dropdown .sk-row {
1172
- height: 9px;
1173
- }
1174
- .sk-dropdown .sk-row.full {
1175
- width: 100%;
1176
- }
1177
- .sk-dropdown .sk-row.three-q {
1178
- width: 75%;
1179
- }
1180
- .sk-dropdown .sk-row.half {
1181
- width: 50%;
1182
- }
1183
-
1184
- .panel-clip {
1185
- width: 100%;
1186
- max-width: 16rem;
1187
- height: 9rem;
1188
- overflow: hidden;
1189
- border-radius: 12px;
1190
- background: var(--td-material-bg);
1191
- box-shadow: var(--td-material-shadow);
1192
- }
1193
- .sk-panel {
1194
- padding: 0.875rem;
1195
- display: flex;
1196
- flex-direction: column;
1197
- gap: 0.625rem;
1198
- }
1199
- .sk-panel .sk-line {
1200
- height: 9px;
1201
- }
1202
- .sk-panel .sk-line.full {
1203
- width: 100%;
1204
- }
1205
- .sk-panel .sk-line.three-q {
1206
- width: 75%;
1207
- }
1208
- .sk-panel .sk-tiles {
1209
- display: grid;
1210
- grid-template-columns: repeat(2, 1fr);
1211
- gap: 0.5rem;
1212
- margin-top: 0.25rem;
1213
- }
1214
- .sk-panel .sk-tile {
1215
- height: 2.25rem;
1216
- }
1217
-
1218
- .sk-page-stage {
1219
- position: relative;
1220
- width: 16rem;
1221
- height: 10rem;
1222
- background: var(--td-material-bg);
1223
- box-shadow: var(--td-material-shadow);
1224
- border-radius: 12px;
1225
- overflow: hidden;
1226
- }
1227
- .sk-page {
1228
- padding: 1rem;
1229
- display: flex;
1230
- flex-direction: column;
1231
- gap: 0.5rem;
1232
- }
1233
- .sk-page .sk-row {
1234
- height: 10px;
1235
- }
1236
- .sk-page .sk-row.full {
1237
- width: 100%;
1238
- }
1239
- .sk-page .sk-row.three-q {
1240
- width: 75%;
1241
- }
1242
- .sk-page .sk-row.half {
1243
- width: 50%;
1244
- }
1245
- .sk-page-2 {
1246
- align-items: center;
1247
- justify-content: center;
1248
- gap: 0.625rem;
1249
- }
1250
- .sk-amount {
1251
- font-size: 2rem;
1252
- font-weight: 600;
1253
- color: var(--td-text, var(--foreground));
1254
- font-family: ui-monospace, SFMono-Regular, monospace;
1255
- }
1256
- .sk-pill-row {
1257
- display: flex;
1258
- gap: 0.375rem;
1259
- }
1260
- .sk-mini-pill {
1261
- width: 2.5rem;
1262
- height: 1.25rem;
1263
- border-radius: 999px;
1264
- }
1265
-
1266
- .icon-stack {
1267
- display: inline-grid;
1268
- place-items: center;
1269
- width: 3.5rem;
1270
- height: 3.5rem;
1271
- border-radius: 12px;
1272
- background: var(--td-material-bg);
1273
- box-shadow: var(--td-material-shadow);
1274
- color: var(--td-icon-color, var(--foreground));
1275
- --icon-swap-dur: 220ms;
1276
- }
1277
- .icon-stack :global(.t-icon) {
1278
- display: inline-flex;
1279
- align-items: center;
1280
- justify-content: center;
1281
- }
1282
-
1283
- .check-badge {
1284
- display: inline-flex;
1285
- align-items: center;
1286
- justify-content: center;
1287
- width: 4rem;
1288
- height: 4rem;
1289
- border-radius: 50%;
1290
- background: oklch(0.66 0.16 155);
1291
- color: #fff;
1292
- }
1293
- .check-badge :global(.t-success-check svg) {
1294
- width: 2.25rem;
1295
- height: 2.25rem;
1296
- }
1297
-
1298
- .avatar-row {
1299
- display: inline-flex;
1300
- }
1301
- .avatar-chip {
1302
- display: inline-flex;
1303
- align-items: center;
1304
- justify-content: center;
1305
- width: 2.75rem;
1306
- height: 2.75rem;
1307
- margin-left: -0.625rem;
1308
- border-radius: 50%;
1309
- background: var(--td-material-bg);
1310
- box-shadow: var(--td-material-shadow);
1311
- border: 2px solid var(--td-stage-bg);
1312
- color: var(--td-text);
1313
- font-size: 0.875rem;
1314
- font-weight: 600;
1315
- cursor: default;
1316
- }
1317
- .avatar-chip:first-child {
1318
- margin-left: 0;
1319
- }
1320
-
1321
- .shake-wrap {
1322
- display: flex;
1323
- flex-direction: column;
1324
- gap: 0.5rem;
1325
- width: 13rem;
1326
- }
1327
- .shake-input {
1328
- display: flex;
1329
- align-items: center;
1330
- height: 2.5rem;
1331
- padding: 0 0.75rem;
1332
- border: 1.5px solid var(--border);
1333
- border-radius: 8px;
1334
- background: var(--td-material-bg);
1335
- color: var(--td-text-subtle);
1336
- font-size: 0.875rem;
1337
- }
1338
- .shake-input.is-error {
1339
- border-color: var(--destructive);
1340
- }
1341
- .shake-msg {
1342
- margin: 0;
1343
- font-size: 0.75rem;
1344
- color: var(--destructive);
1345
- }
1346
-
1347
- .clear-field {
1348
- width: 14rem;
1349
- height: 2.75rem;
1350
- border-radius: 10px;
1351
- background: var(--td-material-bg);
1352
- box-shadow: var(--td-material-shadow);
1353
- }
1354
- .clear-input {
1355
- width: 100%;
1356
- height: 100%;
1357
- padding: 0 2.25rem 0 0.75rem;
1358
- border: 0;
1359
- background: transparent;
1360
- outline: none;
1361
- font: inherit;
1362
- font-size: 0.9375rem;
1363
- color: var(--td-text);
1364
- }
1365
- .clear-text {
1366
- padding-left: 0.75rem;
1367
- font-size: 0.9375rem;
1368
- color: var(--td-text);
1369
- }
1370
- .clear-ph {
1371
- color: var(--td-text-subtle);
1372
- }
1373
- .clear-x {
1374
- position: absolute;
1375
- top: 50%;
1376
- right: 0.5rem;
1377
- transform: translateY(-50%);
1378
- z-index: 4;
1379
- display: inline-flex;
1380
- align-items: center;
1381
- justify-content: center;
1382
- width: 1.5rem;
1383
- height: 1.5rem;
1384
- border: 0;
1385
- border-radius: 50%;
1386
- background: var(--td-copy-bg);
1387
- color: var(--td-icon-color-muted);
1388
- cursor: pointer;
1389
- }
1390
- .clear-x:hover {
1391
- background: var(--td-copy-bg-hover);
1392
- color: var(--td-icon-color);
1393
- }
1394
-
1395
- .skel-card {
1396
- width: 14rem;
1397
- height: 4.5rem;
1398
- background: var(--td-material-bg);
1399
- box-shadow: var(--td-material-shadow);
1400
- border-radius: 12px;
1401
- }
1402
- .skel-layer {
1403
- display: flex;
1404
- align-items: center;
1405
- gap: 0.75rem;
1406
- padding: 0 1rem;
1407
- }
1408
- .skel-avatar {
1409
- flex: none;
1410
- width: 2.5rem;
1411
- height: 2.5rem;
1412
- border-radius: 50%;
1413
- }
1414
- .skel-bars {
1415
- display: flex;
1416
- flex-direction: column;
1417
- gap: 0.5rem;
1418
- }
1419
- .skel-l1 {
1420
- width: 6rem;
1421
- height: 9px;
1422
- }
1423
- .skel-l2 {
1424
- width: 4rem;
1425
- height: 9px;
1426
- }
1427
- .skel-avatar-real {
1428
- flex: none;
1429
- display: inline-flex;
1430
- align-items: center;
1431
- justify-content: center;
1432
- width: 2.5rem;
1433
- height: 2.5rem;
1434
- border-radius: 50%;
1435
- background: var(--primary);
1436
- color: var(--primary-foreground);
1437
- font-size: 0.875rem;
1438
- font-weight: 600;
1439
- }
1440
- .skel-lines {
1441
- display: flex;
1442
- flex-direction: column;
1443
- gap: 0.125rem;
1444
- }
1445
- .skel-name {
1446
- font-size: 0.875rem;
1447
- font-weight: 600;
1448
- color: var(--td-text);
1449
- }
1450
- .skel-sub {
1451
- font-size: 0.75rem;
1452
- color: var(--td-text-subtle);
1453
- }
1454
-
1455
- .shimmer-demo {
1456
- font-size: 1.125rem;
1457
- font-weight: 500;
1458
- --shimmer-base: var(--td-text-subtle);
1459
- --shimmer-highlight: var(--td-text);
1460
- }
1461
-
1462
- .tabs-demo {
1463
- font-size: 0.8125rem;
1464
- font-weight: 500;
1465
- --tabs-bar-bg: color-mix(in srgb, var(--td-text) 8%, transparent);
1466
- --tabs-pill-bg: var(--td-material-bg);
1467
- --tabs-text-muted: var(--td-text-subtle);
1468
- --tabs-text-active: var(--td-text);
1469
- }
1470
- .tabs-demo :global(.t-tabs-pill) {
1471
- box-shadow: var(--td-material-shadow);
1472
- }
1473
-
1474
- .tt-trigger {
1475
- display: inline-flex;
1476
- align-items: center;
1477
- justify-content: center;
1478
- width: 2.75rem;
1479
- height: 2.75rem;
1480
- border: 0;
1481
- border-radius: 10px;
1482
- background: var(--td-material-bg);
1483
- box-shadow: var(--td-material-shadow);
1484
- color: var(--td-icon-color);
1485
- cursor: pointer;
1486
- }
1487
- .tt-bubble {
1488
- font-size: 0.8125rem;
1489
- font-weight: 500;
1490
- --tt-bg: var(--td-material-bg);
1491
- --tt-fg: var(--td-text);
1492
- }
1493
-
1494
- .stagger-demo {
1495
- display: flex;
1496
- flex-direction: column;
1497
- gap: 0.375rem;
1498
- max-width: 14rem;
1499
- text-align: center;
1500
- }
1501
- .stagger-head {
1502
- font-size: 1.25rem;
1503
- font-weight: 600;
1504
- color: var(--td-text);
1505
- letter-spacing: -0.01em;
1506
- }
1507
- .stagger-sub {
1508
- font-size: 0.875rem;
1509
- color: var(--td-text-subtle);
1510
- }
1511
- /* ---- the nine added 14 Sep 2026 ---- */
1512
- .acc-demo {
1513
- width: min(24rem, 100%);
1514
- background: var(--td-material-bg);
1515
- box-shadow: var(--td-material-shadow);
1516
- border-radius: 10px;
1517
- }
1518
-
1519
- .acc-head {
1520
- display: flex;
1521
- align-items: center;
1522
- justify-content: space-between;
1523
- gap: 1rem;
1524
- width: 100%;
1525
- padding: 0.75rem 0.875rem;
1526
- font-size: 0.875rem;
1527
- font-weight: 500;
1528
- color: var(--td-text);
1529
- background: none;
1530
- border: 0;
1531
- cursor: pointer;
1532
- }
1533
-
1534
- .acc-body {
1535
- margin: 0;
1536
- padding: 0 0.875rem 0.875rem;
1537
- font-size: 0.8125rem;
1538
- line-height: 1.55;
1539
- color: var(--td-text-subtle);
1540
- }
1541
-
1542
- .tilt-card {
1543
- display: flex;
1544
- flex-direction: column;
1545
- gap: 0.25rem;
1546
- width: 13rem;
1547
- padding: 1.125rem;
1548
- background: var(--td-material-bg);
1549
- box-shadow: var(--td-material-shadow);
1550
- color: var(--td-text);
1551
- font-size: 0.8125rem;
1552
- }
1553
-
1554
- .tilt-price {
1555
- font-size: 1.5rem;
1556
- font-weight: 600;
1557
- letter-spacing: -0.02em;
1558
- }
1559
-
1560
- .tilt-note {
1561
- color: var(--td-text-subtle);
1562
- }
1563
-
1564
- .learn-demo,
1565
- .like-demo {
1566
- display: inline-flex;
1567
- align-items: center;
1568
- gap: 0.4375rem;
1569
- padding: 0.5rem 0.875rem;
1570
- font-size: 0.875rem;
1571
- font-weight: 500;
1572
- color: var(--td-text);
1573
- background: var(--td-copy-bg);
1574
- border: 0;
1575
- border-radius: 8px;
1576
- cursor: pointer;
1577
- }
1578
-
1579
- .like-demo {
1580
- position: relative;
1581
- }
1582
-
1583
- /* The anchor is sized to the open footprint, the way the usage note asks:
1584
- without it the morph grows the stage and every demo under it jumps. */
1585
- .morph-anchor {
1586
- position: relative;
1587
- width: 11rem;
1588
- height: 7.5rem;
1589
- }
1590
-
1591
- .morph-demo {
1592
- position: absolute;
1593
- inset-block-start: 0;
1594
- inset-inline-start: 0;
1595
- background: var(--td-material-bg);
1596
- box-shadow: var(--td-material-shadow);
1597
- }
1598
-
1599
- .morph-menu {
1600
- display: flex;
1601
- flex-direction: column;
1602
- gap: 0.5rem;
1603
- padding: 0.75rem 0.875rem;
1604
- font-size: 0.8125rem;
1605
- color: var(--td-text);
1606
- }
1607
-
1608
- .morph-plus {
1609
- display: inline-flex;
1610
- align-items: center;
1611
- justify-content: center;
1612
- color: var(--td-text);
1613
- background: none;
1614
- border: 0;
1615
- cursor: pointer;
1616
- }
1617
-
1618
- .reel-demo {
1619
- font-size: 2rem;
1620
- font-weight: 600;
1621
- font-family: ui-monospace, SFMono-Regular, monospace;
1622
- color: var(--td-text);
1623
- }
1624
-
1625
- .stream-demo {
1626
- margin: 0;
1627
- max-width: 32rem;
1628
- font-size: 0.9375rem;
1629
- line-height: 1.6;
1630
- color: var(--td-text);
1631
- }
1632
-
1633
- .reason-demo {
1634
- width: min(26rem, 100%);
1635
- height: 7rem;
1636
- padding: 0.875rem 1rem;
1637
- background: var(--td-material-bg);
1638
- box-shadow: var(--td-material-shadow);
1639
- border-radius: 10px;
1640
- --reason-line: 1.5rem;
1641
- }
1642
-
1643
- .reason-line {
1644
- margin: 0;
1645
- height: 1.5rem;
1646
- font-size: 0.8125rem;
1647
- line-height: 1.5rem;
1648
- color: var(--td-text-subtle);
1649
- white-space: nowrap;
1650
- overflow: hidden;
1651
- text-overflow: ellipsis;
1652
- }
1653
-
1654
- .think-demo {
1655
- font-size: 0.9375rem;
1656
- color: var(--td-text-subtle);
1657
- }
1658
- </style>