zabi-components 7.0.1 → 8.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 (174) hide show
  1. package/README.md +46 -3
  2. package/THEMING.md +255 -15
  3. package/dist/atoms/ActionPanel.svelte +12 -7
  4. package/dist/atoms/ActionPanel.svelte.d.ts +2 -0
  5. package/dist/atoms/Badge.svelte +93 -56
  6. package/dist/atoms/Badge.svelte.d.ts +15 -3
  7. package/dist/atoms/Button.svelte +49 -49
  8. package/dist/atoms/Button.svelte.d.ts +3 -0
  9. package/dist/atoms/Card.svelte +31 -28
  10. package/dist/atoms/Card.svelte.d.ts +5 -2
  11. package/dist/atoms/CardContent.svelte +10 -2
  12. package/dist/atoms/CardContent.svelte.d.ts +2 -0
  13. package/dist/atoms/CardFooter.svelte +10 -2
  14. package/dist/atoms/CardFooter.svelte.d.ts +2 -0
  15. package/dist/atoms/CardHeader.svelte +10 -2
  16. package/dist/atoms/CardHeader.svelte.d.ts +2 -0
  17. package/dist/atoms/Checkbox.svelte +4 -0
  18. package/dist/atoms/Checkbox.svelte.d.ts +2 -0
  19. package/dist/atoms/CodeBlock.svelte +15 -7
  20. package/dist/atoms/CodeBlock.svelte.d.ts +9 -3
  21. package/dist/atoms/ColorPicker.svelte +19 -9
  22. package/dist/atoms/ColorPicker.svelte.d.ts +3 -1
  23. package/dist/atoms/Container.svelte +2 -3
  24. package/dist/atoms/Divider.svelte +4 -4
  25. package/dist/atoms/FeatureCard.svelte +4 -3
  26. package/dist/atoms/Heading.svelte +56 -27
  27. package/dist/atoms/Heading.svelte.d.ts +17 -4
  28. package/dist/atoms/IconButton.svelte +30 -37
  29. package/dist/atoms/Input.svelte +26 -34
  30. package/dist/atoms/List.svelte +10 -2
  31. package/dist/atoms/List.svelte.d.ts +2 -0
  32. package/dist/atoms/ListItem.svelte +2 -2
  33. package/dist/atoms/ListItemLeading.svelte +4 -2
  34. package/dist/atoms/ListItemLeading.svelte.d.ts +1 -1
  35. package/dist/atoms/OptimizedImage.svelte +11 -2
  36. package/dist/atoms/OptimizedImage.svelte.d.ts +2 -0
  37. package/dist/atoms/Progress.svelte +4 -1
  38. package/dist/atoms/Progress.svelte.d.ts +2 -0
  39. package/dist/atoms/Radio.svelte +4 -0
  40. package/dist/atoms/Radio.svelte.d.ts +2 -0
  41. package/dist/atoms/Select.svelte +65 -67
  42. package/dist/atoms/Select.svelte.d.ts +4 -0
  43. package/dist/atoms/SelectionControl.svelte +5 -1
  44. package/dist/atoms/SelectionControl.svelte.d.ts +2 -0
  45. package/dist/atoms/Skeleton.svelte +5 -3
  46. package/dist/atoms/Table.svelte +2 -1
  47. package/dist/atoms/Text.svelte +34 -5
  48. package/dist/atoms/Text.svelte.d.ts +7 -1
  49. package/dist/atoms/Textarea.svelte +7 -6
  50. package/dist/atoms/ThemeToggle.svelte +17 -14
  51. package/dist/atoms/ThemeToggle.svelte.d.ts +2 -0
  52. package/dist/atoms/Toast.svelte +10 -8
  53. package/dist/atoms/Toggle.svelte +21 -13
  54. package/dist/atoms/Toggle.svelte.d.ts +7 -0
  55. package/dist/atoms/Tooltip.svelte +89 -57
  56. package/dist/atoms/Tooltip.svelte.d.ts +15 -0
  57. package/dist/components/index.d.ts +3 -1
  58. package/dist/components/index.d.ts.map +1 -1
  59. package/dist/components/organisms/index.d.ts +1 -0
  60. package/dist/components/organisms/index.d.ts.map +1 -1
  61. package/dist/components/util/cn.d.ts +8 -0
  62. package/dist/components/util/cn.d.ts.map +1 -0
  63. package/dist/components/util/ssr-safe.d.ts +4 -4
  64. package/dist/components/util/ssr-safe.d.ts.map +1 -1
  65. package/dist/index.d.ts +3 -1
  66. package/dist/index.js +3 -6
  67. package/dist/molecules/Alert.svelte +43 -64
  68. package/dist/molecules/Alert.svelte.d.ts +7 -1
  69. package/dist/molecules/ComponentDemo.svelte +10 -2
  70. package/dist/molecules/ComponentDemo.svelte.d.ts +2 -0
  71. package/dist/molecules/ContactForm.svelte +12 -2
  72. package/dist/molecules/ContactForm.svelte.d.ts +2 -0
  73. package/dist/molecules/Dropdown.svelte +40 -11
  74. package/dist/molecules/Dropdown.svelte.d.ts +4 -0
  75. package/dist/molecules/EmptyState.svelte +2 -1
  76. package/dist/molecules/Form.svelte +25 -14
  77. package/dist/molecules/Form.svelte.d.ts +13 -5
  78. package/dist/molecules/FormField.svelte +2 -1
  79. package/dist/molecules/Header.svelte +13 -3
  80. package/dist/molecules/Header.svelte.d.ts +2 -0
  81. package/dist/molecules/ImageUpload.svelte +54 -15
  82. package/dist/molecules/ImageUpload.svelte.d.ts +16 -5
  83. package/dist/molecules/Modal.svelte +110 -31
  84. package/dist/molecules/Modal.svelte.d.ts +9 -6
  85. package/dist/molecules/NavigationMenu.svelte +15 -3
  86. package/dist/molecules/NavigationMenu.svelte.d.ts +4 -0
  87. package/dist/molecules/NavigationMenuContent.svelte +36 -38
  88. package/dist/molecules/NavigationMenuContent.svelte.d.ts +2 -0
  89. package/dist/molecules/NavigationMenuItem.svelte +10 -2
  90. package/dist/molecules/NavigationMenuItem.svelte.d.ts +2 -0
  91. package/dist/molecules/NavigationMenuLink.svelte +10 -2
  92. package/dist/molecules/NavigationMenuLink.svelte.d.ts +2 -0
  93. package/dist/molecules/NavigationMenuList.svelte +10 -2
  94. package/dist/molecules/NavigationMenuList.svelte.d.ts +2 -0
  95. package/dist/molecules/NavigationMenuTrigger.svelte +16 -3
  96. package/dist/molecules/NavigationMenuTrigger.svelte.d.ts +2 -0
  97. package/dist/molecules/Page.svelte +8 -2
  98. package/dist/molecules/Page.svelte.d.ts +2 -0
  99. package/dist/molecules/PropsTable.svelte +6 -3
  100. package/dist/molecules/PropsTable.svelte.d.ts +2 -0
  101. package/dist/molecules/RadioGroup.svelte +5 -1
  102. package/dist/molecules/RadioGroup.svelte.d.ts +2 -0
  103. package/dist/molecules/Section.svelte +12 -4
  104. package/dist/molecules/Section.svelte.d.ts +3 -1
  105. package/dist/molecules/SidebarBrandHeader.svelte +13 -4
  106. package/dist/molecules/SidebarBrandHeader.svelte.d.ts +2 -0
  107. package/dist/molecules/SidebarFooter.svelte +12 -4
  108. package/dist/molecules/SidebarFooter.svelte.d.ts +2 -0
  109. package/dist/molecules/SidebarNavSection.svelte +12 -4
  110. package/dist/molecules/SidebarNavSection.svelte.d.ts +2 -0
  111. package/dist/molecules/SlideUp.svelte +85 -21
  112. package/dist/molecules/SlideUp.svelte.d.ts +5 -4
  113. package/dist/molecules/Tabs.svelte +25 -19
  114. package/dist/molecules/Tabs.svelte.d.ts +8 -4
  115. package/dist/molecules/Toaster.svelte +3 -2
  116. package/dist/molecules/ToasterToast.svelte +41 -10
  117. package/dist/molecules/ToasterToast.svelte.d.ts +2 -0
  118. package/dist/organisms/SidebarAccountPanel.svelte +4 -0
  119. package/dist/organisms/SidebarAccountPanel.svelte.d.ts +2 -0
  120. package/dist/organisms/SidebarNavigation.svelte +102 -59
  121. package/dist/organisms/SidebarNavigation.svelte.d.ts +2 -0
  122. package/dist/organisms/SidebarPanel.svelte +19 -11
  123. package/dist/organisms/SidebarPanel.svelte.d.ts +2 -0
  124. package/dist/organisms/SidebarShell.svelte +112 -0
  125. package/dist/organisms/SidebarShell.svelte.d.ts +52 -0
  126. package/dist/organisms/TopNavbar.svelte +88 -17
  127. package/dist/organisms/TopNavbar.svelte.d.ts +2 -0
  128. package/dist/organisms/index.d.ts +1 -0
  129. package/dist/organisms/index.js +1 -0
  130. package/dist/routes/lib/variant-utils.d.ts +26 -0
  131. package/dist/routes/lib/{variant-utils.ts → variant-utils.js} +15 -46
  132. package/dist/types/events.js +1 -0
  133. package/dist/types/index.js +3 -0
  134. package/dist/util/cn.d.ts +7 -0
  135. package/dist/util/cn.js +48 -0
  136. package/dist/util/ssr-safe.d.ts +4 -4
  137. package/dist/zabi-components-colors.css +3 -437
  138. package/dist/zabi-components-theme-dark-only.css +223 -103
  139. package/dist/zabi-components-theme-dark.css +223 -103
  140. package/dist/zabi-components-theme-only.css +347 -107
  141. package/dist/zabi-components-theme.css +347 -107
  142. package/dist/zabi-components.css +953 -843
  143. package/docs/lucide-icons.md +3 -1
  144. package/package.json +20 -25
  145. package/dist/lib/layout-width-tokens.d.ts +0 -17
  146. package/dist/lib/layout-width-tokens.d.ts.map +0 -1
  147. package/dist/lib/showcase/component-docs/Button.d.ts +0 -3
  148. package/dist/lib/showcase/component-docs/Button.d.ts.map +0 -1
  149. package/dist/lib/showcase/component-docs/Input.d.ts +0 -3
  150. package/dist/lib/showcase/component-docs/Input.d.ts.map +0 -1
  151. package/dist/lib/showcase/component-docs/List.d.ts +0 -3
  152. package/dist/lib/showcase/component-docs/List.d.ts.map +0 -1
  153. package/dist/lib/showcase/component-docs/Modal.d.ts +0 -3
  154. package/dist/lib/showcase/component-docs/Modal.d.ts.map +0 -1
  155. package/dist/lib/showcase/component-docs/Radio.d.ts +0 -3
  156. package/dist/lib/showcase/component-docs/Radio.d.ts.map +0 -1
  157. package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts +0 -3
  158. package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts.map +0 -1
  159. package/dist/lib/showcase/component-docs/Skeleton.d.ts +0 -3
  160. package/dist/lib/showcase/component-docs/Skeleton.d.ts.map +0 -1
  161. package/dist/lib/showcase/component-docs/_shared.d.ts +0 -12
  162. package/dist/lib/showcase/component-docs/_shared.d.ts.map +0 -1
  163. package/dist/lib/showcase/component-docs/index.d.ts +0 -4
  164. package/dist/lib/showcase/component-docs/index.d.ts.map +0 -1
  165. package/dist/lib/showcase/components-catalog.d.ts +0 -3
  166. package/dist/lib/showcase/components-catalog.d.ts.map +0 -1
  167. package/dist/lib/showcase/components-showcase-constants.d.ts +0 -89
  168. package/dist/lib/showcase/components-showcase-constants.d.ts.map +0 -1
  169. package/dist/lib/showcase/docs-sidebar-helpers.d.ts +0 -15
  170. package/dist/lib/showcase/docs-sidebar-helpers.d.ts.map +0 -1
  171. package/dist/routes/lib/focus-utils.d.ts +0 -12
  172. package/dist/routes/lib/focus-utils.ts +0 -110
  173. package/dist/routes/lib/ssr-safe.ts +0 -58
  174. package/scripts/fix-lucide-svelte-icon-dts.js +0 -35
@@ -2,12 +2,16 @@
2
2
  import type { Snippet } from "svelte";
3
3
  import type { HTMLButtonAttributes } from "svelte/elements";
4
4
  import type { ButtonVariant, SizeVariant } from "../types/variants.js";
5
+ import { cn } from "../util/cn.js";
5
6
 
6
7
  type Props = Omit<HTMLButtonAttributes, "class"> & {
7
8
  variant?: ButtonVariant;
8
9
  size?: SizeVariant;
9
10
  loading?: boolean;
10
11
  text?: string;
12
+ /** Stretch to the width of the container. */
13
+ fullWidth?: boolean;
14
+ /** @deprecated use `fullWidth` — kept so 7.x call sites keep working. */
11
15
  isFullWidth?: boolean;
12
16
  class?: string;
13
17
  children?: Snippet;
@@ -20,6 +24,7 @@
20
24
  loading = false,
21
25
  type = "button",
22
26
  text = "",
27
+ fullWidth = false,
23
28
  isFullWidth = false,
24
29
  class: className = "",
25
30
  onclick,
@@ -29,63 +34,58 @@
29
34
 
30
35
  const isDisabled = $derived(disabled || loading);
31
36
 
37
+ /**
38
+ * One height scale, shared with Input, Select and IconButton, so controls
39
+ * of the same size line up in a row. Heights are fixed (h-8 / h-10 / h-12)
40
+ * rather than derived from padding + line-height, which is what let Button
41
+ * and Input drift 14px apart at `lg`.
42
+ *
43
+ * Weight and tracking do NOT change with size: a large button used to drop
44
+ * to font-normal, so the biggest button had the lightest label.
45
+ */
32
46
  const sizeClass = $derived.by(() => {
33
47
  if (size === "sm") {
34
- return {
35
- padding: "px-4 py-2.5",
36
- text: "text-sm",
37
- font: "font-medium",
38
- leading: "leading-5",
39
- tracking: "tracking-[0.1px]",
40
- radius: "rounded-lg",
41
- gap: "gap-2",
42
- };
43
- } else if (size === "lg") {
44
- return {
45
- padding: "px-8 py-4",
46
- text: "text-lg",
47
- font: "font-normal",
48
- leading: "leading-8",
49
- tracking: "tracking-normal",
50
- radius: "rounded-xl",
51
- gap: "gap-3",
52
- };
53
- } else {
54
- return {
55
- padding: "px-5 py-3",
56
- text: "text-base",
57
- font: "font-medium",
58
- leading: "leading-6",
59
- tracking: "tracking-[0.15px]",
60
- radius: "rounded-lg",
61
- gap: "gap-2",
62
- };
48
+ return { box: "h-8 px-3", text: "text-sm", gap: "gap-2", spinner: "size-3.5" };
63
49
  }
50
+ if (size === "lg") {
51
+ return { box: "h-12 px-5", text: "text-base", gap: "gap-2", spinner: "size-4" };
52
+ }
53
+ return { box: "h-10 px-4", text: "text-sm", gap: "gap-2", spinner: "size-4" };
64
54
  });
65
55
 
56
+ /**
57
+ * Disabled is deliberately NOT `opacity-50` over the variant colour — that
58
+ * made a disabled primary lighter and more colourful than an enabled
59
+ * secondary. Every variant falls back to the same neutral disabled pair.
60
+ */
61
+ const disabledClass =
62
+ "disabled:bg-action-disabled disabled:text-action-disabled-text disabled:border-transparent disabled:no-underline disabled:shadow-none disabled:cursor-not-allowed disabled:active:scale-100";
63
+
66
64
  const variantClass = $derived.by(() => {
67
- return variant === "primary"
68
- ? "bg-action-primary text-action-primary hover:bg-action-primary-hover active:bg-action-primary-active active:scale-[0.98]"
69
- : variant === "secondary"
70
- ? "bg-action-secondary text-headline hover:bg-action-secondary-hover active:bg-action-secondary-active active:scale-[0.98]"
71
- : variant === "danger"
72
- ? "bg-action-danger text-inverse hover:bg-action-danger-hover active:bg-action-danger-active active:scale-[0.98] focus-ring--danger"
73
- : variant === "ghost"
74
- ? "bg-transparent text-headline hover:bg-base-100 active:bg-base-200 active:scale-[0.98] focus-ring--muted disabled:text-disabled"
75
- : variant === "outline"
76
- ? "bg-transparent border border-border text-headline hover:bg-action-secondary hover:border-border-medium hover:text-action-primary active:bg-action-primary-active active:text-action-primary active:scale-[0.98] disabled:border-disabled disabled:text-disabled"
77
- : variant === "link"
78
- ? "text-brand-700 active:text-brand-700 underline-offset-4 hover:underline disabled:text-disabled disabled:no-underline"
79
- : "bg-action-primary text-action-primary hover:bg-action-primary-hover active:bg-action-primary-active active:scale-[0.98]";
65
+ switch (variant) {
66
+ case "secondary":
67
+ return "bg-action-secondary text-headline hover:bg-action-secondary-hover active:bg-action-secondary-active active:scale-[0.98]";
68
+ case "danger":
69
+ return "bg-action-danger text-action-danger-text hover:bg-action-danger-hover active:bg-action-danger-active active:scale-[0.98] focus-ring--danger";
70
+ case "ghost":
71
+ return "bg-transparent text-headline hover:bg-surface-hover active:bg-surface-active active:scale-[0.98] focus-ring--muted";
72
+ case "outline":
73
+ return "bg-transparent border border-border text-headline hover:bg-surface-hover hover:border-border-medium active:bg-surface-active active:scale-[0.98]";
74
+ case "link":
75
+ // A link variant has to look like a link at rest, not only on
76
+ // hover — otherwise it is indistinguishable from `ghost`.
77
+ return "bg-transparent text-link hover:text-link-hover underline underline-offset-4 decoration-1 hover:decoration-2 px-0 focus-ring--muted";
78
+ case "primary":
79
+ default:
80
+ return "bg-action-primary text-action-primary hover:bg-action-primary-hover active:bg-action-primary-active active:scale-[0.98]";
81
+ }
80
82
  });
81
83
 
82
84
  const buttonClasses = $derived.by(() => {
83
- const sizeStyles = sizeClass;
84
- const flexClass = isFullWidth ? "flex" : "inline-flex";
85
- const widthClass = isFullWidth ? "w-full" : "";
86
- const baseClasses = `${flexClass} focus-ring items-center justify-center transition-all duration-200 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed disabled:active:scale-100`;
87
-
88
- return `${baseClasses} ${widthClass} ${sizeStyles.padding} ${sizeStyles.text} ${sizeStyles.font} ${sizeStyles.leading} ${sizeStyles.tracking} ${sizeStyles.radius} ${sizeStyles.gap} ${variantClass} ${className}`.trim();
85
+ const s = sizeClass;
86
+ const layout = fullWidth || isFullWidth ? "flex w-full" : "inline-flex";
87
+ const base = `${layout} focus-ring items-center justify-center rounded-control font-medium whitespace-nowrap transition-colors duration-150 cursor-pointer select-none`;
88
+ return cn(`${base} ${s.box} ${s.text} ${s.gap} ${variantClass} ${disabledClass} ${className}`);
89
89
  });
90
90
  </script>
91
91
 
@@ -99,7 +99,7 @@
99
99
  >
100
100
  {#if loading}
101
101
  <span
102
- class="inline-block size-4 shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent opacity-80"
102
+ class="inline-block {sizeClass.spinner} shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent opacity-80"
103
103
  aria-hidden="true"
104
104
  ></span>
105
105
  {/if}
@@ -6,6 +6,9 @@ type Props = Omit<HTMLButtonAttributes, "class"> & {
6
6
  size?: SizeVariant;
7
7
  loading?: boolean;
8
8
  text?: string;
9
+ /** Stretch to the width of the container. */
10
+ fullWidth?: boolean;
11
+ /** @deprecated use `fullWidth` — kept so 7.x call sites keep working. */
9
12
  isFullWidth?: boolean;
10
13
  class?: string;
11
14
  children?: Snippet;
@@ -1,72 +1,75 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from "svelte";
3
+ import type { HTMLAttributes } from "svelte/elements";
3
4
  import type { CardVariant, SizeVariant } from "../types/variants.js";
4
5
 
5
- interface Props {
6
+ import { cn } from "../util/cn.js";
7
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
6
8
  onclick?: (event: MouseEvent) => void | Promise<void>;
7
9
  size?: SizeVariant;
8
10
  variant?: CardVariant;
9
11
  fullWidth?: boolean;
12
+ class?: string;
13
+ /** @deprecated use `class` — kept so 7.x call sites keep working. */
10
14
  className?: string;
11
15
  /** Required when `onclick` is set (no visible label). */
12
16
  ariaLabel?: string;
13
17
  children?: Snippet;
14
- }
18
+ };
15
19
 
16
20
  let {
17
21
  onclick,
18
22
  size = "md",
19
23
  variant = "default",
20
24
  fullWidth = true,
25
+ class: classProp = "",
21
26
  className = "",
22
27
  ariaLabel,
23
28
  children,
24
29
  ...restProps
25
30
  }: Props = $props();
26
31
 
32
+ /** Size changes the padding. It does NOT change the corner — see
33
+ * `--radius-container`. A large card is a bigger box, not a rounder one. */
27
34
  const sizeClass = $derived(
28
35
  size === "sm" ? "p-4" : size === "lg" ? "p-8" : "p-6",
29
36
  );
30
37
 
31
- const radiusClass = $derived(
32
- size === "sm"
33
- ? "rounded-xl"
34
- : size === "lg"
35
- ? "rounded-3xl"
36
- : "rounded-2xl",
37
- );
38
-
39
38
  const variantClasses = $derived(
40
39
  variant === "elevated"
41
- ? "bg-card shadow-lg"
40
+ ? "bg-card-elevated shadow-lg"
42
41
  : variant === "outlined"
43
- ? "bg-card border-2 border-base-200 shadow-none"
42
+ ? "bg-card border border-border shadow-none"
44
43
  : variant === "flat"
45
44
  ? "bg-card shadow-none border-none"
46
45
  : "bg-card shadow-sm",
47
46
  );
48
47
 
48
+ const interactiveClasses = $derived.by(() => {
49
+ if (!onclick) return "";
50
+ const shared = "cursor-pointer focus-ring";
51
+ switch (variant) {
52
+ case "elevated":
53
+ return `${shared} hover:bg-card-hover`;
54
+ case "outlined":
55
+ return `${shared} hover:border-border-medium hover:bg-card-hover`;
56
+ case "flat":
57
+ return `${shared} hover:bg-card-hover`;
58
+ default:
59
+ return `${shared} hover:shadow-lg hover:bg-card-hover`;
60
+ }
61
+ });
62
+
49
63
  const cardClasses = $derived(
50
- [
51
- radiusClass,
52
- "transition-all duration-200",
64
+ cn(
65
+ "rounded-container transition-all duration-150",
53
66
  variantClasses,
54
- onclick
55
- ? variant === "elevated"
56
- ? "cursor-pointer hover:shadow-xl hover:bg-card-hover"
57
- : variant === "outlined"
58
- ? "cursor-pointer hover:border-brand-500 hover:bg-card-hover"
59
- : variant === "flat"
60
- ? "cursor-pointer hover:bg-card-hover"
61
- : "cursor-pointer hover:shadow-lg hover:bg-card-hover"
62
- : "",
67
+ interactiveClasses,
63
68
  fullWidth ? "w-full" : "",
64
69
  sizeClass,
70
+ classProp,
65
71
  className,
66
- ]
67
- .filter(Boolean)
68
- .join(" ")
69
- .trim(),
72
+ ),
70
73
  );
71
74
 
72
75
  function handleKeydown(event: KeyboardEvent) {
@@ -1,15 +1,18 @@
1
1
  import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
2
3
  import type { CardVariant, SizeVariant } from "../types/variants.js";
3
- interface Props {
4
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
4
5
  onclick?: (event: MouseEvent) => void | Promise<void>;
5
6
  size?: SizeVariant;
6
7
  variant?: CardVariant;
7
8
  fullWidth?: boolean;
9
+ class?: string;
10
+ /** @deprecated use `class` — kept so 7.x call sites keep working. */
8
11
  className?: string;
9
12
  /** Required when `onclick` is set (no visible label). */
10
13
  ariaLabel?: string;
11
14
  children?: Snippet;
12
- }
15
+ };
13
16
  declare const Card: import("svelte").Component<Props, {}, "">;
14
17
  type Card = ReturnType<typeof Card>;
15
18
  export default Card;
@@ -1,9 +1,12 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from "svelte";
3
+ import { cn } from "../util/cn.js";
3
4
 
4
5
  interface Props {
5
6
  image?: string;
6
7
  imageAlt?: string;
8
+ class?: string;
9
+ /** @deprecated use `class`. */
7
10
  className?: string;
8
11
  children?: Snippet;
9
12
  }
@@ -11,10 +14,15 @@
11
14
  let {
12
15
  image = "",
13
16
  imageAlt = "",
14
- className = "",
17
+ class: classAttr = "",
18
+ className: legacyClass = "",
15
19
  children,
16
20
  ...restProps
17
21
  }: Props = $props();
22
+
23
+ /** `class` is the public prop; `className` is a deprecated alias.
24
+ * Both are merged here so existing call sites keep working. */
25
+ const className = $derived(cn(`${classAttr} ${legacyClass}`));
18
26
  </script>
19
27
 
20
28
  <div class={className} {...restProps}>
@@ -22,7 +30,7 @@
22
30
  <img
23
31
  src={image}
24
32
  alt={imageAlt}
25
- class="w-full h-48 object-cover rounded-lg mb-4"
33
+ class="w-full h-48 object-cover rounded-control mb-4"
26
34
  />
27
35
  {/if}
28
36
  {#if children}
@@ -2,6 +2,8 @@ import type { Snippet } from "svelte";
2
2
  interface Props {
3
3
  image?: string;
4
4
  imageAlt?: string;
5
+ class?: string;
6
+ /** @deprecated use `class`. */
5
7
  className?: string;
6
8
  children?: Snippet;
7
9
  }
@@ -1,20 +1,28 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from "svelte";
3
+ import { cn } from "../util/cn.js";
3
4
 
4
5
  interface Props {
6
+ class?: string;
7
+ /** @deprecated use `class`. */
5
8
  className?: string;
6
9
  children?: Snippet;
7
10
  }
8
11
 
9
12
  let {
10
- className = "",
13
+ class: classAttr = "",
14
+ className: legacyClass = "",
11
15
  children,
12
16
  ...restProps
13
17
  }: Props = $props();
18
+
19
+ /** `class` is the public prop; `className` is a deprecated alias.
20
+ * Both are merged here so existing call sites keep working. */
21
+ const className = $derived(cn(`${classAttr} ${legacyClass}`));
14
22
  </script>
15
23
 
16
24
  <footer
17
- class="flex items-center p-6 pt-0 {className}"
25
+ class={cn("flex items-center p-6 pt-0", className)}
18
26
  {...restProps}
19
27
  >
20
28
  {#if children}
@@ -1,5 +1,7 @@
1
1
  import type { Snippet } from "svelte";
2
2
  interface Props {
3
+ class?: string;
4
+ /** @deprecated use `class`. */
3
5
  className?: string;
4
6
  children?: Snippet;
5
7
  }
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from "svelte";
3
+ import { cn } from "../util/cn.js";
3
4
 
4
5
  interface Props {
5
6
  title?: string;
@@ -10,6 +11,8 @@
10
11
  /** `id` on the description `<p>`. */
11
12
  descriptionId?: string;
12
13
  level?: 1 | 2 | 3 | 4 | 5 | 6;
14
+ class?: string;
15
+ /** @deprecated use `class`. */
13
16
  className?: string;
14
17
  children?: Snippet;
15
18
  }
@@ -21,18 +24,23 @@
21
24
  subtitleId,
22
25
  descriptionId,
23
26
  level = 3,
24
- className = "",
27
+ class: classAttr = "",
28
+ className: legacyClass = "",
25
29
  children,
26
30
  ...restProps
27
31
  }: Props = $props();
28
32
 
33
+ /** `class` is the public prop; `className` is a deprecated alias.
34
+ * Both are merged here so existing call sites keep working. */
35
+ const className = $derived(cn(`${classAttr} ${legacyClass}`));
36
+
29
37
  const headingTag = $derived(`h${level}`);
30
38
  const headingClasses = $derived(() => {
31
39
  return "text-2xl font-semibold leading-none tracking-tight text-headline";
32
40
  });
33
41
  </script>
34
42
 
35
- <header class={`flex flex-col space-y-1.5 pb-4 ${className}`.trim()} {...restProps}>
43
+ <header class={cn(`flex flex-col space-y-2 pb-4 ${className}`)} {...restProps}>
36
44
  {#if title}
37
45
  <svelte:element this={headingTag} class={headingClasses()}>
38
46
  {title}
@@ -8,6 +8,8 @@ interface Props {
8
8
  /** `id` on the description `<p>`. */
9
9
  descriptionId?: string;
10
10
  level?: 1 | 2 | 3 | 4 | 5 | 6;
11
+ class?: string;
12
+ /** @deprecated use `class`. */
11
13
  className?: string;
12
14
  children?: Snippet;
13
15
  }
@@ -3,6 +3,8 @@
3
3
  import type { SelectionControlMarkProps } from "./SelectionControl.svelte";
4
4
 
5
5
  interface Props {
6
+ /** Extra classes for the host element. */
7
+ class?: string;
6
8
  id?: string;
7
9
  name?: string;
8
10
  value?: string;
@@ -16,6 +18,7 @@
16
18
  }
17
19
 
18
20
  let {
21
+ class: className = "",
19
22
  id,
20
23
  value = "",
21
24
  name = "",
@@ -65,5 +68,6 @@
65
68
  {onChange}
66
69
  {onchange}
67
70
  {mark}
71
+ class={className}
68
72
  {...restProps}
69
73
  />
@@ -1,4 +1,6 @@
1
1
  interface Props {
2
+ /** Extra classes for the host element. */
3
+ class?: string;
2
4
  id?: string;
3
5
  name?: string;
4
6
  value?: string;
@@ -2,11 +2,14 @@
2
2
  import Check from "@lucide/svelte/icons/check";
3
3
  import Copy from "@lucide/svelte/icons/copy";
4
4
  import IconButton from "./IconButton.svelte";
5
+ import { cn } from "../util/cn.js";
5
6
 
6
7
  interface Props {
7
8
  /** Raw source; escaped unless `trustHtml` (e.g. highlighter HTML). */
8
9
  code: string;
9
10
  language?: string;
11
+ class?: string;
12
+ /** @deprecated use `class`. */
10
13
  className?: string;
11
14
  showCopyButton?: boolean;
12
15
  /** If true, `{@html code}` — only trusted, sanitized input. */
@@ -16,11 +19,16 @@
16
19
  let {
17
20
  code,
18
21
  language = "svelte",
19
- className = "",
22
+ class: classAttr = "",
23
+ className: legacyClass = "",
20
24
  showCopyButton = true,
21
25
  trustHtml = false,
22
26
  ...restProps
23
- } = $props();
27
+ }: Props & Record<string, unknown> = $props();
28
+
29
+ /** `class` is the public prop; `className` is a deprecated alias.
30
+ * Both are merged here so existing call sites keep working. */
31
+ const className = $derived(cn(`${classAttr} ${legacyClass}`));
24
32
 
25
33
  let copied = $state(false);
26
34
 
@@ -38,16 +46,16 @@
38
46
  </script>
39
47
 
40
48
  <div
41
- class="code-block relative bg-surface-1 border border-border rounded-lg overflow-hidden {className}"
49
+ class={cn("code-block relative bg-surface-1 border border-border rounded-control overflow-hidden", className)}
42
50
  {...restProps}
43
51
  >
44
52
  <div
45
53
  class="flex items-center justify-between px-4 py-2 bg-surface-2 border-b border-border"
46
54
  >
47
- <div class="flex items-center gap-2">
48
- <div class="w-3 h-3 rounded-full bg-red-500"></div>
49
- <div class="w-3 h-3 rounded-full bg-yellow-500"></div>
50
- <div class="w-3 h-3 rounded-full bg-green-500"></div>
55
+ <div class="flex items-center gap-2" aria-hidden="true">
56
+ <div class="w-3 h-3 rounded-full bg-error"></div>
57
+ <div class="w-3 h-3 rounded-full bg-warning"></div>
58
+ <div class="w-3 h-3 rounded-full bg-success"></div>
51
59
  </div>
52
60
  {#if showCopyButton}
53
61
  <IconButton
@@ -1,9 +1,15 @@
1
- declare const CodeBlock: import("svelte").Component<{
2
- code: any;
1
+ interface Props {
2
+ /** Raw source; escaped unless `trustHtml` (e.g. highlighter HTML). */
3
+ code: string;
3
4
  language?: string;
5
+ class?: string;
6
+ /** @deprecated use `class`. */
4
7
  className?: string;
5
8
  showCopyButton?: boolean;
9
+ /** If true, `{@html code}` — only trusted, sanitized input. */
6
10
  trustHtml?: boolean;
7
- } & Record<string, any>, {}, "">;
11
+ }
12
+ type $$ComponentProps = Props & Record<string, unknown>;
13
+ declare const CodeBlock: import("svelte").Component<$$ComponentProps, {}, "">;
8
14
  type CodeBlock = ReturnType<typeof CodeBlock>;
9
15
  export default CodeBlock;
@@ -3,6 +3,8 @@
3
3
  import { onMount, onDestroy, tick } from "svelte";
4
4
 
5
5
  interface Props {
6
+ /** Extra classes for the host element. */
7
+ class?: string;
6
8
  value?: string;
7
9
  label?: string;
8
10
  disabled?: boolean;
@@ -11,7 +13,8 @@
11
13
  }
12
14
 
13
15
  let {
14
- value = "",
16
+ class: className = "",
17
+ value = $bindable(""),
15
18
  label = "",
16
19
  disabled = false,
17
20
  placeholder = "#000000",
@@ -35,7 +38,14 @@
35
38
  return hexPattern.test(hex);
36
39
  }
37
40
 
38
- function hexToHsl(hex: string): [number, number, number] {
41
+ /** `#f00` → `#ff0000`; 6-digit input is returned unchanged. */
42
+ function expandHex(hex: string): string {
43
+ if (hex.length !== 4) return hex;
44
+ return `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`;
45
+ }
46
+
47
+ function hexToHsl(rawHex: string): [number, number, number] {
48
+ const hex = expandHex(rawHex);
39
49
  const r = parseInt(hex.slice(1, 3), 16) / 255;
40
50
  const g = parseInt(hex.slice(3, 5), 16) / 255;
41
51
  const b = parseInt(hex.slice(5, 7), 16) / 255;
@@ -263,7 +273,7 @@
263
273
  const pickerIndicatorY = $derived(`${100 - lightness}%`);
264
274
  </script>
265
275
 
266
- <div {...restProps}>
276
+ <div class={className} {...restProps}>
267
277
  <div class="flex items-start gap-2">
268
278
  <div class="flex-1">
269
279
  <Input
@@ -283,7 +293,7 @@
283
293
  type="button"
284
294
  onclick={togglePicker}
285
295
  {disabled}
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"
296
+ class="focus-ring w-11 h-11 rounded-control border-2 border-card shrink-0 cursor-pointer hover:ring-2 hover:ring-border transition-colors disabled:cursor-not-allowed disabled:opacity-50"
287
297
  style="background-color: {displayColor};"
288
298
  aria-label="Open color picker"
289
299
  aria-expanded={isOpen}
@@ -292,14 +302,14 @@
292
302
  {#if isOpen}
293
303
  <div
294
304
  use:setPickerContainer
295
- class="absolute top-12 right-0 z-50 border border-input-border bg-input rounded-2xl shadow-lg p-4 w-80"
305
+ class="absolute top-12 right-0 z-popover border border-input-border bg-input rounded-container shadow-lg p-4 w-80"
296
306
  role="dialog"
297
307
  aria-label="Color picker"
298
308
  >
299
309
  <div class="space-y-4">
300
310
  <div
301
311
  use:setColorMapContainer
302
- class="relative w-full h-48 rounded-xl overflow-hidden cursor-crosshair"
312
+ class="relative w-full h-48 rounded-container overflow-hidden cursor-crosshair"
303
313
  onmousedown={(e) => {
304
314
  isDragging = true;
305
315
  handleColorMapClick(e);
@@ -314,14 +324,14 @@
314
324
  class="w-full h-full"
315
325
  ></canvas>
316
326
  <div
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"
327
+ class="absolute w-4 h-4 border-2 border-white rounded-full shadow-sm pointer-events-none transform -translate-x-1/2 -translate-y-1/2"
318
328
  style="left: {pickerIndicatorX}; top: {pickerIndicatorY};"
319
329
  ></div>
320
330
  </div>
321
331
 
322
332
  <div class="space-y-2">
323
333
  <div
324
- class="relative h-6 rounded-lg overflow-hidden"
334
+ class="relative h-6 rounded-control overflow-hidden"
325
335
  >
326
336
  <div
327
337
  class="absolute inset-0"
@@ -337,7 +347,7 @@
337
347
  aria-label="Hue slider"
338
348
  />
339
349
  <div
340
- class="absolute top-0 bottom-0 w-3 bg-card shadow-md pointer-events-none my-0.25 rounded-full border border-base-950"
350
+ class="absolute top-0 bottom-0 w-3 bg-card shadow-sm pointer-events-none my-px rounded-full border border-base-950"
341
351
  style="left: {(hue / 360) * 100}%;"
342
352
  ></div>
343
353
  </div>
@@ -1,10 +1,12 @@
1
1
  interface Props {
2
+ /** Extra classes for the host element. */
3
+ class?: string;
2
4
  value?: string;
3
5
  label?: string;
4
6
  disabled?: boolean;
5
7
  placeholder?: string;
6
8
  onchange?: (event: Event) => void;
7
9
  }
8
- declare const ColorPicker: import("svelte").Component<Props, {}, "">;
10
+ declare const ColorPicker: import("svelte").Component<Props, {}, "value">;
9
11
  type ColorPicker = ReturnType<typeof ColorPicker>;
10
12
  export default ColorPicker;
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
 
4
+ import { cn } from "../util/cn.js";
4
5
  type MaxWidth = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full';
5
6
 
6
7
  interface Props {
@@ -31,9 +32,7 @@
31
32
 
32
33
  <svelte:element
33
34
  this={Tag}
34
- class={['mx-auto w-full', maxClass[maxWidth], padded ? 'px-4 sm:px-6' : '', className]
35
- .filter(Boolean)
36
- .join(' ')}
35
+ class={cn('mx-auto w-full', maxClass[maxWidth], padded ? 'px-4 sm:px-6' : '', className)}
37
36
  >
38
37
  {#if children}
39
38
  {@render children()}