@noahbaumgartner/svelte-ui 0.0.5 → 0.0.7

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 (214) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +20 -5
  3. package/dist/components/accordion/Accordion.svelte +16 -6
  4. package/dist/components/accordion/Accordion.svelte.d.ts +3 -1
  5. package/dist/components/accordion/AccordionItem.svelte +106 -67
  6. package/dist/components/accordion/AccordionItem.svelte.d.ts +5 -1
  7. package/dist/components/alert-dialog/AlertDialog.svelte +108 -76
  8. package/dist/components/alert-dialog/AlertDialog.svelte.d.ts +5 -2
  9. package/dist/components/avatar/Avatar.svelte +71 -49
  10. package/dist/components/avatar/Avatar.svelte.d.ts +5 -2
  11. package/dist/components/avatar-group/AvatarGroup.svelte +72 -45
  12. package/dist/components/avatar-group/AvatarGroup.svelte.d.ts +5 -1
  13. package/dist/components/badge/Badge.svelte +50 -28
  14. package/dist/components/badge/Badge.svelte.d.ts +6 -4
  15. package/dist/components/breadcrumb/Breadcrumb.svelte +21 -17
  16. package/dist/components/breadcrumb/Breadcrumb.svelte.d.ts +3 -1
  17. package/dist/components/breadcrumb/BreadcrumbEllipsis.svelte +17 -6
  18. package/dist/components/breadcrumb/BreadcrumbEllipsis.svelte.d.ts +5 -2
  19. package/dist/components/breadcrumb/BreadcrumbItem.svelte +75 -36
  20. package/dist/components/breadcrumb/BreadcrumbItem.svelte.d.ts +8 -5
  21. package/dist/components/breadcrumb/BreadcrumbSeparator.svelte +32 -9
  22. package/dist/components/breadcrumb/BreadcrumbSeparator.svelte.d.ts +10 -17
  23. package/dist/components/button/Button.svelte +206 -168
  24. package/dist/components/button/Button.svelte.d.ts +9 -5
  25. package/dist/components/button-group/ButtonGroup.svelte +52 -55
  26. package/dist/components/button-group/ButtonGroup.svelte.d.ts +3 -1
  27. package/dist/components/calendar/Calendar.svelte +232 -144
  28. package/dist/components/calendar/Calendar.svelte.d.ts +19 -2
  29. package/dist/components/calendar/dates.d.ts +4 -1
  30. package/dist/components/calendar/dates.js +8 -2
  31. package/dist/components/card/Card.svelte +48 -32
  32. package/dist/components/card/Card.svelte.d.ts +3 -1
  33. package/dist/components/card/CardAction.svelte +11 -7
  34. package/dist/components/card/CardAction.svelte.d.ts +3 -1
  35. package/dist/components/card/CardContent.svelte +8 -4
  36. package/dist/components/card/CardContent.svelte.d.ts +3 -1
  37. package/dist/components/card/CardDescription.svelte +11 -7
  38. package/dist/components/card/CardDescription.svelte.d.ts +3 -1
  39. package/dist/components/card/CardFooter.svelte +15 -11
  40. package/dist/components/card/CardFooter.svelte.d.ts +3 -1
  41. package/dist/components/card/CardHeader.svelte +15 -11
  42. package/dist/components/card/CardHeader.svelte.d.ts +3 -1
  43. package/dist/components/card/CardTitle.svelte +11 -7
  44. package/dist/components/card/CardTitle.svelte.d.ts +3 -1
  45. package/dist/components/checkbox/Checkbox.svelte +152 -61
  46. package/dist/components/checkbox/Checkbox.svelte.d.ts +5 -2
  47. package/dist/components/color-picker/ColorPicker.svelte +131 -78
  48. package/dist/components/color-picker/ColorPicker.svelte.d.ts +10 -1
  49. package/dist/components/context-menu/ContextMenu.svelte +78 -61
  50. package/dist/components/context-menu/ContextMenu.svelte.d.ts +5 -2
  51. package/dist/components/context-menu/ContextMenuInfo.svelte +91 -0
  52. package/dist/components/context-menu/ContextMenuInfo.svelte.d.ts +19 -0
  53. package/dist/components/context-menu/ContextMenuItem.svelte +102 -57
  54. package/dist/components/context-menu/ContextMenuItem.svelte.d.ts +10 -5
  55. package/dist/components/context-menu/ContextMenuLabel.svelte +28 -0
  56. package/dist/components/context-menu/ContextMenuLabel.svelte.d.ts +10 -0
  57. package/dist/components/context-menu/ContextMenuSeparator.svelte +29 -0
  58. package/dist/components/context-menu/ContextMenuSeparator.svelte.d.ts +8 -0
  59. package/dist/components/dialog/Dialog.svelte +149 -108
  60. package/dist/components/dialog/Dialog.svelte.d.ts +7 -2
  61. package/dist/components/drawer/Drawer.svelte +240 -189
  62. package/dist/components/drawer/Drawer.svelte.d.ts +7 -2
  63. package/dist/components/empty-state/EmptyState.svelte +67 -47
  64. package/dist/components/empty-state/EmptyState.svelte.d.ts +7 -4
  65. package/dist/components/entry/Entry.svelte +53 -42
  66. package/dist/components/entry/Entry.svelte.d.ts +2 -1
  67. package/dist/components/field/Field.svelte +66 -45
  68. package/dist/components/field/Field.svelte.d.ts +2 -1
  69. package/dist/components/field/FieldContent.svelte +11 -8
  70. package/dist/components/field/FieldContent.svelte.d.ts +2 -1
  71. package/dist/components/field/FieldDescription.svelte +18 -15
  72. package/dist/components/field/FieldDescription.svelte.d.ts +2 -1
  73. package/dist/components/field/FieldError.svelte +22 -13
  74. package/dist/components/field/FieldError.svelte.d.ts +2 -1
  75. package/dist/components/field/FieldGroup.svelte +15 -12
  76. package/dist/components/field/FieldGroup.svelte.d.ts +2 -1
  77. package/dist/components/field/FieldLegend.svelte +23 -14
  78. package/dist/components/field/FieldLegend.svelte.d.ts +2 -1
  79. package/dist/components/field/FieldSeparator.svelte +12 -9
  80. package/dist/components/field/FieldSeparator.svelte.d.ts +2 -1
  81. package/dist/components/field/FieldSet.svelte +18 -15
  82. package/dist/components/field/FieldSet.svelte.d.ts +2 -1
  83. package/dist/components/footer/Footer.svelte +63 -47
  84. package/dist/components/footer/Footer.svelte.d.ts +2 -1
  85. package/dist/components/footer/FooterColumn.svelte +17 -14
  86. package/dist/components/footer/FooterColumn.svelte.d.ts +2 -1
  87. package/dist/components/image/Image.svelte +61 -0
  88. package/dist/components/image/Image.svelte.d.ts +18 -0
  89. package/dist/components/input/Input.svelte +307 -208
  90. package/dist/components/input/Input.svelte.d.ts +12 -1
  91. package/dist/components/input-group/InputGroup.svelte +101 -61
  92. package/dist/components/input-group/InputGroup.svelte.d.ts +2 -1
  93. package/dist/components/input-group/InputGroupAddon.svelte +58 -49
  94. package/dist/components/kbd/Kbd.svelte +39 -30
  95. package/dist/components/kbd/Kbd.svelte.d.ts +2 -1
  96. package/dist/components/kbd/KbdGroup.svelte +15 -12
  97. package/dist/components/kbd/KbdGroup.svelte.d.ts +2 -1
  98. package/dist/components/label/Label.svelte +47 -28
  99. package/dist/components/label/Label.svelte.d.ts +2 -1
  100. package/dist/components/link/Link.svelte +71 -17
  101. package/dist/components/link/Link.svelte.d.ts +2 -1
  102. package/dist/components/mesh-gradient/MeshGradient.svelte +33 -22
  103. package/dist/components/mesh-gradient/MeshGradient.svelte.d.ts +2 -3
  104. package/dist/components/navbar/Navbar.svelte +149 -99
  105. package/dist/components/navbar/Navbar.svelte.d.ts +4 -2
  106. package/dist/components/navigation-menu/NavigationMenu.svelte +37 -20
  107. package/dist/components/navigation-menu/NavigationMenu.svelte.d.ts +4 -2
  108. package/dist/components/navigation-menu/NavigationMenuItem.svelte +131 -89
  109. package/dist/components/navigation-menu/NavigationMenuItem.svelte.d.ts +4 -2
  110. package/dist/components/navigation-menu/NavigationMenuLink.svelte +90 -51
  111. package/dist/components/navigation-menu/NavigationMenuLink.svelte.d.ts +7 -5
  112. package/dist/components/number-stepper/NumberStepper.svelte +39 -20
  113. package/dist/components/number-stepper/NumberStepper.svelte.d.ts +6 -1
  114. package/dist/components/popover/Popover.svelte +85 -65
  115. package/dist/components/popover/Popover.svelte.d.ts +5 -2
  116. package/dist/components/preview-card/PreviewCard.svelte +116 -98
  117. package/dist/components/preview-card/PreviewCard.svelte.d.ts +9 -6
  118. package/dist/components/progress/Progress.svelte +61 -38
  119. package/dist/components/progress/Progress.svelte.d.ts +2 -1
  120. package/dist/components/radio-group/RadioGroup.svelte +24 -19
  121. package/dist/components/radio-group/RadioGroup.svelte.d.ts +2 -1
  122. package/dist/components/radio-group/RadioGroupItem.svelte +101 -54
  123. package/dist/components/radio-group/RadioGroupItem.svelte.d.ts +2 -1
  124. package/dist/components/section/Section.svelte +114 -0
  125. package/dist/components/section/Section.svelte.d.ts +22 -0
  126. package/dist/components/select/Select.svelte +216 -133
  127. package/dist/components/select/Select.svelte.d.ts +11 -2
  128. package/dist/components/separator/Separator.svelte +21 -10
  129. package/dist/components/separator/Separator.svelte.d.ts +2 -1
  130. package/dist/components/sidebar/Sidebar.svelte +142 -108
  131. package/dist/components/sidebar/Sidebar.svelte.d.ts +6 -1
  132. package/dist/components/sidebar/SidebarContent.svelte +15 -12
  133. package/dist/components/sidebar/SidebarContent.svelte.d.ts +2 -1
  134. package/dist/components/sidebar/SidebarFooter.svelte +10 -7
  135. package/dist/components/sidebar/SidebarFooter.svelte.d.ts +2 -1
  136. package/dist/components/sidebar/SidebarGroup.svelte +35 -25
  137. package/dist/components/sidebar/SidebarGroup.svelte.d.ts +2 -1
  138. package/dist/components/sidebar/SidebarHeader.svelte +21 -16
  139. package/dist/components/sidebar/SidebarHeader.svelte.d.ts +6 -1
  140. package/dist/components/sidebar/SidebarInset.svelte +11 -8
  141. package/dist/components/sidebar/SidebarInset.svelte.d.ts +2 -1
  142. package/dist/components/sidebar/SidebarMenu.svelte +13 -10
  143. package/dist/components/sidebar/SidebarMenu.svelte.d.ts +2 -1
  144. package/dist/components/sidebar/SidebarMenuItem.svelte +195 -114
  145. package/dist/components/sidebar/SidebarMenuItem.svelte.d.ts +11 -5
  146. package/dist/components/sidebar/SidebarProvider.svelte +18 -9
  147. package/dist/components/sidebar/SidebarProvider.svelte.d.ts +2 -1
  148. package/dist/components/sidebar/SidebarSeparator.svelte +6 -1
  149. package/dist/components/sidebar/SidebarSeparator.svelte.d.ts +6 -17
  150. package/dist/components/sidebar/SidebarTrigger.svelte +17 -5
  151. package/dist/components/sidebar/SidebarTrigger.svelte.d.ts +4 -2
  152. package/dist/components/skeleton/Skeleton.svelte +26 -16
  153. package/dist/components/skeleton/Skeleton.svelte.d.ts +4 -2
  154. package/dist/components/spinner/Spinner.svelte +71 -46
  155. package/dist/components/spinner/Spinner.svelte.d.ts +2 -1
  156. package/dist/components/switch/Switch.svelte +126 -65
  157. package/dist/components/switch/Switch.svelte.d.ts +4 -2
  158. package/dist/components/table/Table.svelte +135 -76
  159. package/dist/components/table/Table.svelte.d.ts +12 -2
  160. package/dist/components/tabs/Tabs.svelte +173 -116
  161. package/dist/components/tabs/Tabs.svelte.d.ts +10 -8
  162. package/dist/components/textarea/Textarea.svelte +76 -42
  163. package/dist/components/textarea/Textarea.svelte.d.ts +4 -1
  164. package/dist/components/time-picker/TimePicker.svelte +214 -85
  165. package/dist/components/time-picker/TimePicker.svelte.d.ts +18 -1
  166. package/dist/components/toaster/Toaster.svelte +132 -70
  167. package/dist/components/toaster/Toaster.svelte.d.ts +18 -2
  168. package/dist/components/toaster/toast.svelte.d.ts +3 -3
  169. package/dist/components/toggle/Toggle.svelte +25 -4
  170. package/dist/components/toggle/Toggle.svelte.d.ts +1 -1
  171. package/dist/components/tooltip/Tooltip.svelte +87 -58
  172. package/dist/components/tooltip/Tooltip.svelte.d.ts +4 -2
  173. package/dist/icon.d.ts +12 -0
  174. package/dist/icon.js +1 -0
  175. package/dist/index.d.ts +6 -0
  176. package/dist/index.js +5 -0
  177. package/dist/internal/icons/ArrowDown.svelte +14 -0
  178. package/dist/internal/icons/ArrowDown.svelte.d.ts +4 -0
  179. package/dist/internal/icons/ArrowUp.svelte +14 -0
  180. package/dist/internal/icons/ArrowUp.svelte.d.ts +4 -0
  181. package/dist/internal/icons/Calendar.svelte +16 -0
  182. package/dist/internal/icons/Calendar.svelte.d.ts +4 -0
  183. package/dist/internal/icons/Check.svelte +8 -0
  184. package/dist/internal/icons/Check.svelte.d.ts +4 -0
  185. package/dist/internal/icons/ChevronDown.svelte +8 -0
  186. package/dist/internal/icons/ChevronDown.svelte.d.ts +4 -0
  187. package/dist/internal/icons/ChevronLeft.svelte +8 -0
  188. package/dist/internal/icons/ChevronLeft.svelte.d.ts +4 -0
  189. package/dist/internal/icons/ChevronRight.svelte +8 -0
  190. package/dist/internal/icons/ChevronRight.svelte.d.ts +4 -0
  191. package/dist/internal/icons/ChevronUp.svelte +8 -0
  192. package/dist/internal/icons/ChevronUp.svelte.d.ts +4 -0
  193. package/dist/internal/icons/ChevronsUpDown.svelte +14 -0
  194. package/dist/internal/icons/ChevronsUpDown.svelte.d.ts +4 -0
  195. package/dist/internal/icons/Clock.svelte +14 -0
  196. package/dist/internal/icons/Clock.svelte.d.ts +4 -0
  197. package/dist/internal/icons/Ellipsis.svelte +15 -0
  198. package/dist/internal/icons/Ellipsis.svelte.d.ts +4 -0
  199. package/dist/internal/icons/Icon.svelte +33 -0
  200. package/dist/internal/icons/Icon.svelte.d.ts +8 -0
  201. package/dist/internal/icons/Minus.svelte +8 -0
  202. package/dist/internal/icons/Minus.svelte.d.ts +4 -0
  203. package/dist/internal/icons/PanelLeft.svelte +14 -0
  204. package/dist/internal/icons/PanelLeft.svelte.d.ts +4 -0
  205. package/dist/internal/icons/Plus.svelte +14 -0
  206. package/dist/internal/icons/Plus.svelte.d.ts +4 -0
  207. package/dist/internal/icons/User.svelte +14 -0
  208. package/dist/internal/icons/User.svelte.d.ts +4 -0
  209. package/dist/internal/icons/X.svelte +14 -0
  210. package/dist/internal/icons/X.svelte.d.ts +4 -0
  211. package/dist/styles/tokens.css +39 -41
  212. package/package.json +5 -10
  213. package/dist/icons.d.ts +0 -2
  214. package/dist/icons.js +0 -4
@@ -16,11 +16,13 @@
16
16
 
17
17
  <script lang="ts">
18
18
  import type { Snippet } from 'svelte';
19
+ import type { HTMLAttributes } from 'svelte/elements';
20
+ import { prefersReducedMotion } from 'svelte/motion';
19
21
  import { scale } from 'svelte/transition';
20
22
 
21
23
  type Side = 'top' | 'right' | 'bottom' | 'left';
22
24
 
23
- type Props = {
25
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'title'> & {
24
26
  /** Whether the popover is open. Bindable. */
25
27
  open?: boolean;
26
28
  /** Opens on hover and keyboard focus instead of on click, like a hover card. */
@@ -36,6 +38,8 @@
36
38
  /** Renders the trigger. Spread the given props onto it, e.g. `<Button {...props} />`. */
37
39
  trigger: Snippet<[PopoverTriggerProps]>;
38
40
  children?: Snippet;
41
+ /** The popup element, only set while open. Bindable. */
42
+ ref?: HTMLDivElement | null;
39
43
  };
40
44
 
41
45
  let {
@@ -47,7 +51,10 @@
47
51
  title,
48
52
  description,
49
53
  trigger,
50
- children
54
+ children,
55
+ class: className,
56
+ ref = $bindable(null),
57
+ ...rest
51
58
  }: Props = $props();
52
59
 
53
60
  const id = $props.id();
@@ -58,7 +65,6 @@
58
65
 
59
66
  let wrapperEl: HTMLSpanElement;
60
67
  let triggerEl: HTMLElement | undefined;
61
- let contentEl: HTMLDivElement | undefined = $state();
62
68
  let placement: Side = $state('bottom');
63
69
  let position = $state({ top: 0, left: 0 });
64
70
  let timer: ReturnType<typeof setTimeout> | undefined;
@@ -126,9 +132,9 @@
126
132
  }
127
133
 
128
134
  function place() {
129
- if (!triggerEl || !contentEl) return;
135
+ if (!triggerEl || !ref) return;
130
136
  const rect = triggerEl.getBoundingClientRect();
131
- const { offsetWidth: width, offsetHeight: height } = contentEl;
137
+ const { offsetWidth: width, offsetHeight: height } = ref;
132
138
  const s =
133
139
  fits(rect, width, height, side) || !fits(rect, width, height, opposite[side])
134
140
  ? side
@@ -190,23 +196,29 @@
190
196
 
191
197
  {#if open}
192
198
  <div
199
+ {...rest}
193
200
  id="{id}-popover"
194
201
  role={hover ? undefined : 'dialog'}
195
202
  aria-labelledby={title ? `${id}-title` : undefined}
196
203
  aria-describedby={description ? `${id}-description` : undefined}
197
204
  tabindex="-1"
198
205
  popover="manual"
199
- class={['popover-content', `popover-content--${placement}`, `popover-content--${align}`]}
206
+ class={[
207
+ 'popover-content',
208
+ `popover-content--${placement}`,
209
+ `popover-content--${align}`,
210
+ className
211
+ ]}
200
212
  style:top="{position.top}px"
201
213
  style:left="{position.left}px"
202
- bind:this={contentEl}
214
+ bind:this={ref}
203
215
  onpointerenter={() => hover && clearTimeout(timer)}
204
216
  onpointerleave={() => hover && schedule(false, closeDelay)}
205
217
  onfocusout={(event) => {
206
218
  if (leftWrapper(event)) close();
207
219
  }}
208
220
  {@attach popover}
209
- transition:scale={{ duration: 140, start: 0.95 }}
221
+ transition:scale={{ duration: prefersReducedMotion.current ? 0 : 140, start: 0.95 }}
210
222
  >
211
223
  {#if title}
212
224
  <h3 id="{id}-title" class="popover-title">{title}</h3>
@@ -222,74 +234,82 @@
222
234
  </span>
223
235
 
224
236
  <style>
225
- .popover {
226
- display: inline-flex;
227
- height: fit-content;
228
- }
237
+ @layer svelte-ui {
238
+ .popover {
239
+ display: inline-flex;
240
+ height: fit-content;
241
+ }
229
242
 
230
- .popover-content {
231
- position: fixed;
232
- inset: auto;
233
- box-sizing: border-box;
234
- width: 288px;
235
- max-width: calc(100vw - 16px);
236
- margin: 0;
237
- padding: 16px;
238
- overflow: visible;
239
- font-size: 13px;
240
- line-height: 1.5;
241
- background-color: var(--color-bg);
242
- color: var(--color-text);
243
- border: 1px solid var(--color-border);
244
- border-radius: 12px;
245
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
246
- outline: none;
247
- }
243
+ .popover-content {
244
+ position: fixed;
245
+ inset: auto;
246
+ box-sizing: border-box;
247
+ width: 288px;
248
+ max-width: calc(100vw - 16px);
249
+ margin: 0;
250
+ padding: 16px;
251
+ overflow: visible;
252
+ font-size: 13px;
253
+ line-height: 1.5;
254
+ background-color: var(--color-bg);
255
+ color: var(--color-text);
256
+ border: 1px solid var(--color-border);
257
+ border-radius: 12px;
258
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
259
+ outline: none;
260
+ }
248
261
 
249
- .popover-title {
250
- margin: 0;
251
- font-size: 14px;
252
- font-weight: 600;
253
- }
262
+ .popover-title {
263
+ margin: 0;
264
+ font-size: 14px;
265
+ font-weight: 600;
266
+ }
254
267
 
255
- .popover-description {
256
- margin: 4px 0 0;
257
- color: var(--color-text-muted);
258
- }
268
+ .popover-description {
269
+ margin: 4px 0 0;
270
+ color: var(--color-text-muted);
271
+ }
259
272
 
260
- :is(.popover-title, .popover-description) + .popover-body {
261
- margin-top: 14px;
262
- }
273
+ :is(.popover-title, .popover-description) + .popover-body {
274
+ margin-top: 14px;
275
+ }
263
276
 
264
- .popover-content--bottom {
265
- transform-origin: top center;
266
- }
277
+ .popover-content--bottom {
278
+ transform-origin: top center;
279
+ }
267
280
 
268
- .popover-content--top {
269
- transform-origin: bottom center;
270
- }
281
+ .popover-content--top {
282
+ transform-origin: bottom center;
283
+ }
271
284
 
272
- .popover-content--left {
273
- transform-origin: center right;
274
- }
285
+ .popover-content--left {
286
+ transform-origin: center right;
287
+ }
275
288
 
276
- .popover-content--right {
277
- transform-origin: center left;
278
- }
289
+ .popover-content--right {
290
+ transform-origin: center left;
291
+ }
279
292
 
280
- .popover-content--bottom.popover-content--start {
281
- transform-origin: top left;
282
- }
293
+ .popover-content--bottom.popover-content--start {
294
+ transform-origin: top left;
295
+ }
283
296
 
284
- .popover-content--bottom.popover-content--end {
285
- transform-origin: top right;
286
- }
297
+ .popover-content--bottom.popover-content--end {
298
+ transform-origin: top right;
299
+ }
287
300
 
288
- .popover-content--top.popover-content--start {
289
- transform-origin: bottom left;
290
- }
301
+ .popover-content--top.popover-content--start {
302
+ transform-origin: bottom left;
303
+ }
291
304
 
292
- .popover-content--top.popover-content--end {
293
- transform-origin: bottom right;
305
+ .popover-content--top.popover-content--end {
306
+ transform-origin: bottom right;
307
+ }
308
+
309
+ @media (forced-colors: active) {
310
+ .popover-content {
311
+ border-color: CanvasText;
312
+ }
313
+ }
294
314
  }
295
315
  </style>
@@ -11,8 +11,9 @@ export type PopoverTriggerProps = {
11
11
  [key: symbol]: Attachment<HTMLElement>;
12
12
  };
13
13
  import type { Snippet } from 'svelte';
14
+ import type { HTMLAttributes } from 'svelte/elements';
14
15
  type Side = 'top' | 'right' | 'bottom' | 'left';
15
- type Props = {
16
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'title'> & {
16
17
  /** Whether the popover is open. Bindable. */
17
18
  open?: boolean;
18
19
  /** Opens on hover and keyboard focus instead of on click, like a hover card. */
@@ -28,7 +29,9 @@ type Props = {
28
29
  /** Renders the trigger. Spread the given props onto it, e.g. `<Button {...props} />`. */
29
30
  trigger: Snippet<[PopoverTriggerProps]>;
30
31
  children?: Snippet;
32
+ /** The popup element, only set while open. Bindable. */
33
+ ref?: HTMLDivElement | null;
31
34
  };
32
- declare const Popover: import("svelte").Component<Props, {}, "open">;
35
+ declare const Popover: import("svelte").Component<Props, {}, "ref" | "open">;
33
36
  type Popover = ReturnType<typeof Popover>;
34
37
  export default Popover;
@@ -1,8 +1,9 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
- import type { LucideIcon } from '@lucide/svelte';
3
+ import type { HTMLAnchorAttributes } from 'svelte/elements';
4
+ import type { IconComponent } from '../../icon.js';
4
5
 
5
- type Props = {
6
+ type Props = Omit<HTMLAnchorAttributes, 'children' | 'title' | 'href' | 'media'> & {
6
7
  /** Makes the whole card a link. */
7
8
  href?: string;
8
9
  /** Bold lead-in before the description, followed by a colon. */
@@ -10,12 +11,14 @@
10
11
  description?: string;
11
12
  /** Muted text below, e.g. a date. */
12
13
  meta?: string;
13
- /** Any Lucide icon (import from `svelte-ui/icons`), shown in the top-left corner of the media. */
14
- icon?: LucideIcon;
14
+ /** Any icon component, e.g. from `@lucide/svelte`, shown in the top-left corner of the media. */
15
+ icon?: IconComponent;
15
16
  orientation?: 'vertical' | 'horizontal';
16
17
  /** Square media that fills the frame, e.g. a MeshGradient or an `<img>`. */
17
18
  media: Snippet;
18
- class?: string;
19
+ /** Custom description content instead of `description`. */
20
+ children?: Snippet;
21
+ ref?: HTMLElement | null;
19
22
  };
20
23
 
21
24
  let {
@@ -26,12 +29,17 @@
26
29
  icon: Icon,
27
30
  orientation = 'vertical',
28
31
  media,
29
- class: className
32
+ children,
33
+ class: className,
34
+ ref = $bindable(null),
35
+ ...rest
30
36
  }: Props = $props();
31
37
  </script>
32
38
 
33
39
  <svelte:element
34
40
  this={href ? 'a' : 'div'}
41
+ {...rest}
42
+ bind:this={ref}
35
43
  {href}
36
44
  class={['preview-card', `preview-card--${orientation}`, className]}
37
45
  >
@@ -43,8 +51,8 @@
43
51
  </div>
44
52
  <div class="preview-card-info">
45
53
  <p class="preview-card-headline">
46
- <span class="preview-card-title">{title}{description ? ':' : ''}</span>
47
- {description}
54
+ <span class="preview-card-title">{title}{description || children ? ':' : ''}</span>
55
+ {#if children}{@render children()}{:else}{description}{/if}
48
56
  </p>
49
57
  {#if meta}
50
58
  <span class="preview-card-meta">{meta}</span>
@@ -53,114 +61,124 @@
53
61
  </svelte:element>
54
62
 
55
63
  <style>
56
- .preview-card {
57
- display: flex;
58
- gap: 20px;
59
- color: var(--color-text);
60
- text-decoration: none;
61
- border-radius: 14px;
62
- outline: none;
63
- }
64
+ @layer svelte-ui {
65
+ .preview-card {
66
+ display: flex;
67
+ gap: 20px;
68
+ color: var(--color-text);
69
+ text-decoration: none;
70
+ border-radius: 10px;
71
+ outline: none;
72
+ }
64
73
 
65
- a.preview-card:focus-visible {
66
- outline: 2px solid var(--color-accent);
67
- outline-offset: 2px;
68
- }
74
+ a.preview-card:focus-visible {
75
+ outline: 2px solid var(--color-accent);
76
+ outline-offset: 2px;
77
+ }
69
78
 
70
- .preview-card--vertical {
71
- flex-direction: column;
72
- }
79
+ .preview-card--vertical {
80
+ flex-direction: column;
81
+ max-width: 380px;
82
+ }
73
83
 
74
- .preview-card--horizontal {
75
- align-items: center;
76
- }
84
+ .preview-card--horizontal {
85
+ align-items: center;
86
+ }
77
87
 
78
- .preview-card-media {
79
- position: relative;
80
- flex-shrink: 0;
81
- aspect-ratio: 1;
82
- overflow: hidden;
83
- border-radius: 14px;
84
- }
88
+ .preview-card-media {
89
+ position: relative;
90
+ flex-shrink: 0;
91
+ aspect-ratio: 1;
92
+ overflow: hidden;
93
+ border-radius: 10px;
94
+ }
85
95
 
86
- .preview-card--horizontal .preview-card-media {
87
- width: 140px;
88
- }
96
+ .preview-card--horizontal .preview-card-media {
97
+ width: 140px;
98
+ }
89
99
 
90
- .preview-card-media > :global(:not(.preview-card-icon)) {
91
- position: absolute;
92
- inset: 0;
93
- width: 100%;
94
- height: 100%;
95
- object-fit: cover;
96
- border-radius: inherit;
97
- transition: transform 300ms ease;
98
- }
100
+ .preview-card-media > :global(:not(.preview-card-icon)) {
101
+ position: absolute;
102
+ inset: 0;
103
+ width: 100%;
104
+ height: 100%;
105
+ object-fit: cover;
106
+ border-radius: inherit;
107
+ transition: transform 300ms ease;
108
+ }
99
109
 
100
- a.preview-card:hover .preview-card-media > :global(:not(.preview-card-icon)) {
101
- transform: scale(1.05);
102
- }
110
+ a.preview-card:hover .preview-card-media > :global(:not(.preview-card-icon)) {
111
+ transform: scale(1.05);
112
+ }
103
113
 
104
- .preview-card-icon {
105
- position: absolute;
106
- top: 0;
107
- left: 0;
108
- z-index: 1;
109
- display: flex;
110
- align-items: center;
111
- justify-content: center;
112
- width: 40px;
113
- height: 40px;
114
- color: var(--color-base);
115
- background-color: var(--color-ink);
116
- border-radius: 14px 0 0 0;
117
- }
114
+ .preview-card-icon {
115
+ position: absolute;
116
+ top: 0;
117
+ left: 0;
118
+ z-index: 1;
119
+ display: flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ width: 40px;
123
+ height: 40px;
124
+ color: var(--color-base);
125
+ background-color: var(--color-ink);
126
+ border-radius: 10px 0 0 0;
127
+ }
118
128
 
119
- .preview-card-icon :global(svg) {
120
- width: 20px;
121
- height: 20px;
122
- }
129
+ .preview-card-icon :global(svg) {
130
+ width: 20px;
131
+ height: 20px;
132
+ }
123
133
 
124
- .preview-card--horizontal .preview-card-icon {
125
- width: 32px;
126
- height: 32px;
127
- }
134
+ .preview-card--horizontal .preview-card-icon {
135
+ width: 32px;
136
+ height: 32px;
137
+ }
128
138
 
129
- .preview-card--horizontal .preview-card-icon :global(svg) {
130
- width: 16px;
131
- height: 16px;
132
- }
139
+ .preview-card--horizontal .preview-card-icon :global(svg) {
140
+ width: 16px;
141
+ height: 16px;
142
+ }
133
143
 
134
- .preview-card-info {
135
- display: flex;
136
- flex-direction: column;
137
- gap: 8px;
138
- min-width: 0;
139
- }
144
+ .preview-card-info {
145
+ display: flex;
146
+ flex-direction: column;
147
+ gap: 8px;
148
+ min-width: 0;
149
+ }
140
150
 
141
- .preview-card-headline {
142
- margin: 0;
143
- font-size: 16px;
144
- }
151
+ .preview-card-headline {
152
+ margin: 0;
153
+ font-size: 16px;
154
+ }
145
155
 
146
- .preview-card-title {
147
- font-weight: 600;
148
- }
156
+ .preview-card-title {
157
+ font-weight: 600;
158
+ }
149
159
 
150
- .preview-card-meta {
151
- font-size: 14px;
152
- color: var(--color-text-muted);
153
- }
160
+ .preview-card-meta {
161
+ font-size: 14px;
162
+ color: var(--color-text-muted);
163
+ }
154
164
 
155
- @media (min-width: 640px) {
156
- .preview-card--horizontal .preview-card-media {
157
- width: 168px;
165
+ @media (min-width: 640px) {
166
+ .preview-card--horizontal .preview-card-media {
167
+ width: 168px;
168
+ }
169
+ }
170
+
171
+ @media (prefers-reduced-motion: reduce) {
172
+ .preview-card-media > :global(*) {
173
+ transition: none;
174
+ }
158
175
  }
159
- }
160
176
 
161
- @media (prefers-reduced-motion: reduce) {
162
- .preview-card-media > :global(*) {
163
- transition: none;
177
+ @media (forced-colors: active) {
178
+ .preview-card-icon {
179
+ outline: 1px solid CanvasText;
180
+ outline-offset: -1px;
181
+ }
164
182
  }
165
183
  }
166
184
  </style>
@@ -1,6 +1,7 @@
1
1
  import type { Snippet } from 'svelte';
2
- import type { LucideIcon } from '@lucide/svelte';
3
- type Props = {
2
+ import type { HTMLAnchorAttributes } from 'svelte/elements';
3
+ import type { IconComponent } from '../../icon.js';
4
+ type Props = Omit<HTMLAnchorAttributes, 'children' | 'title' | 'href' | 'media'> & {
4
5
  /** Makes the whole card a link. */
5
6
  href?: string;
6
7
  /** Bold lead-in before the description, followed by a colon. */
@@ -8,13 +9,15 @@ type Props = {
8
9
  description?: string;
9
10
  /** Muted text below, e.g. a date. */
10
11
  meta?: string;
11
- /** Any Lucide icon (import from `svelte-ui/icons`), shown in the top-left corner of the media. */
12
- icon?: LucideIcon;
12
+ /** Any icon component, e.g. from `@lucide/svelte`, shown in the top-left corner of the media. */
13
+ icon?: IconComponent;
13
14
  orientation?: 'vertical' | 'horizontal';
14
15
  /** Square media that fills the frame, e.g. a MeshGradient or an `<img>`. */
15
16
  media: Snippet;
16
- class?: string;
17
+ /** Custom description content instead of `description`. */
18
+ children?: Snippet;
19
+ ref?: HTMLElement | null;
17
20
  };
18
- declare const PreviewCard: import("svelte").Component<Props, {}, "">;
21
+ declare const PreviewCard: import("svelte").Component<Props, {}, "ref">;
19
22
  type PreviewCard = ReturnType<typeof PreviewCard>;
20
23
  export default PreviewCard;
@@ -8,15 +8,25 @@
8
8
  size?: 'sm' | 'md';
9
9
  /** Accessible name, e.g. what is loading. */
10
10
  label?: string;
11
+ ref?: HTMLDivElement | null;
11
12
  };
12
13
 
13
- let { value, max = 100, size = 'md', label, class: className, ...rest }: Props = $props();
14
+ let {
15
+ value,
16
+ max = 100,
17
+ size = 'md',
18
+ label,
19
+ class: className,
20
+ ref = $bindable(null),
21
+ ...rest
22
+ }: Props = $props();
14
23
 
15
24
  let fraction = $derived(value === undefined ? undefined : Math.min(Math.max(value / max, 0), 1));
16
25
  </script>
17
26
 
18
27
  <div
19
28
  {...rest}
29
+ bind:this={ref}
20
30
  role="progressbar"
21
31
  aria-label={label}
22
32
  aria-valuemin={0}
@@ -36,53 +46,66 @@
36
46
  </div>
37
47
 
38
48
  <style>
39
- .progress {
40
- width: 100%;
41
- overflow: hidden;
42
- background-color: var(--color-surface);
43
- border-radius: 999px;
44
- }
45
-
46
- .progress--sm {
47
- height: 4px;
48
- }
49
+ @layer svelte-ui {
50
+ .progress {
51
+ width: 100%;
52
+ overflow: hidden;
53
+ background-color: var(--color-surface);
54
+ border-radius: 999px;
55
+ }
49
56
 
50
- .progress--md {
51
- height: 8px;
52
- }
57
+ .progress--sm {
58
+ height: 4px;
59
+ }
53
60
 
54
- .progress-bar {
55
- height: 100%;
56
- background-color: var(--color-accent);
57
- border-radius: inherit;
58
- transition: width 300ms ease;
59
- }
61
+ .progress--md {
62
+ height: 8px;
63
+ }
60
64
 
61
- .progress--indeterminate .progress-bar {
62
- width: 40%;
63
- animation: progress-slide 1.4s ease-in-out infinite;
64
- }
65
+ .progress-bar {
66
+ height: 100%;
67
+ background-color: var(--color-accent);
68
+ border-radius: inherit;
69
+ transition: width 300ms ease;
70
+ }
65
71
 
66
- @keyframes progress-slide {
67
- from {
68
- translate: -100% 0;
72
+ .progress--indeterminate .progress-bar {
73
+ width: 40%;
74
+ animation: progress-slide 1.4s ease-in-out infinite;
69
75
  }
70
76
 
71
- to {
72
- translate: 250% 0;
77
+ @keyframes progress-slide {
78
+ from {
79
+ translate: -100% 0;
80
+ }
81
+
82
+ to {
83
+ translate: 250% 0;
84
+ }
73
85
  }
74
- }
75
86
 
76
- @media (prefers-reduced-motion: reduce) {
77
- .progress-bar {
78
- transition: none;
87
+ @media (prefers-reduced-motion: reduce) {
88
+ .progress-bar {
89
+ transition: none;
90
+ }
91
+
92
+ /* A full, dimmed bar instead of the movement */
93
+ .progress--indeterminate .progress-bar {
94
+ width: 100%;
95
+ opacity: 0.4;
96
+ animation: none;
97
+ }
79
98
  }
80
99
 
81
- /* A full, dimmed bar instead of the movement */
82
- .progress--indeterminate .progress-bar {
83
- width: 100%;
84
- opacity: 0.4;
85
- animation: none;
100
+ @media (forced-colors: active) {
101
+ .progress {
102
+ border: 1px solid CanvasText;
103
+ }
104
+
105
+ .progress-bar {
106
+ forced-color-adjust: none;
107
+ background-color: Highlight;
108
+ }
86
109
  }
87
110
  }
88
111
  </style>