agentic-ds-kit 0.1.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 (345) hide show
  1. package/AGENTS.md +10 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +21 -0
  4. package/README.md +38 -0
  5. package/contracts/accordion.json +38 -0
  6. package/contracts/alert.json +57 -0
  7. package/contracts/alertdialog.json +89 -0
  8. package/contracts/appheader.json +140 -0
  9. package/contracts/appnav.json +72 -0
  10. package/contracts/avatar.json +63 -0
  11. package/contracts/avatargroup.json +83 -0
  12. package/contracts/badge.json +92 -0
  13. package/contracts/barchart.json +55 -0
  14. package/contracts/breadcrumb.json +76 -0
  15. package/contracts/button.json +120 -0
  16. package/contracts/buttongroup.json +48 -0
  17. package/contracts/calendar.json +93 -0
  18. package/contracts/card.json +123 -0
  19. package/contracts/carousel.json +98 -0
  20. package/contracts/cell.json +203 -0
  21. package/contracts/chat.json +131 -0
  22. package/contracts/checkbox.json +101 -0
  23. package/contracts/collapsible.json +66 -0
  24. package/contracts/command.json +84 -0
  25. package/contracts/datatable.json +126 -0
  26. package/contracts/datepicker.json +102 -0
  27. package/contracts/drawer.json +93 -0
  28. package/contracts/dropdownmenu.json +190 -0
  29. package/contracts/empty.json +79 -0
  30. package/contracts/field.json +49 -0
  31. package/contracts/fieldset.json +29 -0
  32. package/contracts/headercell.json +107 -0
  33. package/contracts/index.json +478 -0
  34. package/contracts/input.json +119 -0
  35. package/contracts/inputotp.json +119 -0
  36. package/contracts/insightcard.json +130 -0
  37. package/contracts/linechart.json +51 -0
  38. package/contracts/loadinganimation.json +33 -0
  39. package/contracts/modal.json +100 -0
  40. package/contracts/modalcard.json +88 -0
  41. package/contracts/navigationmenu.json +65 -0
  42. package/contracts/numbertransition.json +33 -0
  43. package/contracts/pageheader.json +101 -0
  44. package/contracts/pagination.json +85 -0
  45. package/contracts/patterns/activity.json +30 -0
  46. package/contracts/patterns/assistant-workspace.json +16 -0
  47. package/contracts/patterns/dashboard.json +12 -0
  48. package/contracts/patterns/empty-first-run.json +15 -0
  49. package/contracts/patterns/inbox.json +32 -0
  50. package/contracts/patterns/invite-members.json +14 -0
  51. package/contracts/patterns/list-detail.json +13 -0
  52. package/contracts/patterns/settings-form.json +13 -0
  53. package/contracts/piechart.json +68 -0
  54. package/contracts/progress.json +124 -0
  55. package/contracts/progresssteps.json +91 -0
  56. package/contracts/radiogroup.json +132 -0
  57. package/contracts/scoreboard.json +46 -0
  58. package/contracts/scorecard.json +124 -0
  59. package/contracts/section.json +97 -0
  60. package/contracts/select.json +130 -0
  61. package/contracts/shimmertext.json +33 -0
  62. package/contracts/sidenav.json +133 -0
  63. package/contracts/slider.json +81 -0
  64. package/contracts/spinner.json +52 -0
  65. package/contracts/switch.json +94 -0
  66. package/contracts/table.json +92 -0
  67. package/contracts/tabs.json +90 -0
  68. package/contracts/textarea.json +54 -0
  69. package/contracts/thinkinganimation.json +32 -0
  70. package/contracts/timeline.json +49 -0
  71. package/contracts/toast.json +104 -0
  72. package/contracts/tooltip.json +66 -0
  73. package/lib/tokens.css +637 -0
  74. package/package.json +60 -0
  75. package/skills/prototype-from-kit/SKILL.md +29 -0
  76. package/src/index.ts +71 -0
  77. package/tokens/tokens.json +725 -0
  78. package/ui/Accordion/Accordion.module.css +68 -0
  79. package/ui/Accordion/Accordion.tsx +51 -0
  80. package/ui/Accordion/Accordion.types.ts +10 -0
  81. package/ui/Accordion/index.ts +2 -0
  82. package/ui/Alert/Alert.module.css +62 -0
  83. package/ui/Alert/Alert.tsx +27 -0
  84. package/ui/Alert/Alert.types.ts +7 -0
  85. package/ui/Alert/index.ts +2 -0
  86. package/ui/AlertDialog/AlertDialog.module.css +71 -0
  87. package/ui/AlertDialog/AlertDialog.tsx +109 -0
  88. package/ui/AlertDialog/AlertDialog.types.ts +14 -0
  89. package/ui/AlertDialog/index.ts +6 -0
  90. package/ui/AppHeader/AppHeader.module.css +120 -0
  91. package/ui/AppHeader/AppHeader.tsx +70 -0
  92. package/ui/AppHeader/AppHeader.types.ts +28 -0
  93. package/ui/AppHeader/index.ts +2 -0
  94. package/ui/AppNav/AppNav.module.css +168 -0
  95. package/ui/AppNav/AppNav.tsx +137 -0
  96. package/ui/AppNav/AppNav.types.ts +24 -0
  97. package/ui/AppNav/index.ts +2 -0
  98. package/ui/Avatar/Avatar.module.css +52 -0
  99. package/ui/Avatar/Avatar.tsx +62 -0
  100. package/ui/Avatar/Avatar.types.ts +8 -0
  101. package/ui/Avatar/index.ts +2 -0
  102. package/ui/AvatarGroup/AvatarGroup.module.css +49 -0
  103. package/ui/AvatarGroup/AvatarGroup.tsx +29 -0
  104. package/ui/AvatarGroup/AvatarGroup.types.ts +16 -0
  105. package/ui/AvatarGroup/index.ts +2 -0
  106. package/ui/Badge/Badge.module.css +92 -0
  107. package/ui/Badge/Badge.tsx +43 -0
  108. package/ui/Badge/Badge.types.ts +11 -0
  109. package/ui/Badge/index.ts +2 -0
  110. package/ui/BarChart/BarChart.module.css +56 -0
  111. package/ui/BarChart/BarChart.tsx +380 -0
  112. package/ui/BarChart/BarChart.types.ts +18 -0
  113. package/ui/BarChart/index.ts +2 -0
  114. package/ui/Breadcrumb/Breadcrumb.module.css +64 -0
  115. package/ui/Breadcrumb/Breadcrumb.tsx +89 -0
  116. package/ui/Breadcrumb/Breadcrumb.types.ts +14 -0
  117. package/ui/Breadcrumb/index.ts +2 -0
  118. package/ui/Button/Button.module.css +101 -0
  119. package/ui/Button/Button.tsx +49 -0
  120. package/ui/Button/Button.types.ts +18 -0
  121. package/ui/Button/index.ts +2 -0
  122. package/ui/Button/lucideName.tsx +188 -0
  123. package/ui/ButtonGroup/ButtonGroup.module.css +31 -0
  124. package/ui/ButtonGroup/ButtonGroup.tsx +12 -0
  125. package/ui/ButtonGroup/ButtonGroup.types.ts +6 -0
  126. package/ui/ButtonGroup/index.ts +2 -0
  127. package/ui/Calendar/Calendar.module.css +146 -0
  128. package/ui/Calendar/Calendar.tsx +296 -0
  129. package/ui/Calendar/Calendar.types.ts +11 -0
  130. package/ui/Calendar/index.ts +2 -0
  131. package/ui/Card/Card.module.css +95 -0
  132. package/ui/Card/Card.tsx +69 -0
  133. package/ui/Card/Card.types.ts +23 -0
  134. package/ui/Card/index.ts +2 -0
  135. package/ui/Carousel/Carousel.module.css +114 -0
  136. package/ui/Carousel/Carousel.tsx +152 -0
  137. package/ui/Carousel/Carousel.types.ts +17 -0
  138. package/ui/Carousel/index.ts +7 -0
  139. package/ui/Cell/Cell.module.css +133 -0
  140. package/ui/Cell/Cell.tsx +300 -0
  141. package/ui/Cell/Cell.types.ts +69 -0
  142. package/ui/Cell/index.ts +11 -0
  143. package/ui/Chat/Chat.module.css +149 -0
  144. package/ui/Chat/Chat.tsx +175 -0
  145. package/ui/Chat/Chat.types.ts +39 -0
  146. package/ui/Chat/index.ts +2 -0
  147. package/ui/Checkbox/Checkbox.module.css +135 -0
  148. package/ui/Checkbox/Checkbox.tsx +70 -0
  149. package/ui/Checkbox/Checkbox.types.ts +15 -0
  150. package/ui/Checkbox/index.ts +2 -0
  151. package/ui/Collapsible/Collapsible.module.css +68 -0
  152. package/ui/Collapsible/Collapsible.tsx +59 -0
  153. package/ui/Collapsible/Collapsible.types.ts +9 -0
  154. package/ui/Collapsible/index.ts +2 -0
  155. package/ui/Command/Command.module.css +138 -0
  156. package/ui/Command/Command.tsx +152 -0
  157. package/ui/Command/Command.types.ts +20 -0
  158. package/ui/Command/index.ts +2 -0
  159. package/ui/DataTable/DataTable.module.css +211 -0
  160. package/ui/DataTable/DataTable.tsx +429 -0
  161. package/ui/DataTable/DataTable.types.ts +35 -0
  162. package/ui/DataTable/index.ts +8 -0
  163. package/ui/DatePicker/DatePicker.module.css +44 -0
  164. package/ui/DatePicker/DatePicker.tsx +123 -0
  165. package/ui/DatePicker/DatePicker.types.ts +15 -0
  166. package/ui/DatePicker/index.ts +2 -0
  167. package/ui/Drawer/Drawer.module.css +204 -0
  168. package/ui/Drawer/Drawer.tsx +112 -0
  169. package/ui/Drawer/Drawer.types.ts +14 -0
  170. package/ui/Drawer/index.ts +2 -0
  171. package/ui/DropdownMenu/DropdownMenu.module.css +319 -0
  172. package/ui/DropdownMenu/DropdownMenu.tsx +580 -0
  173. package/ui/DropdownMenu/DropdownMenu.types.ts +48 -0
  174. package/ui/DropdownMenu/index.ts +8 -0
  175. package/ui/Empty/Empty.module.css +68 -0
  176. package/ui/Empty/Empty.tsx +35 -0
  177. package/ui/Empty/Empty.types.ts +10 -0
  178. package/ui/Empty/index.ts +2 -0
  179. package/ui/Field/Field.module.css +66 -0
  180. package/ui/Field/Field.tsx +46 -0
  181. package/ui/Field/Field.types.ts +12 -0
  182. package/ui/Field/index.ts +2 -0
  183. package/ui/FieldSet/FieldSet.module.css +30 -0
  184. package/ui/FieldSet/FieldSet.tsx +14 -0
  185. package/ui/FieldSet/FieldSet.types.ts +7 -0
  186. package/ui/FieldSet/index.ts +2 -0
  187. package/ui/HeaderCell/HeaderCell.module.css +85 -0
  188. package/ui/HeaderCell/HeaderCell.tsx +71 -0
  189. package/ui/HeaderCell/HeaderCell.types.ts +18 -0
  190. package/ui/HeaderCell/index.ts +2 -0
  191. package/ui/Input/Input.module.css +59 -0
  192. package/ui/Input/Input.tsx +65 -0
  193. package/ui/Input/Input.types.ts +22 -0
  194. package/ui/Input/index.ts +2 -0
  195. package/ui/InputOTP/InputOTP.module.css +139 -0
  196. package/ui/InputOTP/InputOTP.tsx +136 -0
  197. package/ui/InputOTP/InputOTP.types.ts +19 -0
  198. package/ui/InputOTP/index.ts +2 -0
  199. package/ui/InsightCard/InsightCard.module.css +166 -0
  200. package/ui/InsightCard/InsightCard.tsx +90 -0
  201. package/ui/InsightCard/InsightCard.types.ts +20 -0
  202. package/ui/InsightCard/index.ts +7 -0
  203. package/ui/LineChart/LineChart.module.css +93 -0
  204. package/ui/LineChart/LineChart.tsx +424 -0
  205. package/ui/LineChart/LineChart.types.ts +19 -0
  206. package/ui/LineChart/index.ts +2 -0
  207. package/ui/LoadingAnimation/LoadingAnimation.module.css +66 -0
  208. package/ui/LoadingAnimation/LoadingAnimation.tsx +41 -0
  209. package/ui/LoadingAnimation/LoadingAnimation.types.ts +9 -0
  210. package/ui/LoadingAnimation/index.ts +6 -0
  211. package/ui/Modal/Modal.module.css +48 -0
  212. package/ui/Modal/Modal.tsx +114 -0
  213. package/ui/Modal/Modal.types.ts +15 -0
  214. package/ui/Modal/index.ts +2 -0
  215. package/ui/ModalCard/ModalCard.module.css +137 -0
  216. package/ui/ModalCard/ModalCard.tsx +58 -0
  217. package/ui/ModalCard/ModalCard.types.ts +15 -0
  218. package/ui/ModalCard/index.ts +2 -0
  219. package/ui/NavigationMenu/NavigationMenu.module.css +31 -0
  220. package/ui/NavigationMenu/NavigationMenu.tsx +57 -0
  221. package/ui/NavigationMenu/NavigationMenu.types.ts +22 -0
  222. package/ui/NavigationMenu/index.ts +7 -0
  223. package/ui/NumberTransition/NumberTransition.module.css +110 -0
  224. package/ui/NumberTransition/NumberTransition.tsx +78 -0
  225. package/ui/NumberTransition/NumberTransition.types.ts +9 -0
  226. package/ui/NumberTransition/index.ts +2 -0
  227. package/ui/PageHeader/PageHeader.module.css +57 -0
  228. package/ui/PageHeader/PageHeader.tsx +39 -0
  229. package/ui/PageHeader/PageHeader.types.ts +12 -0
  230. package/ui/PageHeader/index.ts +2 -0
  231. package/ui/Pagination/Pagination.module.css +19 -0
  232. package/ui/Pagination/Pagination.tsx +147 -0
  233. package/ui/Pagination/Pagination.types.ts +12 -0
  234. package/ui/Pagination/index.ts +2 -0
  235. package/ui/PieChart/PieChart.module.css +116 -0
  236. package/ui/PieChart/PieChart.tsx +293 -0
  237. package/ui/PieChart/PieChart.types.ts +22 -0
  238. package/ui/PieChart/index.ts +2 -0
  239. package/ui/Progress/Progress.module.css +226 -0
  240. package/ui/Progress/Progress.tsx +167 -0
  241. package/ui/Progress/Progress.types.ts +15 -0
  242. package/ui/Progress/index.ts +8 -0
  243. package/ui/ProgressSteps/ProgressSteps.module.css +210 -0
  244. package/ui/ProgressSteps/ProgressSteps.tsx +105 -0
  245. package/ui/ProgressSteps/ProgressSteps.types.ts +19 -0
  246. package/ui/ProgressSteps/index.ts +8 -0
  247. package/ui/RadioGroup/RadioGroup.module.css +284 -0
  248. package/ui/RadioGroup/RadioGroup.tsx +116 -0
  249. package/ui/RadioGroup/RadioGroup.types.ts +30 -0
  250. package/ui/RadioGroup/index.ts +8 -0
  251. package/ui/Scoreboard/Scoreboard.module.css +53 -0
  252. package/ui/Scoreboard/Scoreboard.tsx +41 -0
  253. package/ui/Scoreboard/Scoreboard.types.ts +7 -0
  254. package/ui/Scoreboard/index.ts +2 -0
  255. package/ui/Scorecard/Scorecard.module.css +150 -0
  256. package/ui/Scorecard/Scorecard.tsx +92 -0
  257. package/ui/Scorecard/Scorecard.types.ts +18 -0
  258. package/ui/Scorecard/index.ts +2 -0
  259. package/ui/Section/Section.module.css +104 -0
  260. package/ui/Section/Section.tsx +75 -0
  261. package/ui/Section/Section.types.ts +15 -0
  262. package/ui/Section/index.ts +2 -0
  263. package/ui/Select/Select.module.css +7 -0
  264. package/ui/Select/Select.tsx +113 -0
  265. package/ui/Select/Select.types.ts +26 -0
  266. package/ui/Select/index.ts +2 -0
  267. package/ui/ShimmerText/ShimmerText.module.css +46 -0
  268. package/ui/ShimmerText/ShimmerText.tsx +48 -0
  269. package/ui/ShimmerText/ShimmerText.types.ts +12 -0
  270. package/ui/ShimmerText/index.ts +2 -0
  271. package/ui/SideNav/SideNav.module.css +347 -0
  272. package/ui/SideNav/SideNav.tsx +222 -0
  273. package/ui/SideNav/SideNav.types.ts +42 -0
  274. package/ui/SideNav/index.ts +8 -0
  275. package/ui/Slider/Slider.module.css +105 -0
  276. package/ui/Slider/Slider.tsx +187 -0
  277. package/ui/Slider/Slider.types.ts +11 -0
  278. package/ui/Slider/index.ts +2 -0
  279. package/ui/Spinner/Spinner.module.css +58 -0
  280. package/ui/Spinner/Spinner.tsx +19 -0
  281. package/ui/Spinner/Spinner.types.ts +6 -0
  282. package/ui/Spinner/index.ts +2 -0
  283. package/ui/Switch/Switch.module.css +189 -0
  284. package/ui/Switch/Switch.tsx +65 -0
  285. package/ui/Switch/Switch.types.ts +12 -0
  286. package/ui/Switch/index.ts +2 -0
  287. package/ui/Table/Table.module.css +132 -0
  288. package/ui/Table/Table.tsx +108 -0
  289. package/ui/Table/Table.types.ts +26 -0
  290. package/ui/Table/index.ts +8 -0
  291. package/ui/Tabs/Tabs.module.css +162 -0
  292. package/ui/Tabs/Tabs.tsx +150 -0
  293. package/ui/Tabs/Tabs.types.ts +21 -0
  294. package/ui/Tabs/index.ts +2 -0
  295. package/ui/Textarea/Textarea.module.css +74 -0
  296. package/ui/Textarea/Textarea.tsx +38 -0
  297. package/ui/Textarea/Textarea.types.ts +14 -0
  298. package/ui/Textarea/index.ts +2 -0
  299. package/ui/ThinkingAnimation/ThinkingAnimation.module.css +75 -0
  300. package/ui/ThinkingAnimation/ThinkingAnimation.tsx +31 -0
  301. package/ui/ThinkingAnimation/ThinkingAnimation.types.ts +8 -0
  302. package/ui/ThinkingAnimation/index.ts +2 -0
  303. package/ui/Timeline/Timeline.module.css +99 -0
  304. package/ui/Timeline/Timeline.tsx +47 -0
  305. package/ui/Timeline/Timeline.types.ts +16 -0
  306. package/ui/Timeline/index.ts +2 -0
  307. package/ui/Toast/Toast.module.css +133 -0
  308. package/ui/Toast/Toast.tsx +89 -0
  309. package/ui/Toast/Toast.types.ts +17 -0
  310. package/ui/Toast/index.ts +2 -0
  311. package/ui/Tooltip/Tooltip.module.css +111 -0
  312. package/ui/Tooltip/Tooltip.tsx +101 -0
  313. package/ui/Tooltip/index.ts +2 -0
  314. package/ui/patterns/ActivityPattern/ActivityFeed.tsx +77 -0
  315. package/ui/patterns/ActivityPattern/ActivityPattern.tsx +120 -0
  316. package/ui/patterns/ActivityPattern/ActivityTasks.tsx +86 -0
  317. package/ui/patterns/ActivityPattern/TaskDrawer.module.css +39 -0
  318. package/ui/patterns/ActivityPattern/TaskDrawer.tsx +186 -0
  319. package/ui/patterns/ActivityPattern/index.ts +2 -0
  320. package/ui/patterns/AssistantWorkspacePattern/AssistantWorkspacePattern.module.css +16 -0
  321. package/ui/patterns/AssistantWorkspacePattern/AssistantWorkspacePattern.tsx +159 -0
  322. package/ui/patterns/AssistantWorkspacePattern/index.ts +2 -0
  323. package/ui/patterns/DashboardPattern/DashboardPattern.tsx +102 -0
  324. package/ui/patterns/DashboardPattern/index.ts +1 -0
  325. package/ui/patterns/DashboardPattern/patterns.module.css +6 -0
  326. package/ui/patterns/EmptyFirstRunPattern/EmptyFirstRunPattern.tsx +125 -0
  327. package/ui/patterns/EmptyFirstRunPattern/index.ts +2 -0
  328. package/ui/patterns/InboxPattern/InboxPattern.module.css +40 -0
  329. package/ui/patterns/InboxPattern/InboxPattern.tsx +192 -0
  330. package/ui/patterns/InboxPattern/InboxReplyDrawer.tsx +97 -0
  331. package/ui/patterns/InboxPattern/inbox-data.ts +101 -0
  332. package/ui/patterns/InboxPattern/index.ts +2 -0
  333. package/ui/patterns/InviteMembersPattern/InviteMembersPattern.tsx +148 -0
  334. package/ui/patterns/InviteMembersPattern/index.ts +2 -0
  335. package/ui/patterns/ListDetailPattern/ListDetailPattern.module.css +34 -0
  336. package/ui/patterns/ListDetailPattern/ListDetailPattern.tsx +145 -0
  337. package/ui/patterns/ListDetailPattern/index.ts +2 -0
  338. package/ui/patterns/SettingsFormPattern/SettingsFormPattern.module.css +12 -0
  339. package/ui/patterns/SettingsFormPattern/SettingsFormPattern.tsx +89 -0
  340. package/ui/patterns/SettingsFormPattern/index.ts +3 -0
  341. package/ui/shared/ChartFrame.module.css +295 -0
  342. package/ui/shared/ChartFrame.tsx +87 -0
  343. package/ui/shared/chartMath.ts +228 -0
  344. package/ui/shared/controls.module.css +48 -0
  345. package/ui/shared/useChartSize.ts +22 -0
@@ -0,0 +1,105 @@
1
+ .root {
2
+ display: flex;
3
+ width: 100%;
4
+ min-width: 0;
5
+ }
6
+
7
+ .horizontal {
8
+ height: 1.25rem;
9
+ align-items: center;
10
+ }
11
+
12
+ .vertical {
13
+ width: 1.25rem;
14
+ height: 8rem;
15
+ justify-content: center;
16
+ }
17
+
18
+ .track {
19
+ position: relative;
20
+ flex: 1;
21
+ border-radius: var(--radius-pill);
22
+ background: var(--fill-subtle);
23
+ touch-action: none;
24
+ }
25
+
26
+ .horizontal .track {
27
+ height: var(--space-2);
28
+ }
29
+
30
+ .vertical .track {
31
+ width: var(--space-2);
32
+ height: 100%;
33
+ }
34
+
35
+ .fill {
36
+ position: absolute;
37
+ border-radius: inherit;
38
+ background: var(--fill-brand);
39
+ }
40
+
41
+ .horizontal .fill {
42
+ top: 0;
43
+ bottom: 0;
44
+ }
45
+
46
+ .vertical .fill {
47
+ left: 0;
48
+ right: 0;
49
+ }
50
+
51
+ .thumb {
52
+ position: absolute;
53
+ box-sizing: border-box;
54
+ width: 1rem;
55
+ height: 1rem;
56
+ margin: 0;
57
+ padding: 0;
58
+ border: var(--border-control) solid var(--border-faint);
59
+ border-radius: var(--radius-full);
60
+ background: var(--action-on-primary);
61
+ box-shadow: 0 0 0 1px var(--surface-card);
62
+ cursor: grab;
63
+ touch-action: none;
64
+ transition:
65
+ border-color var(--motion-interaction-duration) var(--motion-interaction-easing),
66
+ outline-color var(--motion-interaction-duration) var(--motion-interaction-easing);
67
+ }
68
+
69
+ .horizontal .thumb {
70
+ top: 50%;
71
+ transform: translate(-50%, -50%);
72
+ }
73
+
74
+ .vertical .thumb {
75
+ left: 50%;
76
+ transform: translate(-50%, 50%);
77
+ }
78
+
79
+ .thumb:hover:not(:disabled) {
80
+ border-color: var(--border-strong);
81
+ }
82
+
83
+ .thumb:focus-visible {
84
+ outline: var(--border-focus-width) solid var(--focus-ring);
85
+ outline-offset: var(--space-inset-2xs);
86
+ }
87
+
88
+ .thumb:active {
89
+ cursor: grabbing;
90
+ }
91
+
92
+ .disabled {
93
+ opacity: var(--opacity-disabled);
94
+ cursor: not-allowed;
95
+ }
96
+
97
+ .disabled .thumb {
98
+ cursor: not-allowed;
99
+ }
100
+
101
+ @media (prefers-reduced-motion: reduce) {
102
+ .thumb {
103
+ transition: none;
104
+ }
105
+ }
@@ -0,0 +1,187 @@
1
+ "use client";
2
+
3
+ import { useCallback, useRef, type KeyboardEvent, type PointerEvent } from "react";
4
+ import type { SliderProps } from "./Slider.types";
5
+ import styles from "./Slider.module.css";
6
+
7
+ export type { SliderProps, SliderOrientation } from "./Slider.types";
8
+
9
+ function clampValue(value: number, min: number, max: number, step: number): number {
10
+ if (!Number.isFinite(value)) return min;
11
+ const span = max - min;
12
+ if (span <= 0) return min;
13
+ const steps = Math.round((value - min) / step);
14
+ const snapped = min + steps * step;
15
+ return Math.min(max, Math.max(min, snapped));
16
+ }
17
+
18
+ function toPercent(value: number, min: number, max: number): number {
19
+ if (max <= min) return 0;
20
+ return ((value - min) / (max - min)) * 100;
21
+ }
22
+
23
+ function nearestThumbIndex(values: number[], target: number): number {
24
+ if (values.length <= 1) return 0;
25
+ let best = 0;
26
+ let bestDist = Math.abs(values[0] - target);
27
+ for (let i = 1; i < values.length; i += 1) {
28
+ const dist = Math.abs(values[i] - target);
29
+ if (dist < bestDist) {
30
+ best = i;
31
+ bestDist = dist;
32
+ }
33
+ }
34
+ return best;
35
+ }
36
+
37
+ export function Slider({
38
+ value,
39
+ min = 0,
40
+ max = 100,
41
+ step = 1,
42
+ orientation = "horizontal",
43
+ disabled = false,
44
+ onValueChange,
45
+ }: SliderProps) {
46
+ const trackRef = useRef<HTMLDivElement>(null);
47
+ const values = Array.isArray(value) ? value : [value];
48
+ const isVertical = orientation === "vertical";
49
+ const isRange = Array.isArray(value);
50
+
51
+ const commit = useCallback(
52
+ (index: number, next: number) => {
53
+ if (disabled || !onValueChange) return;
54
+ const clamped = clampValue(next, min, max, step);
55
+ if (isRange) {
56
+ const copy = [...(value as number[])];
57
+ copy[index] = clamped;
58
+ if (copy.length === 2) {
59
+ if (index === 0 && copy[0] > copy[1]) copy[0] = copy[1];
60
+ if (index === 1 && copy[1] < copy[0]) copy[1] = copy[0];
61
+ }
62
+ onValueChange(copy);
63
+ } else {
64
+ onValueChange(clamped);
65
+ }
66
+ },
67
+ [disabled, isRange, max, min, onValueChange, step, value],
68
+ );
69
+
70
+ const pointerToValue = useCallback(
71
+ (clientX: number, clientY: number) => {
72
+ const track = trackRef.current;
73
+ if (!track) return min;
74
+ const rect = track.getBoundingClientRect();
75
+ const ratio = isVertical
76
+ ? 1 - (clientY - rect.top) / rect.height
77
+ : (clientX - rect.left) / rect.width;
78
+ const raw = min + Math.min(1, Math.max(0, ratio)) * (max - min);
79
+ return clampValue(raw, min, max, step);
80
+ },
81
+ [isVertical, min, max, step],
82
+ );
83
+
84
+ const onTrackPointerDown = (event: PointerEvent<HTMLDivElement>) => {
85
+ if (disabled) return;
86
+ const target = pointerToValue(event.clientX, event.clientY);
87
+ const index = nearestThumbIndex(values, target);
88
+ commit(index, target);
89
+ event.currentTarget.setPointerCapture(event.pointerId);
90
+ };
91
+
92
+ const onTrackPointerMove = (event: PointerEvent<HTMLDivElement>) => {
93
+ if (!event.currentTarget.hasPointerCapture(event.pointerId) || disabled) return;
94
+ const target = pointerToValue(event.clientX, event.clientY);
95
+ const index = nearestThumbIndex(values, target);
96
+ commit(index, target);
97
+ };
98
+
99
+ const onThumbKeyDown = (index: number, event: KeyboardEvent<HTMLSpanElement>) => {
100
+ if (disabled) return;
101
+ const current = values[index];
102
+ let next = current;
103
+ const big = step * 10;
104
+
105
+ switch (event.key) {
106
+ case "ArrowRight":
107
+ case "ArrowUp":
108
+ next = current + step;
109
+ break;
110
+ case "ArrowLeft":
111
+ case "ArrowDown":
112
+ next = current - step;
113
+ break;
114
+ case "PageUp":
115
+ next = current + big;
116
+ break;
117
+ case "PageDown":
118
+ next = current - big;
119
+ break;
120
+ case "Home":
121
+ next = min;
122
+ break;
123
+ case "End":
124
+ next = max;
125
+ break;
126
+ default:
127
+ return;
128
+ }
129
+
130
+ event.preventDefault();
131
+ commit(index, next);
132
+ };
133
+
134
+ const sorted = [...values].sort((a, b) => a - b);
135
+ const fillStart = toPercent(sorted[0], min, max);
136
+ const fillEnd = toPercent(sorted[sorted.length - 1], min, max);
137
+
138
+ const fillStyle = isVertical
139
+ ? { bottom: `${fillStart}%`, height: `${fillEnd - fillStart}%` }
140
+ : { left: `${fillStart}%`, width: `${fillEnd - fillStart}%` };
141
+
142
+ return (
143
+ <div
144
+ className={`${styles.root} ${styles[orientation]}${disabled ? ` ${styles.disabled}` : ""}`}
145
+ >
146
+ <div
147
+ ref={trackRef}
148
+ className={styles.track}
149
+ onPointerDown={onTrackPointerDown}
150
+ onPointerMove={onTrackPointerMove}
151
+ >
152
+ <div className={styles.fill} style={fillStyle} aria-hidden />
153
+ {values.map((thumbValue, index) => {
154
+ const percent = toPercent(thumbValue, min, max);
155
+ const thumbStyle = isVertical
156
+ ? { bottom: `${percent}%` }
157
+ : { left: `${percent}%` };
158
+
159
+ return (
160
+ <span
161
+ key={index}
162
+ role="slider"
163
+ className={styles.thumb}
164
+ tabIndex={disabled ? -1 : 0}
165
+ aria-valuemin={min}
166
+ aria-valuemax={max}
167
+ aria-valuenow={thumbValue}
168
+ aria-orientation={orientation}
169
+ aria-disabled={disabled || undefined}
170
+ style={thumbStyle}
171
+ onKeyDown={(event) => onThumbKeyDown(index, event)}
172
+ onPointerDown={(event) => {
173
+ event.stopPropagation();
174
+ if (disabled) return;
175
+ event.currentTarget.setPointerCapture(event.pointerId);
176
+ }}
177
+ onPointerMove={(event) => {
178
+ if (!event.currentTarget.hasPointerCapture(event.pointerId) || disabled) return;
179
+ commit(index, pointerToValue(event.clientX, event.clientY));
180
+ }}
181
+ />
182
+ );
183
+ })}
184
+ </div>
185
+ </div>
186
+ );
187
+ }
@@ -0,0 +1,11 @@
1
+ export type SliderOrientation = "horizontal" | "vertical";
2
+
3
+ export interface SliderProps {
4
+ value: number | number[];
5
+ min?: number;
6
+ max?: number;
7
+ step?: number;
8
+ orientation?: SliderOrientation;
9
+ disabled?: boolean;
10
+ onValueChange?: (value: number | number[]) => void;
11
+ }
@@ -0,0 +1,2 @@
1
+ export { Slider } from "./Slider";
2
+ export type { SliderProps, SliderOrientation } from "./Slider.types";
@@ -0,0 +1,58 @@
1
+ .root {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ gap: var(--space-2);
5
+ font-family: var(--font-sans);
6
+ color: var(--icon-secondary);
7
+ }
8
+
9
+ .icon {
10
+ display: block;
11
+ flex-shrink: 0;
12
+ animation: spinner-spin calc(var(--motion-duration-600) * 4) var(--motion-ease-linear) infinite;
13
+ }
14
+
15
+ .sm .icon {
16
+ width: 0.875rem;
17
+ height: 0.875rem;
18
+ }
19
+
20
+ .md .icon {
21
+ width: 1rem;
22
+ height: 1rem;
23
+ }
24
+
25
+ .lg .icon {
26
+ width: 1.25rem;
27
+ height: 1.25rem;
28
+ }
29
+
30
+ .label {
31
+ font-size: var(--type-size-body-sm);
32
+ line-height: var(--type-line-body);
33
+ color: var(--text-secondary);
34
+ }
35
+
36
+ .sm .label {
37
+ font-size: var(--type-size-caption);
38
+ }
39
+
40
+ .lg .label {
41
+ font-size: var(--type-size-label);
42
+ }
43
+
44
+ @keyframes spinner-spin {
45
+ from {
46
+ transform: rotate(0deg);
47
+ }
48
+
49
+ to {
50
+ transform: rotate(360deg);
51
+ }
52
+ }
53
+
54
+ @media (prefers-reduced-motion: reduce) {
55
+ .icon {
56
+ animation: none;
57
+ }
58
+ }
@@ -0,0 +1,19 @@
1
+ import { Loader } from "lucide-react";
2
+ import type { SpinnerProps } from "./Spinner.types";
3
+ import styles from "./Spinner.module.css";
4
+
5
+ export type { SpinnerProps, SpinnerSize } from "./Spinner.types";
6
+
7
+ export function Spinner({ size = "md", label }: SpinnerProps) {
8
+ return (
9
+ <span
10
+ className={`${styles.root} ${styles[size]}`}
11
+ role="status"
12
+ aria-live="polite"
13
+ aria-label={label ? undefined : "Loading"}
14
+ >
15
+ <Loader className={styles.icon} strokeWidth={2} aria-hidden />
16
+ {label ? <span className={styles.label}>{label}</span> : null}
17
+ </span>
18
+ );
19
+ }
@@ -0,0 +1,6 @@
1
+ export type SpinnerSize = "sm" | "md" | "lg";
2
+
3
+ export interface SpinnerProps {
4
+ size?: SpinnerSize;
5
+ label?: string;
6
+ }
@@ -0,0 +1,2 @@
1
+ export { Spinner } from "./Spinner";
2
+ export type { SpinnerProps, SpinnerSize } from "./Spinner.types";
@@ -0,0 +1,189 @@
1
+ .switch {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ width: fit-content;
5
+ font-family: var(--font-sans);
6
+ color: var(--text-primary);
7
+ }
8
+
9
+ .labeled {
10
+ display: flex;
11
+ width: 100%;
12
+ justify-content: space-between;
13
+ gap: var(--space-inline-md);
14
+ }
15
+
16
+ .label {
17
+ font-weight: var(--type-weight-label);
18
+ line-height: var(--type-line-body);
19
+ cursor: pointer;
20
+ }
21
+
22
+ .switch[data-disabled] {
23
+ cursor: not-allowed;
24
+ }
25
+
26
+ .switch[data-disabled] .label {
27
+ color: var(--text-disabled);
28
+ cursor: not-allowed;
29
+ }
30
+
31
+ .sm {
32
+ font-size: var(--type-size-caption);
33
+ }
34
+
35
+ .md {
36
+ font-size: var(--type-size-label);
37
+ }
38
+
39
+ .lg {
40
+ font-size: var(--type-size-body-sm);
41
+ }
42
+
43
+ .control {
44
+ appearance: none;
45
+ -webkit-appearance: none;
46
+ position: relative;
47
+ box-sizing: border-box;
48
+ flex-shrink: 0;
49
+ margin: 0;
50
+ padding: 0;
51
+ width: 2.5rem;
52
+ height: 1.4rem;
53
+ border: var(--border-control) solid var(--border-faint);
54
+ border-radius: var(--radius-pill);
55
+ background: var(--action-secondary);
56
+ cursor: pointer;
57
+ --switch-thumb-inset: 0.12rem;
58
+ --switch-thumb-size: 1rem;
59
+ --switch-thumb-stretch: 1.18rem;
60
+ --switch-thumb-squash: 0.92rem;
61
+ }
62
+
63
+ .sm .control {
64
+ width: 2rem;
65
+ height: 1.15rem;
66
+ --switch-thumb-inset: 0.1rem;
67
+ --switch-thumb-size: 0.85rem;
68
+ --switch-thumb-stretch: 0.98rem;
69
+ --switch-thumb-squash: 0.78rem;
70
+ }
71
+
72
+ .lg .control {
73
+ width: 3rem;
74
+ height: 1.65rem;
75
+ --switch-thumb-inset: 0.14rem;
76
+ --switch-thumb-size: 1.2rem;
77
+ --switch-thumb-stretch: 1.38rem;
78
+ --switch-thumb-squash: 1.1rem;
79
+ }
80
+
81
+ .control[aria-checked="true"] {
82
+ background: var(--action-primary);
83
+ border-color: var(--action-primary);
84
+ }
85
+
86
+ .control:hover:not(:disabled) {
87
+ border-color: var(--border-strong);
88
+ }
89
+
90
+ .control[aria-checked="true"]:hover:not(:disabled) {
91
+ background: var(--action-primary-hover);
92
+ border-color: var(--action-primary-hover);
93
+ }
94
+
95
+ .control:focus-visible {
96
+ outline: var(--border-focus-width) solid var(--focus-ring);
97
+ outline-offset: var(--space-inset-2xs);
98
+ }
99
+
100
+ .control:disabled {
101
+ opacity: var(--opacity-disabled);
102
+ cursor: not-allowed;
103
+ }
104
+
105
+ .thumb {
106
+ position: absolute;
107
+ top: 50%;
108
+ left: var(--switch-thumb-inset);
109
+ width: var(--switch-thumb-size);
110
+ height: var(--switch-thumb-size);
111
+ border-radius: var(--radius-pill);
112
+ background: var(--action-on-primary);
113
+ transform: translateY(-50%);
114
+ pointer-events: none;
115
+ transition:
116
+ left var(--motion-modal-duration) var(--motion-ease-spring),
117
+ width var(--motion-modal-duration) var(--motion-ease-spring);
118
+ }
119
+
120
+ .control[aria-checked="true"] .thumb {
121
+ left: calc(100% - var(--switch-thumb-size) - var(--switch-thumb-inset));
122
+ }
123
+
124
+ @keyframes switch-thumb-on {
125
+ 0% {
126
+ left: var(--switch-thumb-inset);
127
+ width: var(--switch-thumb-size);
128
+ height: var(--switch-thumb-size);
129
+ }
130
+ 30% {
131
+ left: var(--switch-thumb-inset);
132
+ width: var(--switch-thumb-stretch);
133
+ height: var(--switch-thumb-squash);
134
+ }
135
+ 70% {
136
+ left: calc(100% - var(--switch-thumb-stretch) - var(--switch-thumb-inset));
137
+ width: var(--switch-thumb-stretch);
138
+ height: var(--switch-thumb-squash);
139
+ }
140
+ 100% {
141
+ left: calc(100% - var(--switch-thumb-size) - var(--switch-thumb-inset));
142
+ width: var(--switch-thumb-size);
143
+ height: var(--switch-thumb-size);
144
+ }
145
+ }
146
+
147
+ @keyframes switch-thumb-off {
148
+ 0% {
149
+ left: calc(100% - var(--switch-thumb-size) - var(--switch-thumb-inset));
150
+ width: var(--switch-thumb-size);
151
+ height: var(--switch-thumb-size);
152
+ }
153
+ 30% {
154
+ left: calc(100% - var(--switch-thumb-stretch) - var(--switch-thumb-inset));
155
+ width: var(--switch-thumb-stretch);
156
+ height: var(--switch-thumb-squash);
157
+ }
158
+ 70% {
159
+ left: var(--switch-thumb-inset);
160
+ width: var(--switch-thumb-stretch);
161
+ height: var(--switch-thumb-squash);
162
+ }
163
+ 100% {
164
+ left: var(--switch-thumb-inset);
165
+ width: var(--switch-thumb-size);
166
+ height: var(--switch-thumb-size);
167
+ }
168
+ }
169
+
170
+ .control[data-jelly][aria-checked="true"] .thumb {
171
+ animation: switch-thumb-on var(--motion-modal-duration) var(--motion-ease-spring) both;
172
+ transition: none;
173
+ }
174
+
175
+ .control[data-jelly][aria-checked="false"] .thumb {
176
+ animation: switch-thumb-off var(--motion-modal-duration) var(--motion-ease-spring) both;
177
+ transition: none;
178
+ }
179
+
180
+ @media (prefers-reduced-motion: reduce) {
181
+ .thumb {
182
+ transition: none;
183
+ }
184
+
185
+ .control[data-jelly][aria-checked="true"] .thumb,
186
+ .control[data-jelly][aria-checked="false"] .thumb {
187
+ animation: none;
188
+ }
189
+ }
@@ -0,0 +1,65 @@
1
+ "use client";
2
+
3
+ import { useId, useState } from "react";
4
+ import type { SwitchProps } from "./Switch.types";
5
+ import styles from "./Switch.module.css";
6
+
7
+ export type { SwitchProps, SwitchSize } from "./Switch.types";
8
+
9
+ export function Switch({
10
+ id,
11
+ label,
12
+ ariaLabel,
13
+ size = "md",
14
+ disabled = false,
15
+ checked,
16
+ defaultChecked = false,
17
+ onChange,
18
+ }: SwitchProps) {
19
+ const generatedId = useId();
20
+ const switchId = id ?? generatedId;
21
+ const isControlled = typeof checked === "boolean";
22
+ const [uncontrolled, setUncontrolled] = useState(defaultChecked);
23
+ const isChecked = isControlled ? checked : uncontrolled;
24
+ const [jelly, setJelly] = useState(false);
25
+
26
+ function toggle() {
27
+ if (disabled) return;
28
+ const next = !isChecked;
29
+ if (!isControlled) setUncontrolled(next);
30
+ onChange?.(next);
31
+ if (jelly) {
32
+ setJelly(false);
33
+ requestAnimationFrame(() => setJelly(true));
34
+ return;
35
+ }
36
+ setJelly(true);
37
+ }
38
+
39
+ return (
40
+ <div
41
+ className={`${styles.switch} ${styles[size]}${label ? ` ${styles.labeled}` : ""}`}
42
+ data-disabled={disabled || undefined}
43
+ >
44
+ {label ? (
45
+ <label className={styles.label} htmlFor={switchId}>
46
+ {label}
47
+ </label>
48
+ ) : null}
49
+ <button
50
+ type="button"
51
+ role="switch"
52
+ id={switchId}
53
+ className={styles.control}
54
+ aria-checked={isChecked}
55
+ aria-label={label ? undefined : ariaLabel}
56
+ disabled={disabled}
57
+ data-jelly={jelly || undefined}
58
+ onClick={toggle}
59
+ onAnimationEnd={() => setJelly(false)}
60
+ >
61
+ <span className={styles.thumb} aria-hidden />
62
+ </button>
63
+ </div>
64
+ );
65
+ }
@@ -0,0 +1,12 @@
1
+ export type SwitchSize = "sm" | "md" | "lg";
2
+
3
+ export interface SwitchProps {
4
+ id?: string;
5
+ label?: string;
6
+ ariaLabel?: string;
7
+ size?: SwitchSize;
8
+ disabled?: boolean;
9
+ checked?: boolean;
10
+ defaultChecked?: boolean;
11
+ onChange?: (checked: boolean) => void;
12
+ }
@@ -0,0 +1,2 @@
1
+ export { Switch } from "./Switch";
2
+ export type { SwitchProps, SwitchSize } from "./Switch.types";