zabi-components 5.0.22 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/README.md +139 -1478
  2. package/THEME.md +473 -0
  3. package/THEMING.md +248 -0
  4. package/dist/atoms/ActionPanel.svelte +116 -0
  5. package/dist/atoms/ActionPanel.svelte.d.ts +19 -0
  6. package/dist/atoms/Badge.svelte +1 -1
  7. package/dist/atoms/Badge.svelte.d.ts +1 -1
  8. package/dist/atoms/Button.svelte +35 -16
  9. package/dist/atoms/Button.svelte.d.ts +3 -1
  10. package/dist/atoms/Card.svelte +49 -67
  11. package/dist/atoms/Card.svelte.d.ts +4 -4
  12. package/dist/atoms/CardHeader.svelte +15 -2
  13. package/dist/atoms/CardHeader.svelte.d.ts +5 -0
  14. package/dist/atoms/Checkbox.svelte +50 -76
  15. package/dist/atoms/Checkbox.svelte.d.ts +7 -2
  16. package/dist/atoms/CodeBlock.svelte +20 -34
  17. package/dist/atoms/CodeBlock.svelte.d.ts +1 -0
  18. package/dist/atoms/ColorPicker.svelte +3 -7
  19. package/dist/atoms/Container.svelte +41 -0
  20. package/dist/atoms/Container.svelte.d.ts +12 -0
  21. package/dist/atoms/Divider.svelte +68 -0
  22. package/dist/atoms/Divider.svelte.d.ts +13 -0
  23. package/dist/atoms/FeatureCard.svelte +35 -50
  24. package/dist/atoms/FeatureCard.svelte.d.ts +9 -4
  25. package/dist/atoms/IconButton.svelte +33 -24
  26. package/dist/atoms/IconButton.svelte.d.ts +4 -8
  27. package/dist/atoms/Input.svelte +58 -26
  28. package/dist/atoms/Input.svelte.d.ts +6 -1
  29. package/dist/atoms/List.svelte +2 -7
  30. package/dist/atoms/List.svelte.d.ts +1 -6
  31. package/dist/atoms/ListItem.svelte +54 -65
  32. package/dist/atoms/ListItem.svelte.d.ts +7 -13
  33. package/dist/atoms/ListItemLeading.svelte +19 -0
  34. package/dist/atoms/ListItemLeading.svelte.d.ts +9 -0
  35. package/dist/atoms/Progress.svelte +2 -2
  36. package/dist/atoms/Radio.svelte +51 -0
  37. package/dist/atoms/Radio.svelte.d.ts +14 -0
  38. package/dist/atoms/Select.svelte +28 -33
  39. package/dist/atoms/Select.svelte.d.ts +0 -10
  40. package/dist/atoms/SelectionControl.svelte +98 -0
  41. package/dist/atoms/SelectionControl.svelte.d.ts +26 -0
  42. package/dist/atoms/Skeleton.svelte +39 -15
  43. package/dist/atoms/Skeleton.svelte.d.ts +7 -3
  44. package/dist/atoms/Table.svelte +27 -0
  45. package/dist/atoms/Table.svelte.d.ts +10 -0
  46. package/dist/atoms/Text.svelte +46 -0
  47. package/dist/atoms/Text.svelte.d.ts +13 -0
  48. package/dist/atoms/Textarea.svelte +74 -53
  49. package/dist/atoms/Textarea.svelte.d.ts +6 -2
  50. package/dist/atoms/ThemeToggle.svelte +35 -24
  51. package/dist/atoms/ThemeToggle.svelte.d.ts +1 -0
  52. package/dist/atoms/Toast.svelte +55 -31
  53. package/dist/atoms/Toast.svelte.d.ts +4 -1
  54. package/dist/atoms/Toggle.svelte +37 -22
  55. package/dist/atoms/Toggle.svelte.d.ts +2 -1
  56. package/dist/atoms/Tooltip.svelte +90 -11
  57. package/dist/atoms/index.d.ts +6 -0
  58. package/dist/atoms/index.js +6 -0
  59. package/dist/atoms/selection-control.styles.d.ts +25 -0
  60. package/dist/atoms/selection-control.styles.js +30 -0
  61. package/dist/components/atoms/index.d.ts +6 -0
  62. package/dist/components/atoms/index.d.ts.map +1 -1
  63. package/dist/components/atoms/selection-control.styles.d.ts +26 -0
  64. package/dist/components/atoms/selection-control.styles.d.ts.map +1 -0
  65. package/dist/components/index.d.ts +42 -7
  66. package/dist/components/index.d.ts.map +1 -1
  67. package/dist/components/molecules/index.d.ts +10 -0
  68. package/dist/components/molecules/index.d.ts.map +1 -1
  69. package/dist/components/molecules/navigation-menu-context.d.ts +11 -0
  70. package/dist/components/molecules/navigation-menu-context.d.ts.map +1 -1
  71. package/dist/components/molecules/toast-store.d.ts +39 -0
  72. package/dist/components/molecules/toast-store.d.ts.map +1 -0
  73. package/dist/components/organisms/index.d.ts +3 -3
  74. package/dist/components/organisms/index.d.ts.map +1 -1
  75. package/dist/components/types/page.types.d.ts +69 -0
  76. package/dist/components/types/page.types.d.ts.map +1 -0
  77. package/dist/components/types/page.types.js +1 -0
  78. package/dist/components/types/variants.d.ts +64 -0
  79. package/dist/components/types/variants.d.ts.map +1 -0
  80. package/dist/components/types/variants.js +44 -0
  81. package/dist/components/util/fixed-sidebar-flyout.d.ts +23 -0
  82. package/dist/components/util/fixed-sidebar-flyout.d.ts.map +1 -0
  83. package/dist/components/util/focus-utils.d.ts +12 -0
  84. package/dist/components/util/focus-utils.d.ts.map +1 -0
  85. package/dist/components/util/ssr-safe.d.ts +17 -0
  86. package/dist/components/util/ssr-safe.d.ts.map +1 -0
  87. package/dist/index.d.ts +42 -7
  88. package/dist/index.js +45 -10
  89. package/dist/lib/layout-width-tokens.d.ts +17 -0
  90. package/dist/lib/layout-width-tokens.d.ts.map +1 -0
  91. package/dist/lib/showcase/component-docs/Button.d.ts +3 -0
  92. package/dist/lib/showcase/component-docs/Button.d.ts.map +1 -0
  93. package/dist/lib/showcase/component-docs/Input.d.ts +3 -0
  94. package/dist/lib/showcase/component-docs/Input.d.ts.map +1 -0
  95. package/dist/lib/showcase/component-docs/List.d.ts +3 -0
  96. package/dist/lib/showcase/component-docs/List.d.ts.map +1 -0
  97. package/dist/lib/showcase/component-docs/Modal.d.ts +3 -0
  98. package/dist/lib/showcase/component-docs/Modal.d.ts.map +1 -0
  99. package/dist/lib/showcase/component-docs/Radio.d.ts +3 -0
  100. package/dist/lib/showcase/component-docs/Radio.d.ts.map +1 -0
  101. package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts +3 -0
  102. package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts.map +1 -0
  103. package/dist/lib/showcase/component-docs/Skeleton.d.ts +3 -0
  104. package/dist/lib/showcase/component-docs/Skeleton.d.ts.map +1 -0
  105. package/dist/lib/showcase/component-docs/_shared.d.ts +12 -0
  106. package/dist/lib/showcase/component-docs/_shared.d.ts.map +1 -0
  107. package/dist/lib/showcase/component-docs/index.d.ts +4 -0
  108. package/dist/lib/showcase/component-docs/index.d.ts.map +1 -0
  109. package/dist/lib/showcase/components-catalog.d.ts +3 -0
  110. package/dist/lib/showcase/components-catalog.d.ts.map +1 -0
  111. package/dist/lib/showcase/components-showcase-constants.d.ts +89 -0
  112. package/dist/lib/showcase/components-showcase-constants.d.ts.map +1 -0
  113. package/dist/lib/showcase/docs-sidebar-helpers.d.ts +15 -0
  114. package/dist/lib/showcase/docs-sidebar-helpers.d.ts.map +1 -0
  115. package/dist/molecules/Alert.svelte +50 -23
  116. package/dist/molecules/Alert.svelte.d.ts +1 -1
  117. package/dist/molecules/ComponentDemo.svelte +54 -51
  118. package/dist/molecules/ContactForm.svelte +70 -65
  119. package/dist/molecules/Dropdown.svelte +99 -108
  120. package/dist/molecules/Dropdown.svelte.d.ts +12 -6
  121. package/dist/molecules/EmptyState.svelte +44 -0
  122. package/dist/molecules/EmptyState.svelte.d.ts +11 -0
  123. package/dist/molecules/FormField.svelte +89 -0
  124. package/dist/molecules/FormField.svelte.d.ts +23 -0
  125. package/dist/molecules/Header.svelte +41 -0
  126. package/dist/molecules/Header.svelte.d.ts +10 -0
  127. package/dist/molecules/ImageUpload.svelte +1 -1
  128. package/dist/molecules/Modal.svelte +39 -26
  129. package/dist/molecules/Modal.svelte.d.ts +4 -2
  130. package/dist/molecules/NavigationMenu.svelte +16 -0
  131. package/dist/molecules/NavigationMenu.svelte.d.ts +2 -0
  132. package/dist/molecules/NavigationMenuContent.svelte +9 -1
  133. package/dist/molecules/NavigationMenuLink.svelte +1 -1
  134. package/dist/molecules/NavigationMenuTrigger.svelte +9 -4
  135. package/dist/molecules/Page.svelte +16 -0
  136. package/dist/molecules/Page.svelte.d.ts +8 -0
  137. package/dist/molecules/PropsTable.svelte +61 -0
  138. package/dist/molecules/PropsTable.svelte.d.ts +8 -0
  139. package/dist/molecules/RadioGroup.svelte +197 -0
  140. package/dist/molecules/RadioGroup.svelte.d.ts +20 -0
  141. package/dist/molecules/Section.svelte +14 -23
  142. package/dist/molecules/Section.svelte.d.ts +2 -11
  143. package/dist/molecules/SidebarBrandHeader.svelte +68 -0
  144. package/dist/molecules/SidebarBrandHeader.svelte.d.ts +10 -0
  145. package/dist/molecules/SidebarFooter.svelte +119 -0
  146. package/dist/molecules/SidebarFooter.svelte.d.ts +23 -0
  147. package/dist/molecules/SidebarNavSection.svelte +49 -0
  148. package/dist/molecules/SidebarNavSection.svelte.d.ts +14 -0
  149. package/dist/molecules/SlideUp.svelte +21 -16
  150. package/dist/molecules/SlideUp.svelte.d.ts +1 -1
  151. package/dist/molecules/Tabs.svelte +6 -11
  152. package/dist/molecules/Toaster.svelte +21 -0
  153. package/dist/molecules/Toaster.svelte.d.ts +6 -0
  154. package/dist/molecules/ToasterToast.svelte +217 -0
  155. package/dist/molecules/ToasterToast.svelte.d.ts +7 -0
  156. package/dist/molecules/index.d.ts +10 -0
  157. package/dist/molecules/index.js +10 -0
  158. package/dist/molecules/navigation-menu-context.d.ts +11 -0
  159. package/dist/molecules/navigation-menu-context.js +18 -0
  160. package/dist/molecules/toast-store.d.ts +38 -0
  161. package/dist/molecules/toast-store.js +34 -0
  162. package/dist/organisms/SidebarAccountPanel.svelte +118 -0
  163. package/dist/organisms/SidebarAccountPanel.svelte.d.ts +20 -0
  164. package/dist/organisms/SidebarNavigation.svelte +286 -212
  165. package/dist/organisms/SidebarNavigation.svelte.d.ts +22 -1
  166. package/dist/organisms/{SidebarProjectPanel.svelte → SidebarPanel.svelte} +67 -34
  167. package/dist/organisms/{SidebarProjectPanel.svelte.d.ts → SidebarPanel.svelte.d.ts} +9 -6
  168. package/dist/organisms/TopNavbar.svelte +214 -0
  169. package/dist/organisms/TopNavbar.svelte.d.ts +35 -0
  170. package/dist/organisms/index.d.ts +3 -3
  171. package/dist/organisms/index.js +3 -3
  172. package/dist/routes/lib/focus-utils.d.ts +7 -18
  173. package/dist/routes/lib/focus-utils.ts +32 -34
  174. package/dist/routes/lib/ssr-safe.ts +5 -4
  175. package/dist/routes/lib/variant-utils.ts +48 -39
  176. package/dist/types/components.d.ts +187 -0
  177. package/dist/types/components.d.ts.map +1 -0
  178. package/dist/types/events.d.ts +200 -0
  179. package/dist/types/events.d.ts.map +1 -0
  180. package/dist/types/index.d.ts +171 -0
  181. package/dist/types/index.d.ts.map +1 -0
  182. package/dist/types/page.types.d.ts +3 -0
  183. package/dist/types/page.types.d.ts.map +1 -0
  184. package/dist/types/page.types.js +1 -0
  185. package/dist/types/page.types.ts +79 -0
  186. package/dist/types/variants.d.ts +3 -0
  187. package/dist/types/variants.d.ts.map +1 -0
  188. package/dist/types/variants.js +1 -0
  189. package/dist/types/variants.ts +75 -0
  190. package/dist/util/fixed-sidebar-flyout.d.ts +22 -0
  191. package/dist/util/fixed-sidebar-flyout.js +148 -0
  192. package/dist/util/focus-utils.d.ts +11 -0
  193. package/dist/util/focus-utils.js +94 -0
  194. package/dist/util/ssr-safe.d.ts +16 -0
  195. package/dist/util/ssr-safe.js +48 -0
  196. package/dist/zabi-components-colors.css +360 -324
  197. package/dist/zabi-components-theme-dark-only.css +164 -151
  198. package/dist/zabi-components-theme-dark.css +164 -151
  199. package/dist/zabi-components-theme-only.css +253 -230
  200. package/dist/zabi-components-theme.css +253 -230
  201. package/dist/zabi-components.css +1262 -241
  202. package/docs/theme-imports.md +112 -0
  203. package/package.json +32 -10
  204. package/dist/organisms/Navbar.svelte +0 -92
  205. package/dist/organisms/Navbar.svelte.d.ts +0 -14
  206. package/dist/organisms/Navigation.svelte +0 -119
  207. package/dist/organisms/Navigation.svelte.d.ts +0 -21
@@ -1,9 +1,16 @@
1
1
  <script lang="ts">
2
+ import Check from "@lucide/svelte/icons/check";
3
+ import Copy from "@lucide/svelte/icons/copy";
4
+ import IconButton from "./IconButton.svelte";
5
+
2
6
  interface Props {
7
+ /** Raw source; escaped unless `trustHtml` (e.g. highlighter HTML). */
3
8
  code: string;
4
9
  language?: string;
5
10
  className?: string;
6
11
  showCopyButton?: boolean;
12
+ /** If true, `{@html code}` — only trusted, sanitized input. */
13
+ trustHtml?: boolean;
7
14
  }
8
15
 
9
16
  let {
@@ -11,6 +18,7 @@
11
18
  language = "svelte",
12
19
  className = "",
13
20
  showCopyButton = true,
21
+ trustHtml = false,
14
22
  ...restProps
15
23
  } = $props();
16
24
 
@@ -30,11 +38,11 @@
30
38
  </script>
31
39
 
32
40
  <div
33
- class="code-block relative bg-base-900 rounded-lg overflow-hidden {className}"
41
+ class="code-block relative bg-surface-1 border border-border rounded-lg overflow-hidden {className}"
34
42
  {...restProps}
35
43
  >
36
44
  <div
37
- class="flex items-center justify-between px-4 py-2 bg-base-800 border-b border-base-700"
45
+ class="flex items-center justify-between px-4 py-2 bg-surface-2 border-b border-border"
38
46
  >
39
47
  <div class="flex items-center gap-2">
40
48
  <div class="w-3 h-3 rounded-full bg-red-500"></div>
@@ -42,46 +50,24 @@
42
50
  <div class="w-3 h-3 rounded-full bg-green-500"></div>
43
51
  </div>
44
52
  {#if showCopyButton}
45
- <button
53
+ <IconButton
54
+ variant="ghost"
55
+ size="sm"
56
+ label={copied ? "Code copied to clipboard" : "Copy code to clipboard"}
46
57
  onclick={copyToClipboard}
47
- class="flex items-center gap-2 px-3 py-1 text-xs text-base-300 hover:text-white hover:bg-base-700 rounded transition-colors duration-200"
48
- aria-label="Copy code to clipboard"
49
58
  >
50
59
  {#if copied}
51
- <svg
52
- class="w-4 h-4"
53
- fill="currentColor"
54
- viewBox="0 0 20 20"
55
- >
56
- <path
57
- fill-rule="evenodd"
58
- d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
59
- clip-rule="evenodd"
60
- />
61
- </svg>
62
- Copied!
60
+ <Check size={16} class="shrink-0" aria-hidden="true" />
63
61
  {:else}
64
- <svg
65
- class="w-4 h-4"
66
- fill="none"
67
- stroke="currentColor"
68
- viewBox="0 0 24 24"
69
- >
70
- <path
71
- stroke-linecap="round"
72
- stroke-linejoin="round"
73
- stroke-width="2"
74
- d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
75
- />
76
- </svg>
77
- Copy
62
+ <Copy size={16} class="shrink-0" aria-hidden="true" />
78
63
  {/if}
79
- </button>
64
+ </IconButton>
80
65
  {/if}
81
66
  </div>
82
67
 
83
- <pre class="p-4 overflow-x-auto text-sm text-base-100 leading-relaxed"><code
84
- class="language-{language}">{@html code}</code
68
+ <pre class="p-4 overflow-x-auto text-sm text-base-900 leading-relaxed"><code
69
+ class="language-{language}"
70
+ >{#if trustHtml}{@html code}{:else}{code}{/if}</code
85
71
  ></pre>
86
72
  </div>
87
73
 
@@ -3,6 +3,7 @@ declare const CodeBlock: import("svelte").Component<{
3
3
  language?: string;
4
4
  className?: string;
5
5
  showCopyButton?: boolean;
6
+ trustHtml?: boolean;
6
7
  } & Record<string, any>, {}, "">;
7
8
  type CodeBlock = ReturnType<typeof CodeBlock>;
8
9
  export default CodeBlock;
@@ -25,7 +25,7 @@
25
25
  let colorMap: HTMLCanvasElement | undefined;
26
26
  let colorMapContainer: HTMLDivElement | undefined;
27
27
 
28
- // HSL values (0-360, 0-100, 0-100)
28
+ // Working color in HSL (h: 0–360; s, l: 0–100).
29
29
  let hue = $state(0);
30
30
  let saturation = $state(100);
31
31
  let lightness = $state(50);
@@ -156,7 +156,6 @@
156
156
  const width = colorMap.width;
157
157
  const height = colorMap.height;
158
158
 
159
- // Draw saturation/lightness gradient
160
159
  for (let x = 0; x < width; x++) {
161
160
  for (let y = 0; y < height; y++) {
162
161
  const s = (x / width) * 100;
@@ -284,7 +283,7 @@
284
283
  type="button"
285
284
  onclick={togglePicker}
286
285
  {disabled}
287
- class="w-11 h-11 rounded-lg border-2 border-card shrink-0 cursor-pointer hover:ring-2 hover:ring-brand-500 transition-colors disabled:cursor-not-allowed disabled:opacity-50"
286
+ class="focus-ring w-11 h-11 rounded-lg border-2 border-card shrink-0 cursor-pointer hover:ring-2 hover:ring-border transition-colors disabled:cursor-not-allowed disabled:opacity-50"
288
287
  style="background-color: {displayColor};"
289
288
  aria-label="Open color picker"
290
289
  aria-expanded={isOpen}
@@ -293,12 +292,11 @@
293
292
  {#if isOpen}
294
293
  <div
295
294
  use:setPickerContainer
296
- class="absolute top-12 right-0 z-50 bg-input rounded-2xl shadow-lg p-4 w-80"
295
+ class="absolute top-12 right-0 z-50 border border-input-border bg-input rounded-2xl shadow-lg p-4 w-80"
297
296
  role="dialog"
298
297
  aria-label="Color picker"
299
298
  >
300
299
  <div class="space-y-4">
301
- <!-- Color Map -->
302
300
  <div
303
301
  use:setColorMapContainer
304
302
  class="relative w-full h-48 rounded-xl overflow-hidden cursor-crosshair"
@@ -315,14 +313,12 @@
315
313
  height={192}
316
314
  class="w-full h-full"
317
315
  ></canvas>
318
- <!-- Color indicator -->
319
316
  <div
320
317
  class="absolute w-4 h-4 border-2 border-white rounded-full shadow-lg pointer-events-none transform -translate-x-1/2 -translate-y-1/2"
321
318
  style="left: {pickerIndicatorX}; top: {pickerIndicatorY};"
322
319
  ></div>
323
320
  </div>
324
321
 
325
- <!-- Hue Slider -->
326
322
  <div class="space-y-2">
327
323
  <div
328
324
  class="relative h-6 rounded-lg overflow-hidden"
@@ -0,0 +1,41 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ type MaxWidth = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full';
5
+
6
+ interface Props {
7
+ as?: 'div' | 'section' | 'main' | 'article';
8
+ maxWidth?: MaxWidth;
9
+ padded?: boolean;
10
+ class?: string;
11
+ children?: Snippet;
12
+ }
13
+
14
+ let {
15
+ as: Tag = 'div',
16
+ maxWidth = 'xl',
17
+ padded = true,
18
+ class: className = '',
19
+ children,
20
+ }: Props = $props();
21
+
22
+ const maxClass: Record<MaxWidth, string> = {
23
+ sm: 'max-w-screen-sm',
24
+ md: 'max-w-screen-md',
25
+ lg: 'max-w-screen-lg',
26
+ xl: 'max-w-screen-xl',
27
+ '2xl': 'max-w-screen-2xl',
28
+ full: 'max-w-full',
29
+ };
30
+ </script>
31
+
32
+ <svelte:element
33
+ this={Tag}
34
+ class={['mx-auto w-full', maxClass[maxWidth], padded ? 'px-4 sm:px-6' : '', className]
35
+ .filter(Boolean)
36
+ .join(' ')}
37
+ >
38
+ {#if children}
39
+ {@render children()}
40
+ {/if}
41
+ </svelte:element>
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from 'svelte';
2
+ type MaxWidth = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full';
3
+ interface Props {
4
+ as?: 'div' | 'section' | 'main' | 'article';
5
+ maxWidth?: MaxWidth;
6
+ padded?: boolean;
7
+ class?: string;
8
+ children?: Snippet;
9
+ }
10
+ declare const Container: import("svelte").Component<Props, {}, "">;
11
+ type Container = ReturnType<typeof Container>;
12
+ export default Container;
@@ -0,0 +1,68 @@
1
+ <script lang="ts">
2
+ type Orientation = 'horizontal' | 'vertical';
3
+
4
+ interface Props {
5
+ orientation?: Orientation;
6
+ label?: string;
7
+ inset?: boolean;
8
+ class?: string;
9
+ labelClass?: string;
10
+ /** Decorative: omit `role="separator"` so SR does not announce a split. */
11
+ decorative?: boolean;
12
+ }
13
+
14
+ let {
15
+ orientation = 'horizontal',
16
+ label,
17
+ inset = false,
18
+ class: className = '',
19
+ labelClass = '',
20
+ decorative = true,
21
+ }: Props = $props();
22
+
23
+ const isVertical = $derived(orientation === 'vertical');
24
+
25
+ const lineClasses = $derived(
26
+ [
27
+ 'bg-border',
28
+ isVertical ? 'h-full w-px flex-1' : 'h-px w-full flex-1',
29
+ inset ? (isVertical ? 'my-2' : 'mx-2') : undefined,
30
+ ]
31
+ .filter(Boolean)
32
+ .join(' '),
33
+ );
34
+ </script>
35
+
36
+ <div
37
+ class={[
38
+ 'flex items-center text-description',
39
+ isVertical ? 'h-full min-h-[1rem] flex-col' : 'w-full flex-row',
40
+ className,
41
+ ]
42
+ .filter(Boolean)
43
+ .join(' ')}
44
+ >
45
+ <div
46
+ aria-hidden={decorative ? 'true' : undefined}
47
+ role={decorative ? 'presentation' : 'separator'}
48
+ aria-orientation={decorative ? undefined : orientation}
49
+ class={lineClasses}
50
+ ></div>
51
+
52
+ {#if label}
53
+ <span
54
+ class={['shrink-0 px-3 text-xs font-medium text-description', labelClass]
55
+ .filter(Boolean)
56
+ .join(' ')}
57
+ >
58
+ {label}
59
+ </span>
60
+
61
+ <div
62
+ aria-hidden={decorative ? 'true' : undefined}
63
+ role={decorative ? 'presentation' : 'separator'}
64
+ aria-orientation={decorative ? undefined : orientation}
65
+ class={lineClasses}
66
+ ></div>
67
+ {/if}
68
+ </div>
@@ -0,0 +1,13 @@
1
+ type Orientation = 'horizontal' | 'vertical';
2
+ interface Props {
3
+ orientation?: Orientation;
4
+ label?: string;
5
+ inset?: boolean;
6
+ class?: string;
7
+ labelClass?: string;
8
+ /** Decorative: omit `role="separator"` so SR does not announce a split. */
9
+ decorative?: boolean;
10
+ }
11
+ declare const Divider: import("svelte").Component<Props, {}, "">;
12
+ type Divider = ReturnType<typeof Divider>;
13
+ export default Divider;
@@ -1,64 +1,49 @@
1
1
  <script lang="ts">
2
- import { onMount } from "svelte";
2
+ import type { Component, Snippet } from "svelte";
3
3
 
4
4
  interface Props {
5
- icon?: string;
6
5
  title: string;
7
- description: string;
8
- className?: string;
6
+ description?: string;
7
+ icon?: Component<{ size?: number; class?: string }>;
8
+ children?: Snippet;
9
+ class?: string;
9
10
  }
10
11
 
11
12
  let {
12
- icon = "✨",
13
13
  title,
14
14
  description,
15
- className = "",
16
- ...restProps
15
+ icon: Icon,
16
+ children,
17
+ class: className = "",
17
18
  }: Props = $props();
18
-
19
- let mounted = $state(false);
20
-
21
- onMount(() => {
22
- mounted = true;
23
- });
24
19
  </script>
25
20
 
26
- {#if mounted}
27
- <div
28
- class="p-6 rounded-lg bg-base-50 border border-border hover:border-primary/20 hover:shadow-sm transition-colors duration-200 {className}"
29
- {...restProps}
30
- >
31
- <div class="flex items-start gap-4">
32
- <div class="shrink-0 text-2xl" aria-hidden="true">
33
- {icon}
34
- </div>
35
- <div class="flex-1 min-w-0">
36
- <h3 class="text-lg font-semibold text-headline mb-2">
37
- {title}
38
- </h3>
39
- <p class="text-description leading-relaxed">
40
- {description}
41
- </p>
42
- </div>
43
- </div>
21
+ <article
22
+ class="flex flex-col gap-3 p-6 rounded-2xl bg-surface-1 border border-base-200 transition-colors duration-200 hover:border-base-300 {className}"
23
+ >
24
+ {#if Icon}
25
+ <span
26
+ class="inline-flex items-center justify-center w-10 h-10 rounded-lg bg-brand-50 text-brand-600"
27
+ aria-hidden="true"
28
+ >
29
+ <Icon size={20} />
30
+ </span>
31
+ {/if}
32
+
33
+ <div class="flex flex-col gap-1.5">
34
+ <h3 class="text-base font-semibold leading-snug text-headline">
35
+ {title}
36
+ </h3>
37
+ {#if description}
38
+ <p class="text-sm leading-relaxed text-description">
39
+ {description}
40
+ </p>
41
+ {/if}
44
42
  </div>
45
- {:else}
46
- <div
47
- class="p-6 rounded-lg bg-base-100 border border-base-300 hover:border-base-400 hover:shadow-sm transition-colors duration-200 {className}"
48
- {...restProps}
49
- >
50
- <div class="flex items-start gap-4">
51
- <div class="shrink-0 text-2xl" aria-hidden="true">
52
- {icon}
53
- </div>
54
- <div class="flex-1 min-w-0">
55
- <h3 class="text-lg font-semibold text-headline mb-2">
56
- {title}
57
- </h3>
58
- <p class="text-description leading-relaxed">
59
- {description}
60
- </p>
61
- </div>
43
+
44
+ {#if children}
45
+ <div class="mt-2">
46
+ {@render children()}
62
47
  </div>
63
- </div>
64
- {/if}
48
+ {/if}
49
+ </article>
@@ -1,9 +1,14 @@
1
+ import type { Component, Snippet } from "svelte";
1
2
  interface Props {
2
- icon?: string;
3
3
  title: string;
4
- description: string;
5
- className?: string;
4
+ description?: string;
5
+ icon?: Component<{
6
+ size?: number;
7
+ class?: string;
8
+ }>;
9
+ children?: Snippet;
10
+ class?: string;
6
11
  }
7
- declare const FeatureCard: import("svelte").Component<Props, {}, "">;
12
+ declare const FeatureCard: Component<Props, {}, "">;
8
13
  type FeatureCard = ReturnType<typeof FeatureCard>;
9
14
  export default FeatureCard;
@@ -1,21 +1,17 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from "svelte";
3
- import type { ButtonVariant, SizeVariant } from "../../types/variants.js";
3
+ import type { ButtonVariant, SizeVariant } from "../types/variants.js";
4
4
 
5
5
  interface Props {
6
- /** Visual style variant. */
7
6
  variant?: ButtonVariant;
8
- /** Size variant controlling padding and radius. */
9
7
  size?: SizeVariant;
10
- /** Whether the button is disabled. */
11
8
  disabled?: boolean;
12
- /** Button type attribute. */
9
+ loading?: boolean;
13
10
  type?: "button" | "submit" | "reset";
14
- /** Accessible label for icon-only buttons. */
11
+ /** Required for icon-only usage (no visible text). */
15
12
  label?: string;
16
- /** Click handler for native click events. */
13
+ class?: string;
17
14
  onclick?: (event: MouseEvent) => void;
18
- /** Icon content. */
19
15
  children?: Snippet;
20
16
  }
21
17
 
@@ -23,66 +19,79 @@
23
19
  variant = "primary",
24
20
  size = "md",
25
21
  disabled = false,
22
+ loading = false,
26
23
  type = "button",
27
24
  label = "",
25
+ class: className = "",
28
26
  onclick,
29
27
  children,
30
28
  ...restProps
31
29
  }: Props = $props();
32
30
 
33
- const sizeClass = $derived(() => {
31
+ const isDisabled = $derived(disabled || loading);
32
+
33
+ const sizeClass = $derived.by(() => {
34
34
  if (size === "sm") {
35
35
  return {
36
36
  padding: "p-2",
37
37
  radius: "rounded-md",
38
+ spinner: "size-4",
38
39
  };
39
40
  } else if (size === "lg") {
40
41
  return {
41
42
  padding: "p-3",
42
43
  radius: "rounded-xl",
44
+ spinner: "size-6",
43
45
  };
44
46
  } else {
45
47
  return {
46
48
  padding: "p-2.5",
47
49
  radius: "rounded-lg",
50
+ spinner: "size-5",
48
51
  };
49
52
  }
50
53
  });
51
54
 
52
- const variantClass = $derived(() => {
55
+ const variantClass = $derived.by(() => {
53
56
  return variant === "primary"
54
- ? "bg-action-primary text-action-primary hover:bg-action-primary-hover active:bg-action-primary-active focus:ring-2 focus:ring-brand-500 focus:ring-offset-2"
57
+ ? "bg-action-primary text-action-primary hover:bg-action-primary-hover active:bg-action-primary-active active:scale-[0.98]"
55
58
  : variant === "secondary"
56
- ? "bg-action-secondary text-headline hover:bg-action-secondary-hover active:bg-action-secondary-active focus:ring-2 focus:ring-brand-500 focus:ring-offset-2"
59
+ ? "bg-action-secondary text-headline hover:bg-action-secondary-hover active:bg-action-secondary-active active:scale-[0.98]"
57
60
  : variant === "danger"
58
- ? "bg-action-danger text-inverse hover:bg-action-danger-hover active:bg-action-danger-active focus:ring-2 focus:ring-red-500 focus:ring-offset-2"
61
+ ? "bg-action-danger text-inverse hover:bg-action-danger-hover active:bg-action-danger-active active:scale-[0.98] focus-ring--danger"
59
62
  : variant === "ghost"
60
- ? "bg-transparent text-headline hover:bg-base-100 active:bg-base-200 focus:ring-2 focus:ring-base-500 focus:ring-offset-2 disabled:text-disabled"
63
+ ? "bg-transparent text-headline hover:bg-base-100 active:bg-base-200 active:scale-[0.98] focus-ring--muted disabled:text-disabled"
61
64
  : variant === "outline"
62
- ? "bg-transparent border-2 border-action-primary text-headline hover:bg-action-secondary hover:text-action-primary active:bg-action-primary-active active:text-action-primary focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 disabled:border-disabled disabled:text-disabled"
65
+ ? "bg-transparent border-2 border-action-primary text-headline hover:bg-action-secondary hover:text-action-primary active:bg-action-primary-active active:text-action-primary active:scale-[0.98] disabled:border-disabled disabled:text-disabled"
63
66
  : variant === "link"
64
- ? "text-brand-700 active:text-brand-700 underline-offset-4 hover:underline focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 disabled:text-disabled disabled:no-underline"
65
- : "bg-action-primary text-action-primary hover:bg-action-primary-hover active:bg-action-primary-active focus:ring-2 focus:ring-brand-500 focus:ring-offset-2";
67
+ ? "text-brand-700 active:text-brand-700 hover:underline disabled:text-disabled disabled:no-underline"
68
+ : "bg-action-primary text-action-primary hover:bg-action-primary-hover active:bg-action-primary-active active:scale-[0.98]";
66
69
  });
67
70
 
68
- const buttonClasses = $derived(() => {
69
- const sizeStyles = sizeClass();
71
+ const buttonClasses = $derived.by(() => {
72
+ const sizeStyles = sizeClass;
70
73
  const baseClasses =
71
- "inline-flex items-center justify-center transition-all duration-200 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed focus:outline-none";
74
+ "inline-flex focus-ring items-center justify-center transition-all duration-200 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed disabled:active:scale-100";
72
75
 
73
- return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.radius} ${variantClass()}`.trim();
76
+ return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.radius} ${variantClass} ${className}`.trim();
74
77
  });
75
78
  </script>
76
79
 
77
80
  <button
78
81
  {type}
79
- class={buttonClasses()}
80
- {disabled}
82
+ class={buttonClasses}
83
+ disabled={isDisabled}
84
+ aria-busy={loading ? "true" : undefined}
81
85
  {onclick}
82
86
  aria-label={label || undefined}
83
87
  {...restProps}
84
88
  >
85
- {#if children}
89
+ {#if loading}
90
+ <span
91
+ class="inline-block {sizeClass.spinner} shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent opacity-80"
92
+ aria-hidden="true"
93
+ ></span>
94
+ {:else if children}
86
95
  {@render children()}
87
96
  {/if}
88
97
  </button>
@@ -1,19 +1,15 @@
1
1
  import type { Snippet } from "svelte";
2
- import type { ButtonVariant, SizeVariant } from "../../types/variants.js";
2
+ import type { ButtonVariant, SizeVariant } from "../types/variants.js";
3
3
  interface Props {
4
- /** Visual style variant. */
5
4
  variant?: ButtonVariant;
6
- /** Size variant controlling padding and radius. */
7
5
  size?: SizeVariant;
8
- /** Whether the button is disabled. */
9
6
  disabled?: boolean;
10
- /** Button type attribute. */
7
+ loading?: boolean;
11
8
  type?: "button" | "submit" | "reset";
12
- /** Accessible label for icon-only buttons. */
9
+ /** Required for icon-only usage (no visible text). */
13
10
  label?: string;
14
- /** Click handler for native click events. */
11
+ class?: string;
15
12
  onclick?: (event: MouseEvent) => void;
16
- /** Icon content. */
17
13
  children?: Snippet;
18
14
  }
19
15
  declare const IconButton: import("svelte").Component<Props, {}, "">;