@nqmcreative/ui 0.1.0 → 0.2.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 (308) hide show
  1. package/README.md +151 -439
  2. package/cli/index.mjs +67 -30
  3. package/dist/core/calendar.d.ts +3 -0
  4. package/dist/core/calendar.js +36 -0
  5. package/dist/core/dialog.svelte.d.ts +14 -0
  6. package/dist/core/dialog.svelte.js +23 -0
  7. package/dist/core/files.d.ts +35 -0
  8. package/dist/core/files.js +62 -0
  9. package/dist/core/index.d.ts +37 -0
  10. package/dist/core/index.js +35 -0
  11. package/dist/core/list.svelte.d.ts +41 -0
  12. package/dist/core/list.svelte.js +77 -0
  13. package/dist/core/number.d.ts +7 -0
  14. package/dist/core/number.js +17 -0
  15. package/dist/core/pagination.d.ts +6 -0
  16. package/dist/core/pagination.js +21 -0
  17. package/dist/core/password.d.ts +8 -0
  18. package/dist/core/password.js +23 -0
  19. package/dist/core/table.d.ts +22 -0
  20. package/dist/core/table.js +41 -0
  21. package/dist/core/theme.svelte.d.ts +16 -0
  22. package/dist/core/theme.svelte.js +34 -0
  23. package/dist/core/trigger.d.ts +35 -0
  24. package/dist/core/trigger.js +77 -0
  25. package/dist/index.d.ts +11 -103
  26. package/dist/index.js +11 -75
  27. package/dist/{components → styles/matte}/AccordionItem.svelte +1 -1
  28. package/dist/{components → styles/matte}/AccordionItem.svelte.d.ts +1 -1
  29. package/dist/{components → styles/matte}/Alert.svelte +8 -2
  30. package/dist/{components → styles/matte}/Alert.svelte.d.ts +1 -1
  31. package/dist/{components → styles/matte}/Avatar.svelte +1 -1
  32. package/dist/{components → styles/matte}/Avatar.svelte.d.ts +1 -1
  33. package/dist/{components → styles/matte}/Badge.svelte +8 -3
  34. package/dist/{components → styles/matte}/Badge.svelte.d.ts +1 -3
  35. package/dist/{components → styles/matte}/Breadcrumb.svelte +2 -2
  36. package/dist/{components → styles/matte}/Breadcrumb.svelte.d.ts +1 -1
  37. package/dist/{components → styles/matte}/Button.svelte +1 -1
  38. package/dist/{components → styles/matte}/Button.svelte.d.ts +1 -1
  39. package/dist/styles/matte/CTASection.svelte +76 -0
  40. package/dist/styles/matte/CTASection.svelte.d.ts +18 -0
  41. package/dist/{components → styles/matte}/Calendar.svelte +7 -17
  42. package/dist/{components → styles/matte}/Calendar.svelte.d.ts +1 -1
  43. package/dist/{components → styles/matte}/Card.svelte +1 -1
  44. package/dist/{components → styles/matte}/Card.svelte.d.ts +1 -1
  45. package/dist/{components → styles/matte}/Checkbox.svelte +1 -1
  46. package/dist/{components → styles/matte}/Checkbox.svelte.d.ts +1 -1
  47. package/dist/{components → styles/matte}/CheckboxGroup.svelte +1 -1
  48. package/dist/{components → styles/matte}/CheckboxGroup.svelte.d.ts +1 -1
  49. package/dist/{components → styles/matte}/Combobox.svelte +31 -42
  50. package/dist/{components → styles/matte}/Combobox.svelte.d.ts +1 -1
  51. package/dist/{components → styles/matte}/CommandPalette.svelte +26 -41
  52. package/dist/{components → styles/matte}/CommandPalette.svelte.d.ts +1 -1
  53. package/dist/{components → styles/matte}/ConfirmDialog.svelte +2 -2
  54. package/dist/{components → styles/matte}/ConfirmDialog.svelte.d.ts +2 -2
  55. package/dist/{components → styles/matte}/ContextMenu.svelte +5 -7
  56. package/dist/{components → styles/matte}/DatePicker.svelte +7 -11
  57. package/dist/{components → styles/matte}/DatePicker.svelte.d.ts +1 -1
  58. package/dist/{components → styles/matte}/Drawer.svelte +4 -7
  59. package/dist/{components → styles/matte}/Dropdown.svelte +18 -36
  60. package/dist/{components → styles/matte}/Dropdown.svelte.d.ts +1 -1
  61. package/dist/{components → styles/matte}/Dropzone.svelte +11 -43
  62. package/dist/{components → styles/matte}/Dropzone.svelte.d.ts +2 -5
  63. package/dist/{components → styles/matte}/EmptyState.svelte +1 -1
  64. package/dist/{components → styles/matte}/EmptyState.svelte.d.ts +1 -1
  65. package/dist/styles/matte/FeatureCard.svelte +58 -0
  66. package/dist/styles/matte/FeatureCard.svelte.d.ts +16 -0
  67. package/dist/styles/matte/FeatureGrid.svelte +62 -0
  68. package/dist/styles/matte/FeatureGrid.svelte.d.ts +16 -0
  69. package/dist/{components → styles/matte}/Footer.svelte +1 -1
  70. package/dist/{components → styles/matte}/Footer.svelte.d.ts +1 -1
  71. package/dist/styles/matte/HeroSection.svelte +80 -0
  72. package/dist/styles/matte/HeroSection.svelte.d.ts +21 -0
  73. package/dist/{components → styles/matte}/Input.svelte +1 -1
  74. package/dist/{components → styles/matte}/Input.svelte.d.ts +1 -1
  75. package/dist/{components → styles/matte}/Link.svelte +1 -1
  76. package/dist/{components → styles/matte}/Link.svelte.d.ts +1 -1
  77. package/dist/{components → styles/matte}/LocaleProvider.svelte +1 -1
  78. package/dist/{components → styles/matte}/LocaleProvider.svelte.d.ts +1 -1
  79. package/dist/styles/matte/LogoCloud.svelte +61 -0
  80. package/dist/styles/matte/LogoCloud.svelte.d.ts +19 -0
  81. package/dist/{components → styles/matte}/MenuItem.svelte +1 -1
  82. package/dist/{components → styles/matte}/MenuItem.svelte.d.ts +1 -1
  83. package/dist/{components → styles/matte}/Modal.svelte +4 -7
  84. package/dist/{components → styles/matte}/MultiSelect.svelte +18 -37
  85. package/dist/{components → styles/matte}/MultiSelect.svelte.d.ts +1 -1
  86. package/dist/{components → styles/matte}/Navbar.svelte +2 -2
  87. package/dist/{components → styles/matte}/Navbar.svelte.d.ts +1 -1
  88. package/dist/{components → styles/matte}/NumberInput.svelte +4 -8
  89. package/dist/{components → styles/matte}/NumberInput.svelte.d.ts +1 -1
  90. package/dist/{components → styles/matte}/Pagination.svelte +4 -15
  91. package/dist/{components → styles/matte}/Pagination.svelte.d.ts +1 -1
  92. package/dist/{components → styles/matte}/PasswordInput.svelte +4 -13
  93. package/dist/{components → styles/matte}/PasswordInput.svelte.d.ts +1 -1
  94. package/dist/{components → styles/matte}/Popover.svelte +16 -41
  95. package/dist/{components → styles/matte}/Popover.svelte.d.ts +1 -1
  96. package/dist/styles/matte/PricingCard.svelte +107 -0
  97. package/dist/styles/matte/PricingCard.svelte.d.ts +25 -0
  98. package/dist/{components → styles/matte}/Progress.svelte +3 -2
  99. package/dist/{components → styles/matte}/Progress.svelte.d.ts +1 -1
  100. package/dist/{components → styles/matte}/Radio.svelte +1 -1
  101. package/dist/{components → styles/matte}/Radio.svelte.d.ts +1 -1
  102. package/dist/{components → styles/matte}/RadioGroup.svelte +1 -1
  103. package/dist/{components → styles/matte}/RadioGroup.svelte.d.ts +1 -1
  104. package/dist/{components → styles/matte}/SegmentedControl.svelte +1 -1
  105. package/dist/{components → styles/matte}/SegmentedControl.svelte.d.ts +1 -1
  106. package/dist/{components → styles/matte}/Select.svelte +1 -1
  107. package/dist/{components → styles/matte}/Select.svelte.d.ts +1 -1
  108. package/dist/{components → styles/matte}/Sidebar.svelte +2 -2
  109. package/dist/{components → styles/matte}/Sidebar.svelte.d.ts +1 -1
  110. package/dist/{components → styles/matte}/Slider.svelte +3 -2
  111. package/dist/{components → styles/matte}/Slider.svelte.d.ts +1 -1
  112. package/dist/{components → styles/matte}/Spinner.svelte +2 -2
  113. package/dist/{components → styles/matte}/Spinner.svelte.d.ts +1 -1
  114. package/dist/{components → styles/matte}/Stat.svelte +1 -1
  115. package/dist/{components → styles/matte}/Stat.svelte.d.ts +1 -1
  116. package/dist/styles/matte/StatsBand.svelte +57 -0
  117. package/dist/styles/matte/StatsBand.svelte.d.ts +20 -0
  118. package/dist/{components → styles/matte}/Steps.svelte +1 -1
  119. package/dist/{components → styles/matte}/Steps.svelte.d.ts +1 -1
  120. package/dist/{components → styles/matte}/Switch.svelte +1 -1
  121. package/dist/{components → styles/matte}/Switch.svelte.d.ts +1 -1
  122. package/dist/{components → styles/matte}/Table.svelte +6 -28
  123. package/dist/{components → styles/matte}/Table.svelte.d.ts +2 -6
  124. package/dist/{components → styles/matte}/Tabs.svelte +8 -1
  125. package/dist/{components → styles/matte}/Tabs.svelte.d.ts +1 -1
  126. package/dist/styles/matte/Testimonial.svelte +67 -0
  127. package/dist/styles/matte/Testimonial.svelte.d.ts +19 -0
  128. package/dist/{components → styles/matte}/Textarea.svelte +1 -1
  129. package/dist/{components → styles/matte}/Textarea.svelte.d.ts +1 -1
  130. package/dist/{components → styles/matte}/ThemeToggle.svelte +14 -27
  131. package/dist/{components → styles/matte}/ThemeToggle.svelte.d.ts +2 -6
  132. package/dist/{components → styles/matte}/Toaster.svelte +4 -4
  133. package/dist/{fonts.css → styles/matte/fonts.css} +4 -3
  134. package/dist/styles/matte/index.d.ts +109 -0
  135. package/dist/styles/matte/index.js +83 -0
  136. package/dist/{theme.css → styles/matte/theme.css} +0 -1
  137. package/dist/styles/paper/Accordion.svelte +17 -0
  138. package/dist/styles/paper/Accordion.svelte.d.ts +8 -0
  139. package/dist/styles/paper/AccordionItem.svelte +46 -0
  140. package/dist/styles/paper/AccordionItem.svelte.d.ts +14 -0
  141. package/dist/styles/paper/Alert.svelte +99 -0
  142. package/dist/styles/paper/Alert.svelte.d.ts +18 -0
  143. package/dist/styles/paper/Avatar.svelte +60 -0
  144. package/dist/styles/paper/Avatar.svelte.d.ts +16 -0
  145. package/dist/styles/paper/AvatarGroup.svelte +17 -0
  146. package/dist/styles/paper/AvatarGroup.svelte.d.ts +8 -0
  147. package/dist/styles/paper/Badge.svelte +58 -0
  148. package/dist/styles/paper/Badge.svelte.d.ts +16 -0
  149. package/dist/styles/paper/Breadcrumb.svelte +49 -0
  150. package/dist/styles/paper/Breadcrumb.svelte.d.ts +14 -0
  151. package/dist/styles/paper/Button.svelte +96 -0
  152. package/dist/styles/paper/Button.svelte.d.ts +21 -0
  153. package/dist/styles/paper/CTASection.svelte +76 -0
  154. package/dist/styles/paper/CTASection.svelte.d.ts +18 -0
  155. package/dist/styles/paper/Calendar.svelte +202 -0
  156. package/dist/styles/paper/Calendar.svelte.d.ts +22 -0
  157. package/dist/styles/paper/Card.svelte +78 -0
  158. package/dist/styles/paper/Card.svelte.d.ts +21 -0
  159. package/dist/styles/paper/Checkbox.svelte +75 -0
  160. package/dist/styles/paper/Checkbox.svelte.d.ts +15 -0
  161. package/dist/styles/paper/CheckboxGroup.svelte +111 -0
  162. package/dist/styles/paper/CheckboxGroup.svelte.d.ts +30 -0
  163. package/dist/styles/paper/Combobox.svelte +266 -0
  164. package/dist/styles/paper/Combobox.svelte.d.ts +29 -0
  165. package/dist/styles/paper/CommandPalette.svelte +209 -0
  166. package/dist/styles/paper/CommandPalette.svelte.d.ts +32 -0
  167. package/dist/styles/paper/ConfirmDialog.svelte +97 -0
  168. package/dist/styles/paper/ConfirmDialog.svelte.d.ts +22 -0
  169. package/dist/styles/paper/ContextMenu.svelte +88 -0
  170. package/dist/styles/paper/ContextMenu.svelte.d.ts +15 -0
  171. package/dist/styles/paper/DatePicker.svelte +192 -0
  172. package/dist/styles/paper/DatePicker.svelte.d.ts +23 -0
  173. package/dist/styles/paper/Divider.svelte +30 -0
  174. package/dist/styles/paper/Divider.svelte.d.ts +10 -0
  175. package/dist/styles/paper/Drawer.svelte +165 -0
  176. package/dist/styles/paper/Drawer.svelte.d.ts +20 -0
  177. package/dist/styles/paper/Dropdown.svelte +94 -0
  178. package/dist/styles/paper/Dropdown.svelte.d.ts +23 -0
  179. package/dist/styles/paper/Dropzone.svelte +210 -0
  180. package/dist/styles/paper/Dropzone.svelte.d.ts +29 -0
  181. package/dist/styles/paper/EmptyState.svelte +71 -0
  182. package/dist/styles/paper/EmptyState.svelte.d.ts +18 -0
  183. package/dist/styles/paper/FeatureCard.svelte +58 -0
  184. package/dist/styles/paper/FeatureCard.svelte.d.ts +16 -0
  185. package/dist/styles/paper/FeatureGrid.svelte +62 -0
  186. package/dist/styles/paper/FeatureGrid.svelte.d.ts +16 -0
  187. package/dist/styles/paper/Field.svelte +42 -0
  188. package/dist/styles/paper/Field.svelte.d.ts +16 -0
  189. package/dist/styles/paper/Footer.svelte +91 -0
  190. package/dist/styles/paper/Footer.svelte.d.ts +25 -0
  191. package/dist/styles/paper/HeroSection.svelte +86 -0
  192. package/dist/styles/paper/HeroSection.svelte.d.ts +21 -0
  193. package/dist/styles/paper/Input.svelte +60 -0
  194. package/dist/styles/paper/Input.svelte.d.ts +18 -0
  195. package/dist/styles/paper/InputGroup.svelte +20 -0
  196. package/dist/styles/paper/InputGroup.svelte.d.ts +11 -0
  197. package/dist/styles/paper/Kbd.svelte +17 -0
  198. package/dist/styles/paper/Kbd.svelte.d.ts +8 -0
  199. package/dist/styles/paper/Label.svelte +16 -0
  200. package/dist/styles/paper/Label.svelte.d.ts +9 -0
  201. package/dist/styles/paper/Link.svelte +44 -0
  202. package/dist/styles/paper/Link.svelte.d.ts +16 -0
  203. package/dist/styles/paper/LocaleProvider.svelte +24 -0
  204. package/dist/styles/paper/LocaleProvider.svelte.d.ts +10 -0
  205. package/dist/styles/paper/LogoCloud.svelte +61 -0
  206. package/dist/styles/paper/LogoCloud.svelte.d.ts +19 -0
  207. package/dist/styles/paper/MenuItem.svelte +64 -0
  208. package/dist/styles/paper/MenuItem.svelte.d.ts +18 -0
  209. package/dist/styles/paper/MenuSeparator.svelte +22 -0
  210. package/dist/styles/paper/MenuSeparator.svelte.d.ts +8 -0
  211. package/dist/styles/paper/Modal.svelte +118 -0
  212. package/dist/styles/paper/Modal.svelte.d.ts +21 -0
  213. package/dist/styles/paper/MultiSelect.svelte +287 -0
  214. package/dist/styles/paper/MultiSelect.svelte.d.ts +31 -0
  215. package/dist/styles/paper/Navbar.svelte +201 -0
  216. package/dist/styles/paper/Navbar.svelte.d.ts +33 -0
  217. package/dist/styles/paper/NumberInput.svelte +96 -0
  218. package/dist/styles/paper/NumberInput.svelte.d.ts +15 -0
  219. package/dist/styles/paper/Pagination.svelte +83 -0
  220. package/dist/styles/paper/Pagination.svelte.d.ts +15 -0
  221. package/dist/styles/paper/PasswordInput.svelte +100 -0
  222. package/dist/styles/paper/PasswordInput.svelte.d.ts +12 -0
  223. package/dist/styles/paper/Popover.svelte +89 -0
  224. package/dist/styles/paper/Popover.svelte.d.ts +19 -0
  225. package/dist/styles/paper/PricingCard.svelte +108 -0
  226. package/dist/styles/paper/PricingCard.svelte.d.ts +25 -0
  227. package/dist/styles/paper/Progress.svelte +59 -0
  228. package/dist/styles/paper/Progress.svelte.d.ts +17 -0
  229. package/dist/styles/paper/Radio.svelte +55 -0
  230. package/dist/styles/paper/Radio.svelte.d.ts +15 -0
  231. package/dist/styles/paper/RadioGroup.svelte +95 -0
  232. package/dist/styles/paper/RadioGroup.svelte.d.ts +27 -0
  233. package/dist/styles/paper/SegmentedControl.svelte +88 -0
  234. package/dist/styles/paper/SegmentedControl.svelte.d.ts +25 -0
  235. package/dist/styles/paper/Select.svelte +61 -0
  236. package/dist/styles/paper/Select.svelte.d.ts +20 -0
  237. package/dist/styles/paper/Sidebar.svelte +206 -0
  238. package/dist/styles/paper/Sidebar.svelte.d.ts +36 -0
  239. package/dist/styles/paper/Skeleton.svelte +46 -0
  240. package/dist/styles/paper/Skeleton.svelte.d.ts +13 -0
  241. package/dist/styles/paper/Slider.svelte +80 -0
  242. package/dist/styles/paper/Slider.svelte.d.ts +19 -0
  243. package/dist/styles/paper/Spinner.svelte +34 -0
  244. package/dist/styles/paper/Spinner.svelte.d.ts +13 -0
  245. package/dist/styles/paper/Stat.svelte +51 -0
  246. package/dist/styles/paper/Stat.svelte.d.ts +16 -0
  247. package/dist/styles/paper/StatsBand.svelte +63 -0
  248. package/dist/styles/paper/StatsBand.svelte.d.ts +20 -0
  249. package/dist/styles/paper/Steps.svelte +141 -0
  250. package/dist/styles/paper/Steps.svelte.d.ts +24 -0
  251. package/dist/styles/paper/Switch.svelte +64 -0
  252. package/dist/styles/paper/Switch.svelte.d.ts +14 -0
  253. package/dist/styles/paper/Table.svelte +217 -0
  254. package/dist/styles/paper/Table.svelte.d.ts +66 -0
  255. package/dist/styles/paper/Tabs.svelte +102 -0
  256. package/dist/styles/paper/Tabs.svelte.d.ts +23 -0
  257. package/dist/styles/paper/Testimonial.svelte +64 -0
  258. package/dist/styles/paper/Testimonial.svelte.d.ts +19 -0
  259. package/dist/styles/paper/Textarea.svelte +54 -0
  260. package/dist/styles/paper/Textarea.svelte.d.ts +12 -0
  261. package/dist/styles/paper/ThemeToggle.svelte +130 -0
  262. package/dist/styles/paper/ThemeToggle.svelte.d.ts +16 -0
  263. package/dist/styles/paper/Toaster.svelte +132 -0
  264. package/dist/styles/paper/Toaster.svelte.d.ts +8 -0
  265. package/dist/styles/paper/Tooltip.svelte +33 -0
  266. package/dist/styles/paper/Tooltip.svelte.d.ts +12 -0
  267. package/dist/styles/paper/fonts.css +46 -0
  268. package/dist/styles/paper/index.d.ts +109 -0
  269. package/dist/styles/paper/index.js +83 -0
  270. package/dist/styles/paper/theme.css +222 -0
  271. package/package.json +531 -201
  272. package/registry.json +658 -339
  273. /package/dist/{actions → core/actions}/anchor.d.ts +0 -0
  274. /package/dist/{actions → core/actions}/anchor.js +0 -0
  275. /package/dist/{actions → core/actions}/dismissable.d.ts +0 -0
  276. /package/dist/{actions → core/actions}/dismissable.js +0 -0
  277. /package/dist/{date.d.ts → core/date.d.ts} +0 -0
  278. /package/dist/{date.js → core/date.js} +0 -0
  279. /package/dist/{locale.svelte.d.ts → core/locale.svelte.d.ts} +0 -0
  280. /package/dist/{locale.svelte.js → core/locale.svelte.js} +0 -0
  281. /package/dist/{toast.svelte.d.ts → core/toast.svelte.d.ts} +0 -0
  282. /package/dist/{toast.svelte.js → core/toast.svelte.js} +0 -0
  283. /package/dist/{tones.d.ts → core/tones.d.ts} +0 -0
  284. /package/dist/{tones.js → core/tones.js} +0 -0
  285. /package/dist/{components → styles/matte}/Accordion.svelte +0 -0
  286. /package/dist/{components → styles/matte}/Accordion.svelte.d.ts +0 -0
  287. /package/dist/{components → styles/matte}/AvatarGroup.svelte +0 -0
  288. /package/dist/{components → styles/matte}/AvatarGroup.svelte.d.ts +0 -0
  289. /package/dist/{components → styles/matte}/ContextMenu.svelte.d.ts +0 -0
  290. /package/dist/{components → styles/matte}/Divider.svelte +0 -0
  291. /package/dist/{components → styles/matte}/Divider.svelte.d.ts +0 -0
  292. /package/dist/{components → styles/matte}/Drawer.svelte.d.ts +0 -0
  293. /package/dist/{components → styles/matte}/Field.svelte +0 -0
  294. /package/dist/{components → styles/matte}/Field.svelte.d.ts +0 -0
  295. /package/dist/{components → styles/matte}/InputGroup.svelte +0 -0
  296. /package/dist/{components → styles/matte}/InputGroup.svelte.d.ts +0 -0
  297. /package/dist/{components → styles/matte}/Kbd.svelte +0 -0
  298. /package/dist/{components → styles/matte}/Kbd.svelte.d.ts +0 -0
  299. /package/dist/{components → styles/matte}/Label.svelte +0 -0
  300. /package/dist/{components → styles/matte}/Label.svelte.d.ts +0 -0
  301. /package/dist/{components → styles/matte}/MenuSeparator.svelte +0 -0
  302. /package/dist/{components → styles/matte}/MenuSeparator.svelte.d.ts +0 -0
  303. /package/dist/{components → styles/matte}/Modal.svelte.d.ts +0 -0
  304. /package/dist/{components → styles/matte}/Skeleton.svelte +0 -0
  305. /package/dist/{components → styles/matte}/Skeleton.svelte.d.ts +0 -0
  306. /package/dist/{components → styles/matte}/Toaster.svelte.d.ts +0 -0
  307. /package/dist/{components → styles/matte}/Tooltip.svelte +0 -0
  308. /package/dist/{components → styles/matte}/Tooltip.svelte.d.ts +0 -0
@@ -0,0 +1,217 @@
1
+ <script module lang="ts">
2
+ export interface TableColumn {
3
+ key: string;
4
+ label: string;
5
+ align?: 'left' | 'center' | 'right';
6
+ /** Extra classes for this column's cells, e.g. `'w-40 tabular-nums'`. */
7
+ class?: string;
8
+ /** Allow clicking the header to sort by this column. */
9
+ sortable?: boolean;
10
+ }
11
+ </script>
12
+
13
+ <script lang="ts" generics="T extends Record<string, unknown>">
14
+ import type { Snippet } from 'svelte';
15
+ import type { HTMLAttributes } from 'svelte/elements';
16
+ import Checkbox from './Checkbox.svelte';
17
+ import type { Tone } from '../../core/tones.js';
18
+ import { nextSort, sortRows, toggleKey, type TableSort } from '../../core/table.js';
19
+ import { useLocale } from '../../core/locale.svelte.js';
20
+
21
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'onselect'> {
22
+ columns: TableColumn[];
23
+ rows: T[];
24
+ /** Custom cell rendering — falls back to the raw value. */
25
+ cell?: Snippet<[{ row: T; column: TableColumn; value: unknown }]>;
26
+ /** Shown instead of the body when `rows` is empty. */
27
+ empty?: Snippet;
28
+ caption?: string;
29
+ striped?: boolean;
30
+ hoverable?: boolean;
31
+ compact?: boolean;
32
+ /** Header stays put while the body scrolls — pair with a `max-h-*` class. */
33
+ stickyHeader?: boolean;
34
+
35
+ /** Bindable. Set it to sort; leave `sortable` columns to drive it. */
36
+ sort?: TableSort | null;
37
+ /** Sort the rows here rather than refetching them server-side. Default true. */
38
+ sortLocally?: boolean;
39
+ onsort?: (sort: TableSort | null) => void;
40
+
41
+ /** Turns on the checkbox column. Bindable list of selected row keys. */
42
+ selectable?: boolean;
43
+ selected?: string[];
44
+ /** Which field identifies a row. Default `'id'`. */
45
+ rowKey?: string;
46
+ tone?: Tone;
47
+ onselect?: (selected: string[]) => void;
48
+ }
49
+
50
+ let {
51
+ columns,
52
+ rows,
53
+ cell,
54
+ empty,
55
+ caption,
56
+ striped = false,
57
+ hoverable = true,
58
+ compact = false,
59
+ stickyHeader = false,
60
+ sort = $bindable(null),
61
+ sortLocally = true,
62
+ onsort,
63
+ selectable = false,
64
+ selected = $bindable([]),
65
+ rowKey = 'id',
66
+ tone = 'brand',
67
+ onselect,
68
+ class: className = '',
69
+ ...rest
70
+ }: Props = $props();
71
+
72
+ const t = useLocale();
73
+
74
+ const aligns = {
75
+ left: 'text-left',
76
+ center: 'text-center',
77
+ right: 'text-right'
78
+ };
79
+
80
+ const pad = $derived(compact ? 'px-4 py-2' : 'px-4 py-3');
81
+
82
+ const sorted = $derived(sortLocally ? sortRows(rows, sort) : rows);
83
+
84
+ function toggleSort(column: TableColumn) {
85
+ if (!column.sortable) return;
86
+ const next = nextSort(sort, column.key);
87
+ sort = next;
88
+ onsort?.(next);
89
+ }
90
+
91
+ const keyOf = (row: T) => String(row[rowKey] ?? '');
92
+
93
+ const allSelected = $derived(
94
+ sorted.length > 0 && sorted.every((row) => selected.includes(keyOf(row)))
95
+ );
96
+ const someSelected = $derived(
97
+ !allSelected && sorted.some((row) => selected.includes(keyOf(row)))
98
+ );
99
+
100
+ function toggleAll() {
101
+ selected = allSelected ? [] : sorted.map(keyOf);
102
+ onselect?.(selected);
103
+ }
104
+
105
+ function toggleRow(row: T) {
106
+ selected = toggleKey(selected, keyOf(row));
107
+ onselect?.(selected);
108
+ }
109
+ </script>
110
+
111
+ <div
112
+ class="w-full overflow-auto rounded-lg border border-hairline bg-bg shadow-xs {className}"
113
+ {...rest}
114
+ >
115
+ <table class="w-full border-collapse font-sans text-sm">
116
+ {#if caption}
117
+ <caption
118
+ class="border-b border-hairline px-4 py-3 text-left font-sans text-[13px] text-text-muted"
119
+ >
120
+ {caption}
121
+ </caption>
122
+ {/if}
123
+ <thead class={stickyHeader ? 'sticky top-0 z-10' : ''}>
124
+ <tr class="border-b border-hairline bg-bg-alt">
125
+ {#if selectable}
126
+ <th scope="col" class="w-10 {pad}">
127
+ <Checkbox
128
+ checked={allSelected}
129
+ indeterminate={someSelected}
130
+ {tone}
131
+ aria-label={t.current.selectAllRows}
132
+ onchange={toggleAll}
133
+ />
134
+ </th>
135
+ {/if}
136
+ {#each columns as column (column.key)}
137
+ <th
138
+ scope="col"
139
+ aria-sort={sort?.key === column.key
140
+ ? sort.direction === 'asc'
141
+ ? 'ascending'
142
+ : 'descending'
143
+ : undefined}
144
+ class="text-[13px] font-semibold text-text-muted {pad} {aligns[
145
+ column.align ?? 'left'
146
+ ]} {column.class ?? ''}"
147
+ >
148
+ {#if column.sortable}
149
+ <button
150
+ type="button"
151
+ onclick={() => toggleSort(column)}
152
+ class="inline-flex items-center gap-1.5 rounded transition-colors duration-150 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current
153
+ {sort?.key === column.key ? 'text-text' : ''}"
154
+ >
155
+ {column.label}
156
+ <span class="text-[9px] leading-none" aria-hidden="true">
157
+ {#if sort?.key === column.key}
158
+ {sort.direction === 'asc' ? '▲' : '▼'}
159
+ {:else}
160
+
161
+ {/if}
162
+ </span>
163
+ </button>
164
+ {:else}
165
+ {column.label}
166
+ {/if}
167
+ </th>
168
+ {/each}
169
+ </tr>
170
+ </thead>
171
+ <tbody>
172
+ {#if sorted.length === 0}
173
+ <tr>
174
+ <td
175
+ colspan={columns.length + (selectable ? 1 : 0)}
176
+ class="px-4 py-10 text-center text-sm text-text-muted"
177
+ >
178
+ {#if empty}{@render empty()}{:else}{t.current.noData}{/if}
179
+ </td>
180
+ </tr>
181
+ {:else}
182
+ {#each sorted as row, i (selectable ? keyOf(row) : i)}
183
+ {@const isSelected = selectable && selected.includes(keyOf(row))}
184
+ <tr
185
+ aria-selected={selectable ? isSelected : undefined}
186
+ class="border-b border-hairline last:border-b-0
187
+ {isSelected ? 'bg-brand-light' : striped && i % 2 === 1 ? 'bg-bg-alt' : ''}
188
+ {hoverable ? 'transition-colors duration-150 hover:bg-bg-alt' : ''}"
189
+ >
190
+ {#if selectable}
191
+ <td class={pad}>
192
+ <Checkbox
193
+ checked={isSelected}
194
+ {tone}
195
+ aria-label={t.current.selectRow}
196
+ onchange={() => toggleRow(row)}
197
+ />
198
+ </td>
199
+ {/if}
200
+ {#each columns as column (column.key)}
201
+ <td
202
+ class="text-text-secondary {pad} {aligns[column.align ?? 'left']} {column.class ??
203
+ ''}"
204
+ >
205
+ {#if cell}
206
+ {@render cell({ row, column, value: row[column.key] })}
207
+ {:else}
208
+ {row[column.key] ?? ''}
209
+ {/if}
210
+ </td>
211
+ {/each}
212
+ </tr>
213
+ {/each}
214
+ {/if}
215
+ </tbody>
216
+ </table>
217
+ </div>
@@ -0,0 +1,66 @@
1
+ export interface TableColumn {
2
+ key: string;
3
+ label: string;
4
+ align?: 'left' | 'center' | 'right';
5
+ /** Extra classes for this column's cells, e.g. `'w-40 tabular-nums'`. */
6
+ class?: string;
7
+ /** Allow clicking the header to sort by this column. */
8
+ sortable?: boolean;
9
+ }
10
+ import type { Snippet } from 'svelte';
11
+ import type { HTMLAttributes } from 'svelte/elements';
12
+ import type { Tone } from '../../core/tones.js';
13
+ import { type TableSort } from '../../core/table.js';
14
+ declare function $$render<T extends Record<string, unknown>>(): {
15
+ props: Omit<HTMLAttributes<HTMLDivElement>, "onselect"> & {
16
+ columns: TableColumn[];
17
+ rows: T[];
18
+ /** Custom cell rendering — falls back to the raw value. */
19
+ cell?: Snippet<[{
20
+ row: T;
21
+ column: TableColumn;
22
+ value: unknown;
23
+ }]>;
24
+ /** Shown instead of the body when `rows` is empty. */
25
+ empty?: Snippet;
26
+ caption?: string;
27
+ striped?: boolean;
28
+ hoverable?: boolean;
29
+ compact?: boolean;
30
+ /** Header stays put while the body scrolls — pair with a `max-h-*` class. */
31
+ stickyHeader?: boolean;
32
+ /** Bindable. Set it to sort; leave `sortable` columns to drive it. */
33
+ sort?: TableSort | null;
34
+ /** Sort the rows here rather than refetching them server-side. Default true. */
35
+ sortLocally?: boolean;
36
+ onsort?: (sort: TableSort | null) => void;
37
+ /** Turns on the checkbox column. Bindable list of selected row keys. */
38
+ selectable?: boolean;
39
+ selected?: string[];
40
+ /** Which field identifies a row. Default `'id'`. */
41
+ rowKey?: string;
42
+ tone?: Tone;
43
+ onselect?: (selected: string[]) => void;
44
+ };
45
+ exports: {};
46
+ bindings: "sort" | "selected";
47
+ slots: {};
48
+ events: {};
49
+ };
50
+ declare class __sveltets_Render<T extends Record<string, unknown>> {
51
+ props(): ReturnType<typeof $$render<T>>['props'];
52
+ events(): ReturnType<typeof $$render<T>>['events'];
53
+ slots(): ReturnType<typeof $$render<T>>['slots'];
54
+ bindings(): "sort" | "selected";
55
+ exports(): {};
56
+ }
57
+ interface $$IsomorphicComponent {
58
+ new <T extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
59
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
60
+ } & ReturnType<__sveltets_Render<T>['exports']>;
61
+ <T extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
62
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
63
+ }
64
+ declare const Table: $$IsomorphicComponent;
65
+ type Table<T extends Record<string, unknown>> = InstanceType<typeof Table<T>>;
66
+ export default Table;
@@ -0,0 +1,102 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import {
4
+ focusRing,
5
+ toneBorder,
6
+ toneRing,
7
+ toneSoft,
8
+ toneText,
9
+ type Tone
10
+ } from '../../core/tones.js';
11
+
12
+ export interface TabItem {
13
+ value: string;
14
+ label: string;
15
+ disabled?: boolean;
16
+ /** Small count/label rendered after the tab title. */
17
+ badge?: string | number;
18
+ }
19
+
20
+ export type TabsVariant = 'underline' | 'pill' | 'segmented';
21
+
22
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'onchange'> {
23
+ items: TabItem[];
24
+ /** Bindable — the `value` of the active tab. */
25
+ value?: string;
26
+ variant?: TabsVariant;
27
+ tone?: Tone;
28
+ /** Spread tabs evenly across the full width. */
29
+ fullWidth?: boolean;
30
+ onchange?: (value: string) => void;
31
+ }
32
+
33
+ let {
34
+ items,
35
+ value = $bindable(items[0]?.value ?? ''),
36
+ variant = 'underline',
37
+ tone = 'brand',
38
+ fullWidth = false,
39
+ onchange,
40
+ class: className = '',
41
+ ...rest
42
+ }: Props = $props();
43
+
44
+ function select(item: TabItem) {
45
+ if (item.disabled) return;
46
+ value = item.value;
47
+ onchange?.(item.value);
48
+ }
49
+
50
+ const listClass = $derived(
51
+ {
52
+ underline: 'gap-5 border-b border-hairline',
53
+ pill: 'gap-1.5',
54
+ segmented: 'gap-1 rounded-lg border border-hairline bg-bg-inset p-1'
55
+ }[variant]
56
+ );
57
+
58
+ function tabClass(active: boolean) {
59
+ const base = `inline-flex items-center justify-center gap-2 font-sans text-sm font-medium whitespace-nowrap transition-all duration-150 ease-brand-out disabled:opacity-40 disabled:pointer-events-none ${focusRing} ${toneRing[tone]}`;
60
+ if (variant === 'underline') {
61
+ return `${base} -mb-px rounded-t-md border-b-2 px-1 pb-2.5 ${
62
+ active
63
+ ? `${toneBorder[tone]} ${toneText[tone]}`
64
+ : 'border-transparent text-text-muted hover:text-text'
65
+ }`;
66
+ }
67
+ if (variant === 'pill') {
68
+ return `${base} h-9 rounded-full px-3.5 ${
69
+ active ? toneSoft[tone] : 'text-text-muted hover:bg-bg-inset hover:text-text'
70
+ }`;
71
+ }
72
+ return `${base} h-8 rounded-md px-3.5 ${
73
+ active ? 'bg-bg text-text shadow-xs' : 'text-text-muted hover:text-text'
74
+ }`;
75
+ }
76
+ </script>
77
+
78
+ <div
79
+ role="tablist"
80
+ class="flex items-center overflow-x-auto {listClass} {fullWidth ? '*:flex-1' : ''} {className}"
81
+ {...rest}
82
+ >
83
+ {#each items as item (item.value)}
84
+ <button
85
+ type="button"
86
+ role="tab"
87
+ aria-selected={value === item.value}
88
+ disabled={item.disabled}
89
+ onclick={() => select(item)}
90
+ class={tabClass(value === item.value)}
91
+ >
92
+ {item.label}
93
+ {#if item.badge !== undefined}
94
+ <span
95
+ class="rounded-full bg-bg-inset px-1.5 text-[11px] font-normal text-text-muted tabular-nums"
96
+ >
97
+ {item.badge}
98
+ </span>
99
+ {/if}
100
+ </button>
101
+ {/each}
102
+ </div>
@@ -0,0 +1,23 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import { type Tone } from '../../core/tones.js';
3
+ export interface TabItem {
4
+ value: string;
5
+ label: string;
6
+ disabled?: boolean;
7
+ /** Small count/label rendered after the tab title. */
8
+ badge?: string | number;
9
+ }
10
+ export type TabsVariant = 'underline' | 'pill' | 'segmented';
11
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'onchange'> {
12
+ items: TabItem[];
13
+ /** Bindable — the `value` of the active tab. */
14
+ value?: string;
15
+ variant?: TabsVariant;
16
+ tone?: Tone;
17
+ /** Spread tabs evenly across the full width. */
18
+ fullWidth?: boolean;
19
+ onchange?: (value: string) => void;
20
+ }
21
+ declare const Tabs: import("svelte").Component<Props, {}, "value">;
22
+ type Tabs = ReturnType<typeof Tabs>;
23
+ export default Tabs;
@@ -0,0 +1,64 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ import Avatar from './Avatar.svelte';
5
+ import { toneBorderLeft, toneSurface, type Tone } from '../../core/tones.js';
6
+
7
+ export type TestimonialVariant = 'plain' | 'card' | 'accent';
8
+
9
+ interface Props extends HTMLAttributes<HTMLElement> {
10
+ quote: string;
11
+ author: string;
12
+ role?: string;
13
+ /** Avatar image. Falls back to the author's initials. */
14
+ avatar?: string;
15
+ tone?: Tone;
16
+ variant?: TestimonialVariant;
17
+ /** Bigger type, for a single pull quote. */
18
+ large?: boolean;
19
+ children?: Snippet;
20
+ }
21
+
22
+ let {
23
+ quote,
24
+ author,
25
+ role,
26
+ avatar,
27
+ tone = 'brand',
28
+ variant = 'plain',
29
+ large = false,
30
+ class: className = '',
31
+ children,
32
+ ...rest
33
+ }: Props = $props();
34
+
35
+ const variants = $derived({
36
+ plain: '',
37
+ card: 'rounded-lg border border-hairline bg-bg p-6 shadow-xs',
38
+ accent: `rounded-lg ${toneSurface[tone]} border-l-4 ${toneBorderLeft[tone]} p-6`
39
+ });
40
+ </script>
41
+
42
+ <figure class="flex flex-col gap-5 {variants[variant]} {className}" {...rest}>
43
+ <blockquote
44
+ class="font-sans leading-relaxed text-pretty text-text-secondary {large
45
+ ? 'text-xl leading-relaxed sm:text-2xl'
46
+ : ''}"
47
+ >
48
+ “{quote}”
49
+ </blockquote>
50
+
51
+ {#if children}
52
+ {@render children()}
53
+ {/if}
54
+
55
+ <figcaption class="flex items-center gap-3">
56
+ <Avatar src={avatar} name={author} size="sm" {tone} />
57
+ <div class="flex min-w-0 flex-col">
58
+ <span class="font-sans text-sm font-semibold text-text">{author}</span>
59
+ {#if role}
60
+ <span class="font-sans text-[13px] text-text-muted">{role}</span>
61
+ {/if}
62
+ </div>
63
+ </figcaption>
64
+ </figure>
@@ -0,0 +1,19 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { type Tone } from '../../core/tones.js';
4
+ export type TestimonialVariant = 'plain' | 'card' | 'accent';
5
+ interface Props extends HTMLAttributes<HTMLElement> {
6
+ quote: string;
7
+ author: string;
8
+ role?: string;
9
+ /** Avatar image. Falls back to the author's initials. */
10
+ avatar?: string;
11
+ tone?: Tone;
12
+ variant?: TestimonialVariant;
13
+ /** Bigger type, for a single pull quote. */
14
+ large?: boolean;
15
+ children?: Snippet;
16
+ }
17
+ declare const Testimonial: import("svelte").Component<Props, {}, "">;
18
+ type Testimonial = ReturnType<typeof Testimonial>;
19
+ export default Testimonial;
@@ -0,0 +1,54 @@
1
+ <script lang="ts">
2
+ import type { HTMLTextareaAttributes } from 'svelte/elements';
3
+ import { toneFocusBorder, type Tone } from '../../core/tones.js';
4
+
5
+ interface Props extends HTMLTextareaAttributes {
6
+ value?: string;
7
+ tone?: Tone;
8
+ invalid?: boolean;
9
+ /** Grow with the content instead of showing a scrollbar. */
10
+ autoresize?: boolean;
11
+ }
12
+
13
+ let {
14
+ value = $bindable(''),
15
+ tone = 'brand',
16
+ invalid = false,
17
+ autoresize = false,
18
+ rows = 4,
19
+ class: className = '',
20
+ ...rest
21
+ }: Props = $props();
22
+
23
+ /** Grows the field to fit its content on input, and once on mount. */
24
+ function autosize(node: HTMLTextAreaElement, enabled: boolean) {
25
+ let on = enabled;
26
+ const resize = () => {
27
+ if (!on) return;
28
+ node.style.height = 'auto';
29
+ node.style.height = `${node.scrollHeight}px`;
30
+ };
31
+ resize();
32
+ node.addEventListener('input', resize);
33
+ return {
34
+ update(next: boolean) {
35
+ on = next;
36
+ if (on) resize();
37
+ else node.style.height = '';
38
+ },
39
+ destroy() {
40
+ node.removeEventListener('input', resize);
41
+ }
42
+ };
43
+ }
44
+ </script>
45
+
46
+ <textarea
47
+ use:autosize={autoresize}
48
+ bind:value
49
+ {rows}
50
+ aria-invalid={invalid ? 'true' : undefined}
51
+ class="w-full rounded-md border bg-bg px-3 py-2 font-sans text-sm text-text shadow-xs transition-colors duration-150 ease-brand-out placeholder:text-text-muted focus:outline-none disabled:pointer-events-none disabled:opacity-50
52
+ {autoresize ? 'resize-none overflow-hidden' : 'resize-y'}
53
+ {invalid ? 'border-danger' : `border-hairline-strong ${toneFocusBorder[tone]}`} {className}"
54
+ {...rest}></textarea>
@@ -0,0 +1,12 @@
1
+ import type { HTMLTextareaAttributes } from 'svelte/elements';
2
+ import { type Tone } from '../../core/tones.js';
3
+ interface Props extends HTMLTextareaAttributes {
4
+ value?: string;
5
+ tone?: Tone;
6
+ invalid?: boolean;
7
+ /** Grow with the content instead of showing a scrollbar. */
8
+ autoresize?: boolean;
9
+ }
10
+ declare const Textarea: import("svelte").Component<Props, {}, "value">;
11
+ type Textarea = ReturnType<typeof Textarea>;
12
+ export default Textarea;
@@ -0,0 +1,130 @@
1
+ <script lang="ts">
2
+ // Choosing a theme is style-agnostic: every style reads the same two classes
3
+ // off <html>, so `applyTheme`, `storedTheme` and `Theme` live in core and
4
+ // are re-exported by the style barrel — not by this component.
5
+ import { focusRing, toneRing, type Tone } from '../../core/tones.js';
6
+ import {
7
+ applyTheme,
8
+ nextTheme,
9
+ persistTheme,
10
+ storedTheme,
11
+ type Theme
12
+ } from '../../core/theme.svelte.js';
13
+ import { useLocale } from '../../core/locale.svelte.js';
14
+
15
+ interface Props {
16
+ /** Bindable. `'system'` follows the OS until the visitor chooses. */
17
+ theme?: Theme;
18
+ /** `'button'` cycles light → dark → system; `'segmented'` shows all three. */
19
+ variant?: 'button' | 'segmented';
20
+ tone?: Tone;
21
+ /** Remember the choice in `localStorage`. Default true. */
22
+ persist?: boolean;
23
+ onchange?: (theme: Theme) => void;
24
+ class?: string;
25
+ }
26
+
27
+ let {
28
+ theme = $bindable(storedTheme()),
29
+ variant = 'button',
30
+ tone = 'neutral',
31
+ persist = true,
32
+ onchange,
33
+ class: className = ''
34
+ }: Props = $props();
35
+
36
+ const t = useLocale();
37
+
38
+ $effect(() => {
39
+ applyTheme(theme);
40
+ if (persist) persistTheme(theme);
41
+ });
42
+
43
+ function set(next: Theme) {
44
+ theme = next;
45
+ onchange?.(next);
46
+ }
47
+
48
+ function cycle() {
49
+ set(nextTheme(theme));
50
+ }
51
+
52
+ const options = $derived([
53
+ { value: 'light' as Theme, label: t.current.lightMode },
54
+ { value: 'dark' as Theme, label: t.current.darkMode },
55
+ { value: 'system' as Theme, label: 'Auto' }
56
+ ]);
57
+ </script>
58
+
59
+ {#snippet glyph(which: Theme)}
60
+ {#if which === 'light'}
61
+ <svg class="size-4" viewBox="0 0 20 20" fill="none" aria-hidden="true">
62
+ <circle cx="10" cy="10" r="3.5" stroke="currentColor" stroke-width="1.5" />
63
+ <path
64
+ d="M10 2v2M10 16v2M2 10h2M16 10h2M4.5 4.5 6 6M14 14l1.5 1.5M15.5 4.5 14 6M6 14l-1.5 1.5"
65
+ stroke="currentColor"
66
+ stroke-width="1.5"
67
+ stroke-linecap="round"
68
+ />
69
+ </svg>
70
+ {:else if which === 'dark'}
71
+ <svg class="size-4" viewBox="0 0 20 20" fill="none" aria-hidden="true">
72
+ <path
73
+ d="M16 12.3A6.8 6.8 0 0 1 7.7 4a6.8 6.8 0 1 0 8.3 8.3Z"
74
+ stroke="currentColor"
75
+ stroke-width="1.5"
76
+ stroke-linejoin="round"
77
+ />
78
+ </svg>
79
+ {:else}
80
+ <svg class="size-4" viewBox="0 0 20 20" fill="none" aria-hidden="true">
81
+ <rect
82
+ x="2.5"
83
+ y="4"
84
+ width="15"
85
+ height="10"
86
+ rx="1.5"
87
+ stroke="currentColor"
88
+ stroke-width="1.5"
89
+ />
90
+ <path d="M7 17h6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
91
+ </svg>
92
+ {/if}
93
+ {/snippet}
94
+
95
+ {#if variant === 'segmented'}
96
+ <div
97
+ role="radiogroup"
98
+ aria-label={t.current.toggleTheme}
99
+ class="inline-flex items-center gap-1 rounded-lg border border-hairline bg-bg-inset p-1 {className}"
100
+ >
101
+ {#each options as option (option.value)}
102
+ <button
103
+ type="button"
104
+ role="radio"
105
+ aria-checked={theme === option.value}
106
+ aria-label={option.label}
107
+ onclick={() => set(option.value)}
108
+ class="inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 font-sans text-[13px] font-medium transition-all duration-150 ease-brand-out {focusRing} {toneRing[
109
+ tone
110
+ ]}
111
+ {theme === option.value ? 'bg-bg text-text shadow-xs' : 'text-text-muted hover:text-text'}"
112
+ >
113
+ {@render glyph(option.value)}
114
+ <span class="hidden sm:inline">{option.label}</span>
115
+ </button>
116
+ {/each}
117
+ </div>
118
+ {:else}
119
+ <button
120
+ type="button"
121
+ onclick={cycle}
122
+ aria-label="{t.current.toggleTheme} ({theme})"
123
+ title={t.current.toggleTheme}
124
+ class="inline-flex size-9 items-center justify-center rounded-md border border-hairline bg-bg text-text-secondary shadow-xs transition-colors duration-150 ease-brand-out hover:bg-bg-alt hover:text-text {focusRing} {toneRing[
125
+ tone
126
+ ]} {className}"
127
+ >
128
+ {@render glyph(theme)}
129
+ </button>
130
+ {/if}