@heyramzi/ui 0.0.0-stage → 1.53.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 (564) hide show
  1. package/COMPONENTS.md +133 -0
  2. package/README.md +136 -2
  3. package/package.json +217 -3
  4. package/scripts/lib/sync-core.mjs +113 -0
  5. package/scripts/relative-imports.mjs +39 -0
  6. package/scripts/sf-symbol.mjs +93 -0
  7. package/scripts/sync-shadcn.mjs +354 -0
  8. package/scripts/sync-transitions.mjs +146 -0
  9. package/scripts/validate-apple-symbol-exports.mjs +45 -0
  10. package/src/agents/agent-avatar.svelte +592 -0
  11. package/src/agents/agent-cast.svelte +197 -0
  12. package/src/agents/demo.svelte +125 -0
  13. package/src/agents/helpers.ts +58 -0
  14. package/src/agents/index.ts +11 -0
  15. package/src/agents/page-state.ts +70 -0
  16. package/src/agents/types.ts +74 -0
  17. package/src/agents/vendor/LICENSE +21 -0
  18. package/src/agents/vendor/color.ts +155 -0
  19. package/src/agents/vendor/draw.ts +840 -0
  20. package/src/agents/vendor/engine.ts +845 -0
  21. package/src/agents/vendor/mesh.ts +838 -0
  22. package/src/agents/vendor/plastic.ts +4958 -0
  23. package/src/agents/vendor/presets.ts +103 -0
  24. package/src/agents/vendor/shapes.ts +41 -0
  25. package/src/agents/vendor/ticker.ts +67 -0
  26. package/src/agents/vendor/types.ts +261 -0
  27. package/src/agents/vendor/wear.ts +1699 -0
  28. package/src/ai/ai-button/AiButton.svelte +86 -0
  29. package/src/ai/ai-button/index.ts +4 -0
  30. package/src/ai/confirmation/Confirmation.svelte +54 -0
  31. package/src/ai/confirmation/ConfirmationAccepted.svelte +23 -0
  32. package/src/ai/confirmation/ConfirmationAction.svelte +53 -0
  33. package/src/ai/confirmation/ConfirmationActions.svelte +24 -0
  34. package/src/ai/confirmation/ConfirmationFailed.svelte +23 -0
  35. package/src/ai/confirmation/ConfirmationRejected.svelte +23 -0
  36. package/src/ai/confirmation/ConfirmationRequest.svelte +25 -0
  37. package/src/ai/confirmation/ConfirmationTitle.svelte +25 -0
  38. package/src/ai/confirmation/confirmation-context.svelte.ts +22 -0
  39. package/src/ai/confirmation/index.ts +50 -0
  40. package/src/ai/generation-progress/GenerationProgress.svelte +354 -0
  41. package/src/ai/generation-progress/index.ts +7 -0
  42. package/src/ai/index.ts +111 -0
  43. package/src/ai/loading-state/PixelLoader.svelte +138 -0
  44. package/src/ai/loading-state/index.ts +5 -0
  45. package/src/ai/prompt-input/PromptInput.svelte +56 -0
  46. package/src/ai/prompt-input/PromptInputBody.svelte +19 -0
  47. package/src/ai/prompt-input/PromptInputSubmit.svelte +79 -0
  48. package/src/ai/prompt-input/PromptInputTextarea.svelte +74 -0
  49. package/src/ai/prompt-input/PromptInputToolbar.svelte +19 -0
  50. package/src/ai/prompt-input/index.ts +34 -0
  51. package/src/ai/reasoning/Reasoning.svelte +50 -0
  52. package/src/ai/reasoning/ReasoningContent.svelte +27 -0
  53. package/src/ai/reasoning/ReasoningTrigger.svelte +48 -0
  54. package/src/ai/reasoning/index.ts +22 -0
  55. package/src/ai/reasoning/reasoning-context.svelte.ts +20 -0
  56. package/src/ai/response/Response.css +32 -0
  57. package/src/ai/response/Response.svelte +130 -0
  58. package/src/ai/response/index.ts +5 -0
  59. package/src/ai/suggestion/Suggestion.svelte +43 -0
  60. package/src/ai/suggestion/Suggestions.svelte +25 -0
  61. package/src/ai/suggestion/index.ts +14 -0
  62. package/src/ai/thinking-dots/ThinkingDots.svelte +42 -0
  63. package/src/ai/thinking-dots/index.ts +5 -0
  64. package/src/ai/typing-dots/TypingDots.css +39 -0
  65. package/src/ai/typing-dots/TypingDots.svelte +27 -0
  66. package/src/ai/typing-dots/index.ts +10 -0
  67. package/src/ai/voice-waveform/VoiceWaveform.css +40 -0
  68. package/src/ai/voice-waveform/VoiceWaveform.svelte +46 -0
  69. package/src/ai/voice-waveform/index.ts +5 -0
  70. package/src/ambient.d.ts +4 -0
  71. package/src/animations/README.md +84 -0
  72. package/src/animations/avatar-group-hover.css +47 -0
  73. package/src/animations/card-tilt.css +74 -0
  74. package/src/animations/digit-pop-in.css +78 -0
  75. package/src/animations/dropdown.css +121 -0
  76. package/src/animations/index.css +23 -0
  77. package/src/animations/input-clear-dissolve.css +86 -0
  78. package/src/animations/input-shake.css +114 -0
  79. package/src/animations/learn-more-hover.css +48 -0
  80. package/src/animations/like-button.css +93 -0
  81. package/src/animations/modal.css +75 -0
  82. package/src/animations/page-side-by-side.css +63 -0
  83. package/src/animations/plus-menu-morph.css +97 -0
  84. package/src/animations/resize.css +20 -0
  85. package/src/animations/shimmer-text.css +72 -0
  86. package/src/animations/spinning-counter.css +52 -0
  87. package/src/animations/success-check.css +120 -0
  88. package/src/animations/text-swap.css +40 -0
  89. package/src/animations/text-swap.ts +40 -0
  90. package/src/animations/thinking-states.css +65 -0
  91. package/src/animations/upstream-coverage.test.ts +65 -0
  92. package/src/components/copy/clipboard.test.ts +206 -0
  93. package/src/components/copy/clipboard.ts +49 -0
  94. package/src/components/copy/copied.svelte.ts +39 -0
  95. package/src/components/copy/copy-button.svelte +175 -0
  96. package/src/components/copy/copy-button.test.ts +110 -0
  97. package/src/components/copy/index.ts +3 -0
  98. package/src/components/data-table/data-table.test.ts +134 -0
  99. package/src/components/data-table/data-table.ts +77 -0
  100. package/src/components/data-table/index.ts +20 -0
  101. package/src/components/data-table/infinite-scroll.svelte +69 -0
  102. package/src/components/data-table/pagination.svelte +62 -0
  103. package/src/components/data-table/row-arrow.svelte +28 -0
  104. package/src/components/data-table/row-context-menu.svelte +106 -0
  105. package/src/components/data-table/row-context-menu.test.ts +18 -0
  106. package/src/components/data-table/sortable-head.svelte +56 -0
  107. package/src/components/data-table/table-skeleton.svelte +83 -0
  108. package/src/components/hold-to-confirm/hold-to-confirm.svelte +232 -0
  109. package/src/components/hold-to-confirm/index.ts +1 -0
  110. package/src/components/icon/icon.svelte +23 -0
  111. package/src/components/icon/index.ts +1 -0
  112. package/src/components/language-flag/index.ts +1 -0
  113. package/src/components/language-flag/language-flag.svelte +60 -0
  114. package/src/components/list-page/detail-empty.svelte +50 -0
  115. package/src/components/list-page/detail-header.svelte +107 -0
  116. package/src/components/list-page/detail-panel.svelte +90 -0
  117. package/src/components/list-page/detail-row.svelte +36 -0
  118. package/src/components/list-page/detail-stack.svelte +33 -0
  119. package/src/components/list-page/empty-state.svelte +91 -0
  120. package/src/components/list-page/filter-item.svelte +49 -0
  121. package/src/components/list-page/filter-popover.svelte +88 -0
  122. package/src/components/list-page/filter-section.ts +12 -0
  123. package/src/components/list-page/grouped-list.svelte +116 -0
  124. package/src/components/list-page/index.ts +28 -0
  125. package/src/components/list-page/list-page-shell.svelte +195 -0
  126. package/src/components/list-page/list-table.ts +19 -0
  127. package/src/components/list-page/master-detail.svelte +206 -0
  128. package/src/components/list-page/master-detail.test.ts +37 -0
  129. package/src/components/list-page/notice-bar.svelte +68 -0
  130. package/src/components/list-page/prev-next.svelte +104 -0
  131. package/src/components/list-page/queue-rail.ts +25 -0
  132. package/src/components/list-page/rail-body.svelte +29 -0
  133. package/src/components/list-page/rail-empty.svelte +51 -0
  134. package/src/components/list-page/rail-head.svelte +49 -0
  135. package/src/components/list-page/rail-row.svelte +163 -0
  136. package/src/components/list-page/section-host.ts +24 -0
  137. package/src/components/list-page/section-label.svelte +39 -0
  138. package/src/components/list-page/step.test.ts +21 -0
  139. package/src/components/list-page/step.ts +10 -0
  140. package/src/components/list-page/toolbar-search.svelte +63 -0
  141. package/src/components/list-page/toolbar-trigger.ts +23 -0
  142. package/src/components/list-page/types.ts +1 -0
  143. package/src/components/locale-switch/index.ts +1 -0
  144. package/src/components/locale-switch/locale-switch.svelte +59 -0
  145. package/src/components/mark/index.ts +1 -0
  146. package/src/components/mark/mark.svelte +70 -0
  147. package/src/components/open-in/index.ts +6 -0
  148. package/src/components/open-in/open-in.svelte +106 -0
  149. package/src/components/picker/index.ts +4 -0
  150. package/src/components/picker/mark-picker.svelte +84 -0
  151. package/src/components/picker/picker.svelte +143 -0
  152. package/src/components/picker/trigger.ts +42 -0
  153. package/src/components/picker/types.ts +26 -0
  154. package/src/components/segmented/animated-tabs.svelte +175 -0
  155. package/src/components/segmented/collapse.test.ts +49 -0
  156. package/src/components/segmented/collapse.ts +39 -0
  157. package/src/components/segmented/index.ts +41 -0
  158. package/src/components/segmented/section-runs.svelte +79 -0
  159. package/src/components/segmented/segment.svelte +106 -0
  160. package/src/components/segmented/segmented-filter.svelte +125 -0
  161. package/src/components/segmented/segmented-nav.svelte +82 -0
  162. package/src/components/segmented/segmented-overflow.svelte +85 -0
  163. package/src/components/segmented/segmented-track.svelte +181 -0
  164. package/src/components/segmented/segmented.test.ts +20 -0
  165. package/src/components/segmented/segmented.ts +80 -0
  166. package/src/components/star-rating-input/index.ts +9 -0
  167. package/src/components/star-rating-input/star-rating-input.svelte +256 -0
  168. package/src/components/svg/index.ts +1 -0
  169. package/src/components/svg/svg.svelte +9 -0
  170. package/src/design/index.css +270 -0
  171. package/src/design/polish.css +159 -0
  172. package/src/effects/animated-beam/AnimatedBeam.svelte +132 -0
  173. package/src/effects/animated-beam/diagrams/BeamSvg.svelte +91 -0
  174. package/src/effects/animated-beam/index.ts +2 -0
  175. package/src/effects/animated-beam/path-calculator.svelte.ts +70 -0
  176. package/src/effects/animated-beam/types.ts +23 -0
  177. package/src/effects/border-beam/BorderBeam.svelte +223 -0
  178. package/src/effects/border-beam/css.test.ts +46 -0
  179. package/src/effects/border-beam/css.ts +574 -0
  180. package/src/effects/border-beam/presets.ts +699 -0
  181. package/src/effects/border-beam/register-properties.test.ts +74 -0
  182. package/src/effects/border-beam/register-properties.ts +73 -0
  183. package/src/effects/border-beam/types.ts +38 -0
  184. package/src/effects/image-comparison/ImageComparison.css +100 -0
  185. package/src/effects/image-comparison/ImageComparison.svelte +209 -0
  186. package/src/effects/image-comparison/compare.test.ts +17 -0
  187. package/src/effects/image-comparison/compare.ts +5 -0
  188. package/src/effects/image-reveal/ImageReveal.css +10 -0
  189. package/src/effects/image-reveal/ImageReveal.svelte +357 -0
  190. package/src/effects/image-reveal/index.ts +2 -0
  191. package/src/effects/image-reveal/types.ts +35 -0
  192. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +338 -0
  193. package/src/effects/magic-card/MagicCard.css +47 -0
  194. package/src/effects/magic-card/MagicCard.svelte +57 -0
  195. package/src/effects/magnetic/magnetic.ts +81 -0
  196. package/src/effects/marquee/Marquee.css +78 -0
  197. package/src/effects/marquee/Marquee.svelte +160 -0
  198. package/src/effects/progressive-blur/ProgressiveBlur.css +9 -0
  199. package/src/effects/progressive-blur/ProgressiveBlur.svelte +63 -0
  200. package/src/effects/reveal/AnimatedGroup.svelte +101 -0
  201. package/src/effects/reveal/InView.svelte +77 -0
  202. package/src/effects/reveal/TextEffect.svelte +142 -0
  203. package/src/effects/reveal/index.ts +11 -0
  204. package/src/effects/reveal/observe.test.ts +60 -0
  205. package/src/effects/reveal/observe.ts +65 -0
  206. package/src/effects/reveal/reveal.css +141 -0
  207. package/src/effects/reveal/types.ts +32 -0
  208. package/src/effects/shader-gradient/ShaderGradient.svelte +227 -0
  209. package/src/effects/shader-gradient/color.ts +44 -0
  210. package/src/effects/shader-gradient/index.ts +5 -0
  211. package/src/effects/shader-gradient/renderer.ts +456 -0
  212. package/src/effects/shader-gradient/shaders.ts +324 -0
  213. package/src/effects/shader-gradient/types.ts +68 -0
  214. package/src/effects/spotlight/Spotlight.css +14 -0
  215. package/src/effects/spotlight/Spotlight.svelte +61 -0
  216. package/src/effects/theme-toggle/ThemeToggle.css +214 -0
  217. package/src/effects/theme-toggle/ThemeToggle.svelte +126 -0
  218. package/src/gallery.test.ts +52 -0
  219. package/src/icons/TradeIcon.svelte +16 -0
  220. package/src/icons/apple-symbol-exports.ts +135 -0
  221. package/src/icons/sf-symbols.ts +527 -0
  222. package/src/icons/trades.ts +89 -0
  223. package/src/index.ts +53 -0
  224. package/src/lib/arrive.ts +26 -0
  225. package/src/lib/icon-types.ts +17 -0
  226. package/src/lib/layout.ts +8 -0
  227. package/src/lib/panel.ts +21 -0
  228. package/src/lib/sliding-pill.ts +68 -0
  229. package/src/lib/theme-transition.ts +79 -0
  230. package/src/lib/tone.ts +48 -0
  231. package/src/shadcn/accordion/accordion-content.svelte +22 -0
  232. package/src/shadcn/accordion/accordion-item.svelte +17 -0
  233. package/src/shadcn/accordion/accordion-trigger.svelte +44 -0
  234. package/src/shadcn/accordion/accordion.svelte +19 -0
  235. package/src/shadcn/accordion/index.ts +16 -0
  236. package/src/shadcn/alert/alert-description.svelte +23 -0
  237. package/src/shadcn/alert/alert-title.svelte +20 -0
  238. package/src/shadcn/alert/alert.svelte +46 -0
  239. package/src/shadcn/alert/index.ts +14 -0
  240. package/src/shadcn/alert-dialog/alert-dialog-action.svelte +18 -0
  241. package/src/shadcn/alert-dialog/alert-dialog-cancel.svelte +18 -0
  242. package/src/shadcn/alert-dialog/alert-dialog-content.svelte +29 -0
  243. package/src/shadcn/alert-dialog/alert-dialog-description.svelte +17 -0
  244. package/src/shadcn/alert-dialog/alert-dialog-footer.svelte +20 -0
  245. package/src/shadcn/alert-dialog/alert-dialog-header.svelte +20 -0
  246. package/src/shadcn/alert-dialog/alert-dialog-overlay.svelte +17 -0
  247. package/src/shadcn/alert-dialog/alert-dialog-portal.svelte +7 -0
  248. package/src/shadcn/alert-dialog/alert-dialog-title.svelte +17 -0
  249. package/src/shadcn/alert-dialog/alert-dialog-trigger.svelte +7 -0
  250. package/src/shadcn/alert-dialog/alert-dialog.svelte +7 -0
  251. package/src/shadcn/alert-dialog/index.ts +37 -0
  252. package/src/shadcn/attachment/attachment-action.svelte +23 -0
  253. package/src/shadcn/attachment/attachment-actions.svelte +23 -0
  254. package/src/shadcn/attachment/attachment-content.svelte +23 -0
  255. package/src/shadcn/attachment/attachment-description.svelte +23 -0
  256. package/src/shadcn/attachment/attachment-group.svelte +23 -0
  257. package/src/shadcn/attachment/attachment-media.svelte +44 -0
  258. package/src/shadcn/attachment/attachment-title.svelte +23 -0
  259. package/src/shadcn/attachment/attachment-trigger.svelte +29 -0
  260. package/src/shadcn/attachment/attachment.svelte +55 -0
  261. package/src/shadcn/attachment/index.ts +40 -0
  262. package/src/shadcn/avatar/avatar-badge.svelte +26 -0
  263. package/src/shadcn/avatar/avatar-fallback.svelte +20 -0
  264. package/src/shadcn/avatar/avatar-group-count.svelte +23 -0
  265. package/src/shadcn/avatar/avatar-group.svelte +23 -0
  266. package/src/shadcn/avatar/avatar-image.svelte +17 -0
  267. package/src/shadcn/avatar/avatar.svelte +26 -0
  268. package/src/shadcn/avatar/index.ts +22 -0
  269. package/src/shadcn/badge/badge.svelte +54 -0
  270. package/src/shadcn/badge/index.ts +2 -0
  271. package/src/shadcn/breadcrumb/breadcrumb-ellipsis.svelte +23 -0
  272. package/src/shadcn/breadcrumb/breadcrumb-item.svelte +20 -0
  273. package/src/shadcn/breadcrumb/breadcrumb-link.svelte +31 -0
  274. package/src/shadcn/breadcrumb/breadcrumb-list.svelte +23 -0
  275. package/src/shadcn/breadcrumb/breadcrumb-page.svelte +23 -0
  276. package/src/shadcn/breadcrumb/breadcrumb-separator.svelte +27 -0
  277. package/src/shadcn/breadcrumb/breadcrumb.svelte +21 -0
  278. package/src/shadcn/breadcrumb/index.ts +25 -0
  279. package/src/shadcn/bubble/bubble-content.svelte +31 -0
  280. package/src/shadcn/bubble/bubble-group.svelte +20 -0
  281. package/src/shadcn/bubble/bubble-reactions.svelte +52 -0
  282. package/src/shadcn/bubble/bubble.svelte +58 -0
  283. package/src/shadcn/bubble/index.ts +25 -0
  284. package/src/shadcn/button/button.svelte +121 -0
  285. package/src/shadcn/button/index.ts +17 -0
  286. package/src/shadcn/calendar/calendar-caption.svelte +76 -0
  287. package/src/shadcn/calendar/calendar-cell.svelte +19 -0
  288. package/src/shadcn/calendar/calendar-day.svelte +35 -0
  289. package/src/shadcn/calendar/calendar-grid-body.svelte +12 -0
  290. package/src/shadcn/calendar/calendar-grid-head.svelte +12 -0
  291. package/src/shadcn/calendar/calendar-grid-row.svelte +12 -0
  292. package/src/shadcn/calendar/calendar-grid.svelte +16 -0
  293. package/src/shadcn/calendar/calendar-head-cell.svelte +19 -0
  294. package/src/shadcn/calendar/calendar-header.svelte +19 -0
  295. package/src/shadcn/calendar/calendar-heading.svelte +16 -0
  296. package/src/shadcn/calendar/calendar-month-select.svelte +44 -0
  297. package/src/shadcn/calendar/calendar-month.svelte +15 -0
  298. package/src/shadcn/calendar/calendar-months.svelte +19 -0
  299. package/src/shadcn/calendar/calendar-nav.svelte +19 -0
  300. package/src/shadcn/calendar/calendar-next-button.svelte +31 -0
  301. package/src/shadcn/calendar/calendar-prev-button.svelte +31 -0
  302. package/src/shadcn/calendar/calendar-year-select.svelte +43 -0
  303. package/src/shadcn/calendar/calendar.svelte +124 -0
  304. package/src/shadcn/calendar/index.ts +40 -0
  305. package/src/shadcn/card/card-action.svelte +23 -0
  306. package/src/shadcn/card/card-content.svelte +20 -0
  307. package/src/shadcn/card/card-description.svelte +20 -0
  308. package/src/shadcn/card/card-footer.svelte +23 -0
  309. package/src/shadcn/card/card-header.svelte +23 -0
  310. package/src/shadcn/card/card-title.svelte +29 -0
  311. package/src/shadcn/card/card.svelte +25 -0
  312. package/src/shadcn/card/index.ts +25 -0
  313. package/src/shadcn/checkbox/checkbox.svelte +38 -0
  314. package/src/shadcn/checkbox/index.ts +6 -0
  315. package/src/shadcn/code-block/code-block-actions.svelte +23 -0
  316. package/src/shadcn/code-block/code-block-content.svelte +55 -0
  317. package/src/shadcn/code-block/code-block-copy-button.svelte +10 -0
  318. package/src/shadcn/code-block/code-block-footer.svelte +20 -0
  319. package/src/shadcn/code-block/code-block-header.svelte +23 -0
  320. package/src/shadcn/code-block/code-block-line.svelte +71 -0
  321. package/src/shadcn/code-block/code-block-title.svelte +23 -0
  322. package/src/shadcn/code-block/code-block.svelte +47 -0
  323. package/src/shadcn/code-block/index.ts +42 -0
  324. package/src/shadcn/collapsible/collapsible-content.svelte +7 -0
  325. package/src/shadcn/collapsible/collapsible-trigger.svelte +7 -0
  326. package/src/shadcn/collapsible/collapsible.svelte +11 -0
  327. package/src/shadcn/collapsible/index.ts +13 -0
  328. package/src/shadcn/command/command-dialog.svelte +66 -0
  329. package/src/shadcn/command/command-empty.svelte +17 -0
  330. package/src/shadcn/command/command-group.svelte +40 -0
  331. package/src/shadcn/command/command-input.svelte +64 -0
  332. package/src/shadcn/command/command-item.svelte +37 -0
  333. package/src/shadcn/command/command-link-item.svelte +20 -0
  334. package/src/shadcn/command/command-list.svelte +20 -0
  335. package/src/shadcn/command/command-loading.svelte +7 -0
  336. package/src/shadcn/command/command-separator.svelte +17 -0
  337. package/src/shadcn/command/command-shortcut.svelte +23 -0
  338. package/src/shadcn/command/command.svelte +28 -0
  339. package/src/shadcn/command/index.ts +40 -0
  340. package/src/shadcn/context-menu/context-menu-checkbox-item.svelte +41 -0
  341. package/src/shadcn/context-menu/context-menu-content.svelte +28 -0
  342. package/src/shadcn/context-menu/context-menu-group-heading.svelte +21 -0
  343. package/src/shadcn/context-menu/context-menu-group.svelte +7 -0
  344. package/src/shadcn/context-menu/context-menu-item.svelte +27 -0
  345. package/src/shadcn/context-menu/context-menu-label.svelte +27 -0
  346. package/src/shadcn/context-menu/context-menu-portal.svelte +7 -0
  347. package/src/shadcn/context-menu/context-menu-radio-group.svelte +16 -0
  348. package/src/shadcn/context-menu/context-menu-radio-item.svelte +35 -0
  349. package/src/shadcn/context-menu/context-menu-separator.svelte +17 -0
  350. package/src/shadcn/context-menu/context-menu-shortcut.svelte +23 -0
  351. package/src/shadcn/context-menu/context-menu-sub-content.svelte +25 -0
  352. package/src/shadcn/context-menu/context-menu-sub-trigger.svelte +29 -0
  353. package/src/shadcn/context-menu/context-menu-sub.svelte +7 -0
  354. package/src/shadcn/context-menu/context-menu-trigger.svelte +17 -0
  355. package/src/shadcn/context-menu/context-menu.svelte +7 -0
  356. package/src/shadcn/context-menu/index.ts +52 -0
  357. package/src/shadcn/dialog/dialog-close.svelte +11 -0
  358. package/src/shadcn/dialog/dialog-content.svelte +57 -0
  359. package/src/shadcn/dialog/dialog-description.svelte +20 -0
  360. package/src/shadcn/dialog/dialog-footer.svelte +32 -0
  361. package/src/shadcn/dialog/dialog-header.svelte +20 -0
  362. package/src/shadcn/dialog/dialog-overlay.svelte +20 -0
  363. package/src/shadcn/dialog/dialog-portal.svelte +7 -0
  364. package/src/shadcn/dialog/dialog-title.svelte +17 -0
  365. package/src/shadcn/dialog/dialog-trigger.svelte +11 -0
  366. package/src/shadcn/dialog/dialog.svelte +7 -0
  367. package/src/shadcn/dialog/index.ts +34 -0
  368. package/src/shadcn/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
  369. package/src/shadcn/dropdown-menu/dropdown-menu-checkbox-item.svelte +43 -0
  370. package/src/shadcn/dropdown-menu/dropdown-menu-content.svelte +31 -0
  371. package/src/shadcn/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
  372. package/src/shadcn/dropdown-menu/dropdown-menu-group.svelte +7 -0
  373. package/src/shadcn/dropdown-menu/dropdown-menu-item.svelte +29 -0
  374. package/src/shadcn/dropdown-menu/dropdown-menu-label.svelte +27 -0
  375. package/src/shadcn/dropdown-menu/dropdown-menu-portal.svelte +7 -0
  376. package/src/shadcn/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
  377. package/src/shadcn/dropdown-menu/dropdown-menu-radio-item.svelte +34 -0
  378. package/src/shadcn/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  379. package/src/shadcn/dropdown-menu/dropdown-menu-shortcut.svelte +23 -0
  380. package/src/shadcn/dropdown-menu/dropdown-menu-sub-content.svelte +20 -0
  381. package/src/shadcn/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
  382. package/src/shadcn/dropdown-menu/dropdown-menu-sub.svelte +7 -0
  383. package/src/shadcn/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
  384. package/src/shadcn/dropdown-menu/dropdown-menu.svelte +7 -0
  385. package/src/shadcn/dropdown-menu/index.ts +56 -0
  386. package/src/shadcn/dropdown-menu/menu-item.ts +43 -0
  387. package/src/shadcn/empty/empty-content.svelte +23 -0
  388. package/src/shadcn/empty/empty-description.svelte +23 -0
  389. package/src/shadcn/empty/empty-header.svelte +20 -0
  390. package/src/shadcn/empty/empty-media.svelte +41 -0
  391. package/src/shadcn/empty/empty-title.svelte +20 -0
  392. package/src/shadcn/empty/empty.svelte +23 -0
  393. package/src/shadcn/empty/index.ts +22 -0
  394. package/src/shadcn/field/field-content.svelte +20 -0
  395. package/src/shadcn/field/field-description.svelte +25 -0
  396. package/src/shadcn/field/field-error.svelte +58 -0
  397. package/src/shadcn/field/field-group.svelte +23 -0
  398. package/src/shadcn/field/field-label.svelte +26 -0
  399. package/src/shadcn/field/field-legend.svelte +29 -0
  400. package/src/shadcn/field/field-separator.svelte +35 -0
  401. package/src/shadcn/field/field-set.svelte +24 -0
  402. package/src/shadcn/field/field-title.svelte +23 -0
  403. package/src/shadcn/field/field.svelte +53 -0
  404. package/src/shadcn/field/index.ts +36 -0
  405. package/src/shadcn/field/simple-field.svelte +71 -0
  406. package/src/shadcn/field/simple-field.test.ts +47 -0
  407. package/src/shadcn/hooks/highlighter.ts +132 -0
  408. package/src/shadcn/hooks/is-mobile.svelte.ts +9 -0
  409. package/src/shadcn/hooks/use-highlighted-code.svelte.ts +98 -0
  410. package/src/shadcn/hover-card/hover-card-content.svelte +31 -0
  411. package/src/shadcn/hover-card/hover-card-portal.svelte +7 -0
  412. package/src/shadcn/hover-card/hover-card-trigger.svelte +7 -0
  413. package/src/shadcn/hover-card/hover-card.svelte +7 -0
  414. package/src/shadcn/hover-card/index.ts +15 -0
  415. package/src/shadcn/input/index.ts +9 -0
  416. package/src/shadcn/input/input.svelte +48 -0
  417. package/src/shadcn/input/secret-input.svelte +75 -0
  418. package/src/shadcn/input-otp/index.ts +15 -0
  419. package/src/shadcn/input-otp/input-otp-group.svelte +20 -0
  420. package/src/shadcn/input-otp/input-otp-separator.svelte +25 -0
  421. package/src/shadcn/input-otp/input-otp-slot.svelte +26 -0
  422. package/src/shadcn/input-otp/input-otp.svelte +22 -0
  423. package/src/shadcn/kbd/index.ts +10 -0
  424. package/src/shadcn/kbd/kbd-group.svelte +20 -0
  425. package/src/shadcn/kbd/kbd.svelte +23 -0
  426. package/src/shadcn/label/index.ts +7 -0
  427. package/src/shadcn/label/label.svelte +20 -0
  428. package/src/shadcn/marker/index.ts +15 -0
  429. package/src/shadcn/marker/marker-content.svelte +23 -0
  430. package/src/shadcn/marker/marker-icon.svelte +21 -0
  431. package/src/shadcn/marker/marker.svelte +52 -0
  432. package/src/shadcn/message/index.ts +22 -0
  433. package/src/shadcn/message/message-avatar.svelte +23 -0
  434. package/src/shadcn/message/message-content.svelte +23 -0
  435. package/src/shadcn/message/message-footer.svelte +23 -0
  436. package/src/shadcn/message/message-group.svelte +20 -0
  437. package/src/shadcn/message/message-header.svelte +23 -0
  438. package/src/shadcn/message/message.svelte +27 -0
  439. package/src/shadcn/message-scroller/index.ts +28 -0
  440. package/src/shadcn/message-scroller/message-scroller-button.svelte +52 -0
  441. package/src/shadcn/message-scroller/message-scroller-content.svelte +17 -0
  442. package/src/shadcn/message-scroller/message-scroller-item.svelte +22 -0
  443. package/src/shadcn/message-scroller/message-scroller-provider.svelte +7 -0
  444. package/src/shadcn/message-scroller/message-scroller-viewport.svelte +20 -0
  445. package/src/shadcn/message-scroller/message-scroller.svelte +20 -0
  446. package/src/shadcn/popover/index.ts +28 -0
  447. package/src/shadcn/popover/popover-close.svelte +7 -0
  448. package/src/shadcn/popover/popover-content.svelte +38 -0
  449. package/src/shadcn/popover/popover-description.svelte +20 -0
  450. package/src/shadcn/popover/popover-header.svelte +20 -0
  451. package/src/shadcn/popover/popover-portal.svelte +7 -0
  452. package/src/shadcn/popover/popover-title.svelte +15 -0
  453. package/src/shadcn/popover/popover-trigger.svelte +17 -0
  454. package/src/shadcn/popover/popover.svelte +7 -0
  455. package/src/shadcn/progress/index.ts +7 -0
  456. package/src/shadcn/progress/progress.svelte +30 -0
  457. package/src/shadcn/radio-group/index.ts +10 -0
  458. package/src/shadcn/radio-group/radio-group-item.svelte +32 -0
  459. package/src/shadcn/radio-group/radio-group.svelte +19 -0
  460. package/src/shadcn/scroll-area/index.ts +10 -0
  461. package/src/shadcn/scroll-area/scroll-area-scrollbar.svelte +30 -0
  462. package/src/shadcn/scroll-area/scroll-area.svelte +43 -0
  463. package/src/shadcn/select/index.ts +37 -0
  464. package/src/shadcn/select/select-content.svelte +45 -0
  465. package/src/shadcn/select/select-group-heading.svelte +21 -0
  466. package/src/shadcn/select/select-group.svelte +17 -0
  467. package/src/shadcn/select/select-item.svelte +38 -0
  468. package/src/shadcn/select/select-label.svelte +20 -0
  469. package/src/shadcn/select/select-portal.svelte +7 -0
  470. package/src/shadcn/select/select-scroll-down-button.svelte +23 -0
  471. package/src/shadcn/select/select-scroll-up-button.svelte +23 -0
  472. package/src/shadcn/select/select-separator.svelte +18 -0
  473. package/src/shadcn/select/select-trigger.svelte +33 -0
  474. package/src/shadcn/select/select.svelte +11 -0
  475. package/src/shadcn/separator/index.ts +7 -0
  476. package/src/shadcn/separator/separator.svelte +23 -0
  477. package/src/shadcn/sheet/index.ts +34 -0
  478. package/src/shadcn/sheet/sheet-close.svelte +7 -0
  479. package/src/shadcn/sheet/sheet-content.svelte +73 -0
  480. package/src/shadcn/sheet/sheet-description.svelte +17 -0
  481. package/src/shadcn/sheet/sheet-footer.svelte +20 -0
  482. package/src/shadcn/sheet/sheet-header.svelte +20 -0
  483. package/src/shadcn/sheet/sheet-overlay.svelte +20 -0
  484. package/src/shadcn/sheet/sheet-portal.svelte +7 -0
  485. package/src/shadcn/sheet/sheet-title.svelte +17 -0
  486. package/src/shadcn/sheet/sheet-trigger.svelte +7 -0
  487. package/src/shadcn/sheet/sheet.css +48 -0
  488. package/src/shadcn/sheet/sheet.svelte +7 -0
  489. package/src/shadcn/sidebar/constants.ts +6 -0
  490. package/src/shadcn/sidebar/context.svelte.ts +81 -0
  491. package/src/shadcn/sidebar/index.ts +75 -0
  492. package/src/shadcn/sidebar/sidebar-content.svelte +24 -0
  493. package/src/shadcn/sidebar/sidebar-footer.svelte +21 -0
  494. package/src/shadcn/sidebar/sidebar-group-action.svelte +33 -0
  495. package/src/shadcn/sidebar/sidebar-group-content.svelte +21 -0
  496. package/src/shadcn/sidebar/sidebar-group-label.svelte +33 -0
  497. package/src/shadcn/sidebar/sidebar-group.svelte +21 -0
  498. package/src/shadcn/sidebar/sidebar-header.svelte +21 -0
  499. package/src/shadcn/sidebar/sidebar-input.svelte +21 -0
  500. package/src/shadcn/sidebar/sidebar-inset.svelte +23 -0
  501. package/src/shadcn/sidebar/sidebar-menu-action.svelte +37 -0
  502. package/src/shadcn/sidebar/sidebar-menu-badge.svelte +24 -0
  503. package/src/shadcn/sidebar/sidebar-menu-button.svelte +103 -0
  504. package/src/shadcn/sidebar/sidebar-menu-item.svelte +21 -0
  505. package/src/shadcn/sidebar/sidebar-menu-skeleton.svelte +36 -0
  506. package/src/shadcn/sidebar/sidebar-menu-sub-button.svelte +39 -0
  507. package/src/shadcn/sidebar/sidebar-menu-sub-item.svelte +21 -0
  508. package/src/shadcn/sidebar/sidebar-menu-sub.svelte +24 -0
  509. package/src/shadcn/sidebar/sidebar-menu.svelte +21 -0
  510. package/src/shadcn/sidebar/sidebar-provider.svelte +62 -0
  511. package/src/shadcn/sidebar/sidebar-rail.svelte +36 -0
  512. package/src/shadcn/sidebar/sidebar-separator.svelte +19 -0
  513. package/src/shadcn/sidebar/sidebar-trigger.svelte +37 -0
  514. package/src/shadcn/sidebar/sidebar.svelte +109 -0
  515. package/src/shadcn/skeleton/index.ts +7 -0
  516. package/src/shadcn/skeleton/skeleton.svelte +17 -0
  517. package/src/shadcn/slider/index.ts +7 -0
  518. package/src/shadcn/slider/slider.svelte +52 -0
  519. package/src/shadcn/sonner/index.ts +1 -0
  520. package/src/shadcn/sonner/sonner.svelte +31 -0
  521. package/src/shadcn/spinner/index.ts +1 -0
  522. package/src/shadcn/spinner/spinner.svelte +26 -0
  523. package/src/shadcn/switch/index.ts +7 -0
  524. package/src/shadcn/switch/switch.svelte +31 -0
  525. package/src/shadcn/table/index.ts +28 -0
  526. package/src/shadcn/table/table-body.svelte +20 -0
  527. package/src/shadcn/table/table-caption.svelte +20 -0
  528. package/src/shadcn/table/table-cell.svelte +20 -0
  529. package/src/shadcn/table/table-footer.svelte +20 -0
  530. package/src/shadcn/table/table-head.svelte +23 -0
  531. package/src/shadcn/table/table-header.svelte +20 -0
  532. package/src/shadcn/table/table-row.svelte +23 -0
  533. package/src/shadcn/table/table.svelte +25 -0
  534. package/src/shadcn/tabs/index.ts +16 -0
  535. package/src/shadcn/tabs/tabs-content.svelte +17 -0
  536. package/src/shadcn/tabs/tabs-list.svelte +36 -0
  537. package/src/shadcn/tabs/tabs-trigger.svelte +20 -0
  538. package/src/shadcn/tabs/tabs.svelte +19 -0
  539. package/src/shadcn/textarea/index.ts +7 -0
  540. package/src/shadcn/textarea/textarea.svelte +23 -0
  541. package/src/shadcn/toggle/index.ts +13 -0
  542. package/src/shadcn/toggle/toggle.svelte +53 -0
  543. package/src/shadcn/toggle-group/index.ts +10 -0
  544. package/src/shadcn/toggle-group/toggle-group-item.svelte +35 -0
  545. package/src/shadcn/toggle-group/toggle-group.svelte +66 -0
  546. package/src/shadcn/tooltip/context.ts +16 -0
  547. package/src/shadcn/tooltip/index.ts +19 -0
  548. package/src/shadcn/tooltip/tooltip-content.svelte +38 -0
  549. package/src/shadcn/tooltip/tooltip-portal.svelte +7 -0
  550. package/src/shadcn/tooltip/tooltip-provider.svelte +16 -0
  551. package/src/shadcn/tooltip/tooltip-trigger.svelte +7 -0
  552. package/src/shadcn/tooltip/tooltip.svelte +19 -0
  553. package/src/shadcn/trace/index.ts +29 -0
  554. package/src/shadcn/trace/trace-collapsible-content.svelte +29 -0
  555. package/src/shadcn/trace/trace-collapsible-trigger.svelte +29 -0
  556. package/src/shadcn/trace/trace-collapsible.svelte +20 -0
  557. package/src/shadcn/trace/trace-description.svelte +20 -0
  558. package/src/shadcn/trace/trace-icon.svelte +24 -0
  559. package/src/shadcn/trace/trace-line.svelte +31 -0
  560. package/src/shadcn/trace/trace-title.svelte +20 -0
  561. package/src/shadcn/trace/trace.svelte +23 -0
  562. package/src/shadcn/utilities.css +549 -0
  563. package/src/test-setup.ts +34 -0
  564. package/src/tokens.css +993 -0
@@ -0,0 +1,20 @@
1
+ import { describe, expect, it } from "vite-plus/test";
2
+ import { segmentedTrackClass } from "./segmented.js";
3
+
4
+ describe("segmentedTrackClass", () => {
5
+ // Every segmented control shares one height, whatever its look.
6
+ it("holds one height across variants", () => {
7
+ for (const variant of ["pill", "underline"] as const) {
8
+ const classes = segmentedTrackClass({ variant }).split(" ");
9
+ expect(classes).toContain("h-7");
10
+ expect(classes).toContain("any-pointer-coarse:h-10");
11
+ }
12
+ });
13
+
14
+ it("draws the pill as a bordered well and the underline as a hairline", () => {
15
+ expect(segmentedTrackClass({ variant: "pill" })).toContain("rounded-lg");
16
+ const underline = segmentedTrackClass({ variant: "underline" });
17
+ expect(underline).toContain("border-b");
18
+ expect(underline).not.toContain("rounded-lg");
19
+ });
20
+ });
@@ -0,0 +1,80 @@
1
+ import { tv } from "tailwind-variants";
2
+ import { pageGutterClass } from "../../lib/layout.js";
3
+
4
+ /**
5
+ * The one look for a segmented control: the track and trigger here, what a
6
+ * segment says in `segment.svelte`.
7
+ */
8
+
9
+ // WHY a recipe, not one component: tabs are a `tablist`, filters a `group` of
10
+ // `aria-pressed` toggles, page links a `nav`. Only the ARIA shape may differ.
11
+
12
+ // The look used to drift three ways, one with an at-rest shadow. All three read from here now.
13
+ export const segmentedTrackClass = tv({
14
+ // WHY the height lives on the track: it carries the inset, so an `h-7` trigger
15
+ // drew 30px beside a 28px button. One toolbar row came out at five heights.
16
+
17
+ // WHY no size variant: `sm` drew 24px, and every tab control must share one height.
18
+ base: "h-7 any-pointer-coarse:h-10 shrink-0 items-stretch",
19
+ variants: {
20
+ // `pill`: a bordered well with a travelling highlight. `underline`: bare
21
+ // labels over one hairline, the active one marked by a rule that travels.
22
+ variant: {
23
+ pill: "overflow-hidden rounded-lg border border-border p-0.5",
24
+ underline: "gap-5 border-b border-border",
25
+ },
26
+ // Fill the row rather than sit at its natural width. For a narrow rail.
27
+ fill: { true: "flex w-full", false: "inline-flex w-fit" },
28
+ },
29
+ defaultVariants: { variant: "pill", fill: false },
30
+ });
31
+
32
+ export type SegmentedVariant = "pill" | "underline";
33
+
34
+ export const segmentedTriggerClass = tv({
35
+ base: [
36
+ // `t-press` is the kit's one press: colour, shadow and a pixel of travel.
37
+
38
+ // `relative z-[1]` keeps the label above the travelling pill, or the pill
39
+ // paints over the word it highlights.
40
+
41
+ // `ui-segment` isn't a style. `segmented-track.svelte` matches on it to time
42
+ // the label colour with the pill.
43
+ "ui-segment group/segment t-press relative z-[1] inline-flex items-center justify-center gap-1.5 whitespace-nowrap font-medium select-none",
44
+ "text-muted-foreground hover:text-foreground",
45
+ "disabled:pointer-events-none disabled:opacity-50",
46
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
47
+ // WHY an inset focus ring: the track clips to its rounded corners, so an
48
+ // outside ring was cut in half on the end segments.
49
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-inset focus-visible:outline-none",
50
+ // WHY no height on the trigger: an icon makes a segment taller than a word.
51
+ // `h-full` against the track's `items-stretch` keeps every segment the same box.
52
+ "h-full text-xs",
53
+ ],
54
+ variants: {
55
+ // The pill is concentric with the track's border and its 2px inset. The
56
+ // underline has no well to be concentric with, so no padding either: the
57
+ // rule under a label is as wide as the label.
58
+ variant: {
59
+ pill: "rounded-[calc(var(--radius-lg)-3px)] px-2.5 any-pointer-coarse:px-3.5",
60
+ underline: "rounded-sm px-0",
61
+ },
62
+ // WHY `min-w-fit flex-auto`, not equal fractions: fractions cut "Approved" to
63
+ // "Appro…" in a 280px rail. Each segment claims its label and shares the slack.
64
+ fill: { true: "min-w-fit flex-auto", false: "" },
65
+ active: { true: "text-foreground", false: "" },
66
+ },
67
+ defaultVariants: { variant: "pill", fill: false, active: false },
68
+ });
69
+
70
+ /**
71
+ * The row a segmented strip sits in, and the one recipe for a navigation row
72
+ * under the app header. Written per caller it produced two different left
73
+ * edges.
74
+ */
75
+ // WHY no bottom hairline: with a section strip and a page toolbar stacked it
76
+ // drew two rules for one boundary. The gap is the boundary.
77
+ export const segmentedRowClass = `flex shrink-0 flex-wrap items-center gap-x-4 gap-y-2 py-2 ${pageGutterClass}`;
78
+
79
+ /** The active style, for a strip whose state lives in a `data-active` attribute. */
80
+ export const segmentedActiveClass = "data-active:text-foreground";
@@ -0,0 +1,9 @@
1
+ import Root from "./star-rating-input.svelte";
2
+
3
+ export type { StarRatingSize } from "./star-rating-input.svelte";
4
+
5
+ export {
6
+ Root,
7
+ //
8
+ Root as StarRatingInput,
9
+ };
@@ -0,0 +1,256 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Two sizes, picked by the job: `sm` reads a value back in a row or cell,
4
+ * `lg` is the ask.
5
+ */
6
+ // A middle `md` step used to exist. Nobody could place it, so the same question
7
+ // ended up asked at three sizes.
8
+ export type StarRatingSize = "sm" | "lg";
9
+
10
+ const STAR_SIZE: Record<StarRatingSize, string> = {
11
+ sm: "size-4",
12
+ lg: "size-7",
13
+ };
14
+
15
+ const HIT_SIZE: Record<StarRatingSize, string> = {
16
+ sm: "p-0.5",
17
+ lg: "p-1",
18
+ };
19
+
20
+ /** Pop duration plus the longest stagger, after which the class can come off. */
21
+ const POP_WINDOW_MS = 350 + 4 * 40;
22
+ </script>
23
+
24
+ <script lang="ts">
25
+ import { Star } from "phosphor-svelte";
26
+ import { cn } from "@heyramzi/utils";
27
+
28
+ type Props = {
29
+ /** Current rating value (0 = unrated). */
30
+ value: number;
31
+ /** Maximum rating. Defaults to 5. */
32
+ max?: number;
33
+ /** Visual size of each star. `sm` reads back, `lg` asks. */
34
+ size?: StarRatingSize;
35
+ /** Render as static display, no hover or click. */
36
+ readonly?: boolean;
37
+ /** Disable interaction. */
38
+ disabled?: boolean;
39
+ /** Form field name for native submission. When set, a hidden input is rendered. */
40
+ name?: string;
41
+ /** ARIA label for the radiogroup. */
42
+ ariaLabel?: string;
43
+ /** Per-step descriptive labels (length must equal max). */
44
+ labels?: readonly string[];
45
+ /** Show the label of the currently hovered/selected step beside the stars. */
46
+ showLabel?: boolean;
47
+ class?: string;
48
+ onchange?: (value: number) => void;
49
+ };
50
+
51
+ let {
52
+ value = $bindable(0),
53
+ max = 5,
54
+ size = "sm",
55
+ readonly = false,
56
+ disabled = false,
57
+ name,
58
+ ariaLabel,
59
+ labels,
60
+ showLabel = false,
61
+ class: className,
62
+ onchange,
63
+ }: Props = $props();
64
+
65
+ let hover = $state(0);
66
+ /** Held for the length of the pop, then dropped so the next pick can replay it. */
67
+ let popping = $state(false);
68
+ let popTimer: ReturnType<typeof setTimeout> | undefined;
69
+ let displayed = $derived(hover || value);
70
+ let starSize = $derived(STAR_SIZE[size]);
71
+ let hitSize = $derived(HIT_SIZE[size]);
72
+ let groupAriaLabel = $derived(ariaLabel ?? `Rating, ${max} stars`);
73
+ let labelText = $derived(
74
+ displayed > 0 && labels && labels.length >= displayed ? labels[displayed - 1] : "",
75
+ );
76
+
77
+ function buttonAriaLabel(step: number): string {
78
+ const stepLabel = labels?.[step - 1];
79
+ const base = `${step} of ${max}`;
80
+ return stepLabel ? `${base} (${stepLabel})` : base;
81
+ }
82
+
83
+ function setRating(step: number) {
84
+ if (readonly || disabled) return;
85
+ value = step;
86
+ onchange?.(step);
87
+
88
+ // Drop the class before re-adding it, or a second pick inside the animation
89
+ // window finds it already set and nothing replays.
90
+ popping = false;
91
+ clearTimeout(popTimer);
92
+ requestAnimationFrame(() => {
93
+ popping = true;
94
+ popTimer = setTimeout(() => (popping = false), POP_WINDOW_MS);
95
+ });
96
+ }
97
+
98
+ function handleEnter(step: number) {
99
+ if (readonly || disabled) return;
100
+ hover = step;
101
+ }
102
+
103
+ function handleLeave() {
104
+ if (readonly || disabled) return;
105
+ hover = 0;
106
+ }
107
+ </script>
108
+
109
+ <!-- One star, drawn once. It used to be written twice, once per branch, and the
110
+ two copies had already drifted apart on the glow. -->
111
+ {#snippet star(step: number)}
112
+ <Star
113
+ class={cn("star", starSize, step <= displayed && "is-filled")}
114
+ weight={step <= displayed ? "fill" : "regular"}
115
+ />
116
+ {/snippet}
117
+
118
+ <div class={cn("rating", `rating-${size}`, className)}>
119
+ <div
120
+ class={cn("stars", popping && "is-popping")}
121
+ role={readonly ? "img" : "radiogroup"}
122
+ aria-label={groupAriaLabel}
123
+ onmouseleave={handleLeave}
124
+ >
125
+ {#each Array(max) as _, i (i)}
126
+ {@const step = i + 1}
127
+ {#if readonly}
128
+ <span class={cn("hit", hitSize)}>{@render star(step)}</span>
129
+ {:else}
130
+ <button
131
+ type="button"
132
+ role="radio"
133
+ aria-checked={value === step}
134
+ aria-label={buttonAriaLabel(step)}
135
+ {disabled}
136
+ data-on={step <= value}
137
+ onclick={() => setRating(step)}
138
+ onmouseenter={() => handleEnter(step)}
139
+ onfocus={() => handleEnter(step)}
140
+ onblur={handleLeave}
141
+ style:--pop-delay="{i * 40}ms"
142
+ class={cn("hit hit-button", hitSize, disabled && "is-disabled")}
143
+ >
144
+ {@render star(step)}
145
+ </button>
146
+ {/if}
147
+ {/each}
148
+ </div>
149
+
150
+ {#if showLabel && labelText}
151
+ <span class="rating-caption">{labelText}</span>
152
+ {/if}
153
+
154
+ {#if name}
155
+ <input type="hidden" {name} value={value} />
156
+ {/if}
157
+ </div>
158
+
159
+ <style>
160
+ .rating {
161
+ --like-pop: var(--duration-medium);
162
+ --like-pop-ease: var(--ease-smooth-out);
163
+ --like-fill: var(--duration-quick);
164
+ --like-ease: var(--ease-smooth-out);
165
+
166
+ display: flex;
167
+ align-items: center;
168
+ gap: 0.25rem;
169
+ }
170
+
171
+ .stars {
172
+ display: flex;
173
+ align-items: center;
174
+ }
175
+
176
+ .hit {
177
+ display: inline-flex;
178
+ border-radius: var(--radius-sm);
179
+ }
180
+
181
+ .hit-button {
182
+ transition: transform var(--duration-quick, 150ms) var(--ease-smooth-out, ease);
183
+ }
184
+
185
+ .hit-button:focus-visible {
186
+ outline: none;
187
+ box-shadow: 0 0 0 2px var(--color-ring);
188
+ }
189
+
190
+ .hit-button:not(.is-disabled):hover {
191
+ transform: scale(1.1);
192
+ }
193
+
194
+ .hit-button:not(.is-disabled):active {
195
+ transform: scale(0.95);
196
+ }
197
+
198
+ .is-disabled {
199
+ cursor: not-allowed;
200
+ opacity: 0.6;
201
+ }
202
+
203
+ .rating :global(.star) {
204
+ color: var(--color-border);
205
+ transition: color var(--like-fill) var(--like-ease);
206
+ }
207
+
208
+ .rating :global(.star.is-filled) {
209
+ color: var(--color-warning-400);
210
+ }
211
+
212
+ /* The glow is the size's business, not a prop: it belongs to the ask, where
213
+ the stars are large enough to carry it, and it turned to mud at 16px. */
214
+ .rating-lg :global(.star.is-filled) {
215
+ filter: drop-shadow(0 0 4px color-mix(in oklab, var(--color-warning-400) 45%, transparent));
216
+ }
217
+
218
+ /* transitions.dev like-button pop. Every selected star pops, left to right,
219
+ as one gesture. A class on the group drives it, since rebuilding the row
220
+ would drop keyboard focus. */
221
+ .is-popping .hit-button[data-on="true"] {
222
+ animation: t-like-pop var(--like-pop) var(--like-pop-ease) var(--pop-delay, 0ms);
223
+ }
224
+
225
+ @keyframes t-like-pop {
226
+ 0% {
227
+ transform: scale(1);
228
+ }
229
+ 30% {
230
+ transform: scale(0.82);
231
+ }
232
+ 100% {
233
+ transform: scale(1);
234
+ }
235
+ }
236
+
237
+ .rating-caption {
238
+ font-size: 0.75rem;
239
+ font-weight: 500;
240
+ font-variant-numeric: tabular-nums;
241
+ color: var(--color-muted-foreground);
242
+ }
243
+
244
+ @media (prefers-reduced-motion: reduce) {
245
+ .hit-button,
246
+ .is-popping .hit-button[data-on="true"] {
247
+ animation: none;
248
+ transition: none;
249
+ }
250
+
251
+ .hit-button:not(.is-disabled):hover,
252
+ .hit-button:not(.is-disabled):active {
253
+ transform: none;
254
+ }
255
+ }
256
+ </style>
@@ -0,0 +1 @@
1
+ export { default as Svg } from "./svg.svelte";
@@ -0,0 +1,9 @@
1
+ <script lang="ts">
2
+ // Renders a drawing kept in its own .svg file, imported with `?raw`.
3
+ // The markup is ours and static, so rendering it as HTML is safe.
4
+ let { src, class: className = "" }: { src: string; class?: string } = $props();
5
+
6
+ const markup = $derived(className ? src.replace("<svg", `<svg class="${className}"`) : src);
7
+ </script>
8
+
9
+ {@html markup}
@@ -0,0 +1,270 @@
1
+ /* The craft layer, the opinionated half of a design system.
2
+ *
3
+ * A palette says which colours a product uses. This file says how a surface
4
+ * lifts off the page, how round a control is against a card, and how long an
5
+ * overlay takes to arrive. Those three answers are what separate a kit that
6
+ * looks assembled from one that looks made, and they were the part every app
7
+ * here was re-deciding per component.
8
+ *
9
+ * WHY it ships beside the palette rather than inside it: the palette is the
10
+ * brand and differs per app; elevation, radius and motion are craft and should
11
+ * not. An app imports this, then tints it with `--elev-tint` and nothing else.
12
+ *
13
+ * Studied from beautifului.dev and rareui.com, which both build the same three
14
+ * ladders: a hairline that is a shadow rather than a border, a radius that
15
+ * steps by the role of the box, and a close that is faster than its open.
16
+ *
17
+ * Import once, after Tailwind:
18
+ * @import "@heyramzi/ui/design";
19
+ */
20
+
21
+ @import "./polish.css";
22
+
23
+ /* ── Radius, by role not by size ─────────────────────────────
24
+ A chip inside a control inside a card has to read as nested, which needs
25
+ three radii that step, not one radius reused. The Tailwind names are the
26
+ home so `rounded-lg` on a button already resolves to the control radius and
27
+ no component has to learn a new class. */
28
+ @theme {
29
+ --radius-xs: 4px; /* tag, dot, inline code */
30
+ --radius-sm: 6px; /* chip, badge, small pill */
31
+ --radius-md: 7px; /* between a chip and a control */
32
+ --radius-lg: 8px; /* control: button, input, select, menu item */
33
+ --radius-xl: 10px; /* card, panel, stage */
34
+ --radius-2xl: 14px; /* overlay: dialog, sheet, command palette */
35
+ --radius-3xl: 20px; /* hero surface, full-bleed panel */
36
+ }
37
+
38
+ /* ── Elevation ───────────────────────────────────────────────
39
+ Four steps of lift, plus the two edges that are not lift.
40
+
41
+ WHY the steps are shadow-only and the hairline is its own token: most boxes
42
+ in a kit already draw a border, and an elevation that bakes a `0 0 0 1px`
43
+ ring into every step gives those boxes two edges a pixel apart. Compose them
44
+ where a component drops its border:
45
+
46
+ box-shadow: var(--elev-hairline), var(--elev-card);
47
+
48
+ WHY each step is stacked rather than one blurred drop: a single shadow reads
49
+ as a sticker. Depth is several offsets falling off together, which is what
50
+ the eye reads as distance. They cost nothing extra because every stop is the
51
+ same colour at a different alpha.
52
+
53
+ WHY the blur radii are large and the alphas small: a 4px blur at 20% draws a
54
+ visible grey edge, which the eye reads as a second border. A 28px blur at 7%
55
+ is barely a colour anywhere and reads as height. Reach for more blur before
56
+ more alpha, every time.
57
+
58
+ `--elev-tint` is the one knob an app turns. Pure black greys a warm palette
59
+ out; the hue of the app's own darkest surface keeps the shadow in the
60
+ family. */
61
+ :root {
62
+ --elev-tint: oklch(0 0 0);
63
+
64
+ /* Resting on the page. A button, a chip, a row. */
65
+ --elev-btn:
66
+ 0 1px 1px -0.5px color-mix(in oklab, var(--elev-tint) 7%, transparent),
67
+ 0 2px 5px -2px color-mix(in oklab, var(--elev-tint) 6%, transparent);
68
+
69
+ /* Sitting on the page. A card, a stage, a panel. */
70
+ --elev-card:
71
+ 0 1px 2px -1px color-mix(in oklab, var(--elev-tint) 7%, transparent),
72
+ 0 4px 9px -3px color-mix(in oklab, var(--elev-tint) 6%, transparent),
73
+ 0 14px 28px -10px color-mix(in oklab, var(--elev-tint) 7%, transparent);
74
+
75
+ /* Lifted off the page. A hovered card, a popover, a menu. */
76
+ --elev-raised:
77
+ 0 1px 2px -1px color-mix(in oklab, var(--elev-tint) 8%, transparent),
78
+ 0 6px 13px -4px color-mix(in oklab, var(--elev-tint) 9%, transparent),
79
+ 0 20px 40px -12px color-mix(in oklab, var(--elev-tint) 12%, transparent);
80
+
81
+ /* Floating over a dimmed page. A dialog, a sheet, a command palette. */
82
+ --elev-overlay:
83
+ 0 2px 4px -1px color-mix(in oklab, var(--elev-tint) 8%, transparent),
84
+ 0 12px 26px -8px color-mix(in oklab, var(--elev-tint) 12%, transparent),
85
+ 0 40px 72px -20px color-mix(in oklab, var(--elev-tint) 20%, transparent);
86
+
87
+ /* The two edges. A hairline separates a surface from what is behind it; a rim
88
+ is the light catching its top edge, which is what makes a filled control
89
+ read as a physical key rather than a coloured rectangle. */
90
+ --elev-hairline: 0 0 0 1px var(--color-border, var(--border));
91
+ --elev-rim: inset 0 1px 0 color-mix(in oklab, white 16%, transparent);
92
+
93
+ /* A field is the one box that goes in rather than up. */
94
+ --elev-field: inset 0 1px 2px color-mix(in oklab, var(--elev-tint) 7%, transparent);
95
+ }
96
+
97
+ /* A dark surface cannot cast a darker shadow than the page it sits on, so the
98
+ hairline does the separating and the shadow only sinks what is behind it. On
99
+ dark the hairline is white at a low alpha, which is what reads as a lit edge
100
+ rather than as a drawn border. */
101
+ .dark {
102
+ --elev-btn: 0 1px 2px color-mix(in oklab, black 30%, transparent);
103
+ --elev-card:
104
+ 0 1px 2px color-mix(in oklab, black 24%, transparent),
105
+ 0 4px 12px -4px color-mix(in oklab, black 30%, transparent);
106
+ --elev-raised:
107
+ 0 2px 6px color-mix(in oklab, black 30%, transparent),
108
+ 0 10px 24px -6px color-mix(in oklab, black 38%, transparent);
109
+ --elev-overlay:
110
+ 0 4px 10px color-mix(in oklab, black 34%, transparent),
111
+ 0 24px 56px -12px color-mix(in oklab, black 50%, transparent);
112
+
113
+ --elev-hairline: 0 0 0 1px color-mix(in oklab, white 10%, transparent);
114
+ --elev-rim: inset 0 1px 0 color-mix(in oklab, white 10%, transparent);
115
+ --elev-field: inset 0 1px 2px color-mix(in oklab, black 40%, transparent);
116
+ }
117
+
118
+ /* ── Focus ───────────────────────────────────────────────────
119
+ One ring, two parts: a solid edge that redraws the control's own outline in
120
+ the accent, and a soft halo that carries it off the surface. Written as a
121
+ variable so a component spends one line on it and every control focuses the
122
+ same way. */
123
+ :root {
124
+ --ring-color: var(--color-ring, var(--ring));
125
+ --ring-halo: color-mix(in oklab, var(--ring-color) 16%, transparent);
126
+ /* A solid pixel redrawing the control's own edge, then a wide soft band. At a
127
+ higher alpha the band reads as a second border rather than as a glow, which
128
+ is why it is 16% over 4px and not 30% over 2px. */
129
+ --ring-focus: 0 0 0 1px var(--ring-color), 0 0 0 5px var(--ring-halo);
130
+ }
131
+
132
+ /* ── Motion ──────────────────────────────────────────────────
133
+ The transitions-dev scale, verbatim, so a snippet pasted from that skill and
134
+ a component written here read from the same names.
135
+
136
+ The doctrine in one line: an open is an invitation and a close should get out
137
+ of the way, so every close is shorter than its open and no close bounces. */
138
+ :root {
139
+ --duration-stagger: 40ms;
140
+ --duration-micro: 80ms;
141
+ --duration-quick: 150ms; /* every close, text swap, tooltip in */
142
+ --duration-fast: 250ms; /* every open, tabs, icon swap, page slide */
143
+ --duration-medium: 350ms; /* panel close, toast close */
144
+ --duration-slow: 400ms; /* panel open, skeleton reveal */
145
+ --duration-very-slow: 500ms; /* emphasis: badge, text reveal, success check */
146
+
147
+ --ease-smooth-out: cubic-bezier(0.22, 1, 0.36, 1); /* all surface motion */
148
+ --ease-in-out: ease-in-out; /* swaps and cross-fades */
149
+ --ease-out: ease-out; /* tooltips */
150
+ --ease-linear: linear; /* shimmer, pulse, spinner */
151
+
152
+ --distance-micro: 4px;
153
+ --distance-small: 6px;
154
+ --distance-base: 8px;
155
+ --distance-medium: 12px;
156
+ --distance-large: 30px;
157
+
158
+ --scale-large: 0.96; /* modal */
159
+ --scale-medium: 0.97; /* dropdown, popover open */
160
+ --scale-small: 0.98; /* tooltip */
161
+ --scale-tiny: 0.99; /* dropdown close */
162
+
163
+ --blur-small: 2px;
164
+ --blur-medium: 3px;
165
+ --blur-large: 8px;
166
+ }
167
+
168
+ /* Tailwind utility aliases, so `shadow-card`, `duration-fast` and
169
+ `ease-smooth-out` exist as classes and a component can stay in utilities
170
+ instead of dropping into a style block for one shadow. */
171
+ @theme inline {
172
+ --shadow-hairline: var(--elev-hairline);
173
+ --shadow-btn: var(--elev-btn);
174
+ --shadow-card: var(--elev-card);
175
+ --shadow-raised: var(--elev-raised);
176
+ --shadow-overlay: var(--elev-overlay);
177
+ --shadow-field: var(--elev-field);
178
+ --shadow-rim: var(--elev-rim);
179
+ /* A filled control: the lit top edge and the resting lift, in one class,
180
+ because the two never appear apart and utilities cannot be stacked. */
181
+ --shadow-key: var(--elev-rim), var(--elev-btn);
182
+ /* The composed pair, for a surface that draws no border of its own. */
183
+ --shadow-edge-card: var(--elev-hairline), var(--elev-card);
184
+ --shadow-edge-raised: var(--elev-hairline), var(--elev-raised);
185
+ --shadow-edge-overlay: var(--elev-hairline), var(--elev-overlay);
186
+
187
+ --ease-smooth-out: var(--ease-smooth-out);
188
+
189
+ /* WHY the durations are re-declared under a second name: Tailwind's
190
+ `duration-*` utility reads `--transition-duration-*`, not `--duration-*`, so
191
+ the six names above generated no class at all. `duration-fast` therefore
192
+ compiled to nothing and the element ended up at `transition-duration: 0s`:
193
+ no error, no warning, and a component that simply does not animate. The
194
+ comment at the top of this block had claimed `duration-fast` was a class
195
+ since the block was written. Found 31 Aug 2026 by probing a live page. */
196
+ --transition-duration-micro: var(--duration-micro);
197
+ --transition-duration-quick: var(--duration-quick);
198
+ --transition-duration-fast: var(--duration-fast);
199
+ --transition-duration-medium: var(--duration-medium);
200
+ --transition-duration-slow: var(--duration-slow);
201
+ --transition-duration-very-slow: var(--duration-very-slow);
202
+ }
203
+
204
+ /* The bits-ui state variants every primitive in `./shadcn` is written against.
205
+ *
206
+ * bits-ui v2 writes `data-checked` / `data-open` and v1 wrote
207
+ * `data-state="checked"`, so each variant matches both and a component does not
208
+ * have to know which it is talking to. They live here rather than in a
209
+ * consumer's stylesheet because the classes that need them ship in this
210
+ * package: an app that defines only the palette rendered a switch that never
211
+ * moved. Moved out of upsys `tokens.css` on 14 Sep 2026, with the kit. */
212
+ @custom-variant data-open {
213
+ &:where([data-state="open"]),
214
+ &:where([data-open]:not([data-open="false"])) {
215
+ @slot;
216
+ }
217
+ }
218
+
219
+ @custom-variant data-closed {
220
+ &:where([data-state="closed"]),
221
+ &:where([data-closed]:not([data-closed="false"])) {
222
+ @slot;
223
+ }
224
+ }
225
+
226
+ @custom-variant data-checked {
227
+ &:where([data-state="checked"]),
228
+ &:where([data-checked]:not([data-checked="false"])) {
229
+ @slot;
230
+ }
231
+ }
232
+
233
+ @custom-variant data-unchecked {
234
+ &:where([data-state="unchecked"]),
235
+ &:where([data-unchecked]:not([data-unchecked="false"])) {
236
+ @slot;
237
+ }
238
+ }
239
+
240
+ @custom-variant data-selected {
241
+ &:where([data-selected]) {
242
+ @slot;
243
+ }
244
+ }
245
+
246
+ @custom-variant data-disabled {
247
+ &:where([data-disabled="true"]),
248
+ &:where([data-disabled]:not([data-disabled="false"])) {
249
+ @slot;
250
+ }
251
+ }
252
+
253
+ @custom-variant data-active {
254
+ &:where([data-state="active"]),
255
+ &:where([data-active]:not([data-active="false"])) {
256
+ @slot;
257
+ }
258
+ }
259
+
260
+ @custom-variant data-horizontal {
261
+ &:where([data-orientation="horizontal"]) {
262
+ @slot;
263
+ }
264
+ }
265
+
266
+ @custom-variant data-vertical {
267
+ &:where([data-orientation="vertical"]) {
268
+ @slot;
269
+ }
270
+ }