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
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import { cn } from "../util/cn.js";
3
+
2
4
  type Orientation = 'horizontal' | 'vertical';
3
5
 
4
6
  interface Props {
@@ -34,13 +36,11 @@
34
36
  </script>
35
37
 
36
38
  <div
37
- class={[
39
+ class={cn(
38
40
  'flex items-center text-description',
39
41
  isVertical ? 'h-full min-h-[1rem] flex-col' : 'w-full flex-row',
40
42
  className,
41
- ]
42
- .filter(Boolean)
43
- .join(' ')}
43
+ )}
44
44
  >
45
45
  <div
46
46
  aria-hidden={decorative ? 'true' : undefined}
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { Component, Snippet } from "svelte";
3
+ import { cn } from "../util/cn.js";
3
4
 
4
5
  interface Props {
5
6
  title: string;
@@ -19,18 +20,18 @@
19
20
  </script>
20
21
 
21
22
  <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
+ class={cn("flex flex-col gap-3 p-6 rounded-container bg-surface-1 border border-border transition-colors duration-200 hover:border-border-medium", className)}
23
24
  >
24
25
  {#if Icon}
25
26
  <span
26
- class="inline-flex items-center justify-center w-10 h-10 rounded-lg bg-brand-50 text-brand-600"
27
+ class="inline-flex items-center justify-center w-10 h-10 rounded-control bg-brand-50 text-brand-600"
27
28
  aria-hidden="true"
28
29
  >
29
30
  <Icon size={20} />
30
31
  </span>
31
32
  {/if}
32
33
 
33
- <div class="flex flex-col gap-1.5">
34
+ <div class="flex flex-col gap-2">
34
35
  <h3 class="text-base font-semibold leading-snug text-headline">
35
36
  {title}
36
37
  </h3>
@@ -1,38 +1,67 @@
1
1
  <script lang="ts">
2
- interface Props {
3
- level?: number;
4
- text: string;
5
- }
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+ import { cn } from "../util/cn.js";
6
5
 
7
- let { level = 1, text }: Props = $props();
6
+ /** Semantic heading level — also the default visual size. */
7
+ type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
8
8
 
9
- const fontWeight = $derived(level <= 2 ? "700" : "500");
9
+ type Props = Omit<HTMLAttributes<HTMLHeadingElement>, "class"> & {
10
+ level?: HeadingLevel;
11
+ /**
12
+ * Visual size, when it should differ from the semantic level — an `h2`
13
+ * that needs to look like an `h4` keeps the document outline correct
14
+ * without forcing the wrong tag.
15
+ */
16
+ size?: HeadingLevel;
17
+ /** Heading text. Ignored when `children` is provided. */
18
+ text?: string;
19
+ class?: string;
20
+ children?: Snippet;
21
+ };
22
+
23
+ let {
24
+ level = 1,
25
+ size,
26
+ text = "",
27
+ class: className = "",
28
+ children,
29
+ ...restProps
30
+ }: Props = $props();
31
+
32
+ const visualSize = $derived(size ?? level);
10
33
  const Tag = $derived(`h${level}`);
11
34
 
12
- const sizeClasses = {
13
- 1: "text-4xl",
14
- 2: "text-3xl",
15
- 3: "text-2xl",
16
- 4: "text-xl",
17
- 5: "text-lg",
18
- 6: "text-base",
35
+ /**
36
+ * Display sizes tighten as they grow. Untracked 36px type with default
37
+ * 40px leading is the single clearest "this is stock Tailwind" tell;
38
+ * negative tracking and a tighter leading is most of what separates
39
+ * display type from body type set large.
40
+ */
41
+ const sizeClasses: Record<HeadingLevel, string> = {
42
+ 1: "text-4xl leading-10 tracking-[-0.02em] font-bold",
43
+ 2: "text-3xl leading-9 tracking-[-0.015em] font-bold",
44
+ 3: "text-2xl leading-8 tracking-[-0.01em] font-semibold",
45
+ 4: "text-xl leading-7 tracking-[-0.005em] font-semibold",
46
+ 5: "text-lg leading-7 font-semibold",
47
+ 6: "text-base leading-6 font-semibold",
19
48
  };
20
49
 
21
- const sizeClass = $derived(
22
- sizeClasses[level as keyof typeof sizeClasses] || "text-base",
50
+ const headingClasses = $derived(
51
+ cn(`text-headline ${sizeClasses[visualSize] ?? sizeClasses[6]} ${className}`),
23
52
  );
24
53
  </script>
25
54
 
26
- <svelte:element
27
- this={Tag}
28
- class="text-headline font-nunito {sizeClass}"
29
- style="font-weight: {fontWeight};"
30
- >
31
- {text}
55
+ <!--
56
+ The font family comes from `--font-family-sans`, applied on `body`. This
57
+ component used to carry its own `font-family: "Nunito Sans"` in a local
58
+ <style> block, which meant a consumer who rebranded the font token got a
59
+ rebranded body and Nunito Sans headings.
60
+ -->
61
+ <svelte:element this={Tag} class={headingClasses} {...restProps}>
62
+ {#if children}
63
+ {@render children()}
64
+ {:else}
65
+ {text}
66
+ {/if}
32
67
  </svelte:element>
33
-
34
- <style>
35
- .font-nunito {
36
- font-family: "Nunito Sans", ui-sans-serif, system-ui, sans-serif;
37
- }
38
- </style>
@@ -1,7 +1,20 @@
1
- interface Props {
2
- level?: number;
3
- text: string;
4
- }
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ /** Semantic heading level — also the default visual size. */
4
+ type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
5
+ type Props = Omit<HTMLAttributes<HTMLHeadingElement>, "class"> & {
6
+ level?: HeadingLevel;
7
+ /**
8
+ * Visual size, when it should differ from the semantic level — an `h2`
9
+ * that needs to look like an `h4` keeps the document outline correct
10
+ * without forcing the wrong tag.
11
+ */
12
+ size?: HeadingLevel;
13
+ /** Heading text. Ignored when `children` is provided. */
14
+ text?: string;
15
+ class?: string;
16
+ children?: Snippet;
17
+ };
5
18
  declare const Heading: import("svelte").Component<Props, {}, "">;
6
19
  type Heading = ReturnType<typeof Heading>;
7
20
  export default Heading;
@@ -2,6 +2,7 @@
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;
@@ -28,50 +29,42 @@
28
29
 
29
30
  const isDisabled = $derived(disabled || loading);
30
31
 
32
+ /**
33
+ * Square, on the same height scale as Button / Input / Select, so an icon
34
+ * button sits flush in a toolbar beside a text button of the same size.
35
+ * Previously these were 8–16px shorter than their own Button.
36
+ */
31
37
  const sizeClass = $derived.by(() => {
32
- if (size === "sm") {
33
- return {
34
- padding: "p-2",
35
- radius: "rounded-md",
36
- spinner: "size-4",
37
- };
38
- } else if (size === "lg") {
39
- return {
40
- padding: "p-3",
41
- radius: "rounded-xl",
42
- spinner: "size-6",
43
- };
44
- } else {
45
- return {
46
- padding: "p-2.5",
47
- radius: "rounded-lg",
48
- spinner: "size-5",
49
- };
50
- }
38
+ if (size === "sm") return { box: "size-8", spinner: "size-3.5" };
39
+ if (size === "lg") return { box: "size-12", spinner: "size-5" };
40
+ return { box: "size-10", spinner: "size-4" };
51
41
  });
52
42
 
43
+ const disabledClass =
44
+ "disabled:bg-action-disabled disabled:text-action-disabled-text disabled:border-transparent disabled:shadow-none disabled:cursor-not-allowed disabled:active:scale-100";
45
+
53
46
  const variantClass = $derived.by(() => {
54
- return variant === "primary"
55
- ? "bg-action-primary text-action-primary hover:bg-action-primary-hover active:bg-action-primary-active active:scale-[0.98]"
56
- : variant === "secondary"
57
- ? "bg-action-secondary text-headline hover:bg-action-secondary-hover active:bg-action-secondary-active active:scale-[0.98]"
58
- : variant === "danger"
59
- ? "bg-action-danger text-inverse hover:bg-action-danger-hover active:bg-action-danger-active active:scale-[0.98] focus-ring--danger"
60
- : variant === "ghost"
61
- ? "bg-transparent text-headline hover:bg-base-100 active:bg-base-200 active:scale-[0.98] focus-ring--muted disabled:text-disabled"
62
- : variant === "outline"
63
- ? "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"
64
- : variant === "link"
65
- ? "text-brand-700 active:text-brand-700 hover:underline disabled:text-disabled disabled:no-underline"
66
- : "bg-action-primary text-action-primary hover:bg-action-primary-hover active:bg-action-primary-active active:scale-[0.98]";
47
+ switch (variant) {
48
+ case "secondary":
49
+ return "bg-action-secondary text-headline hover:bg-action-secondary-hover active:bg-action-secondary-active active:scale-[0.98]";
50
+ case "danger":
51
+ 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";
52
+ case "ghost":
53
+ return "bg-transparent text-headline hover:bg-surface-hover active:bg-surface-active active:scale-[0.98] focus-ring--muted";
54
+ case "outline":
55
+ return "bg-transparent border border-border text-headline hover:bg-surface-hover hover:border-border-medium active:bg-surface-active active:scale-[0.98]";
56
+ case "link":
57
+ return "bg-transparent text-link hover:text-link-hover focus-ring--muted";
58
+ case "primary":
59
+ default:
60
+ return "bg-action-primary text-action-primary hover:bg-action-primary-hover active:bg-action-primary-active active:scale-[0.98]";
61
+ }
67
62
  });
68
63
 
69
64
  const buttonClasses = $derived.by(() => {
70
- const sizeStyles = sizeClass;
71
- const baseClasses =
72
- "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";
73
-
74
- return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.radius} ${variantClass} ${className}`.trim();
65
+ const base =
66
+ "inline-flex focus-ring items-center justify-center rounded-control shrink-0 transition-colors duration-150 cursor-pointer select-none";
67
+ return cn(`${base} ${sizeClass.box} ${variantClass} ${disabledClass} ${className}`);
75
68
  });
76
69
  </script>
77
70
 
@@ -2,6 +2,7 @@
2
2
  import { CheckCircle, AlertTriangle, AlertCircle } from "@lucide/svelte";
3
3
  import type { SemanticVariant, SizeVariant } from "../types/variants.js";
4
4
  import { generateId } from "../util/ssr-safe.js";
5
+ import { cn } from "../util/cn.js";
5
6
 
6
7
  interface Props {
7
8
  /** Omit to auto-generate; pair with FormField `id` when used inside FormField. */
@@ -49,28 +50,18 @@
49
50
  const inputId = $derived(idProp ?? fallbackId);
50
51
  const isDisabled = $derived(disabled || loading);
51
52
 
53
+ /**
54
+ * Same fixed height scale as Button, IconButton and Select (32 / 40 / 48),
55
+ * so a field and its submit button are the same height in a row. These
56
+ * used to be 38 / 46 / 50 while Button was 40 / 48 / 64.
57
+ */
52
58
  const sizeClass = $derived(() => {
53
59
  if (size === "sm") {
54
- return {
55
- padding: "px-4 py-2",
56
- text: "text-sm",
57
- leading: "leading-5",
58
- spinner: "size-4",
59
- };
60
+ return { box: "h-8 px-3", text: "text-sm", spinner: "size-3.5" };
60
61
  } else if (size === "lg") {
61
- return {
62
- padding: "px-4 py-3",
63
- text: "text-base",
64
- leading: "leading-6",
65
- spinner: "size-5",
66
- };
62
+ return { box: "h-12 px-4", text: "text-base", spinner: "size-5" };
67
63
  } else {
68
- return {
69
- padding: "px-4 py-2.5",
70
- text: "text-base",
71
- leading: "leading-6",
72
- spinner: "size-5",
73
- };
64
+ return { box: "h-10 px-3", text: "text-sm", spinner: "size-4" };
74
65
  }
75
66
  });
76
67
 
@@ -86,30 +77,31 @@
86
77
 
87
78
  const inputClasses = $derived(() => {
88
79
  const sizeStyles = sizeClass();
89
- const trailingPad = loading
90
- ? size === "sm"
91
- ? "pr-9"
92
- : "pr-10"
93
- : "";
80
+ const trailingPad = loading ? (size === "sm" ? "pr-9" : "pr-10") : "";
81
+ /**
82
+ * The field is a raised surface (`bg-input` = white in light, an inset
83
+ * step in dark). It used to be a grey fill on a grey page, which read
84
+ * as disabled. `min-w-48` is gone: it forced a 192px floor on every
85
+ * field regardless of the layout around it.
86
+ */
94
87
  const baseClasses =
95
- "focus-ring w-full min-w-48 border bg-input hover:bg-input-hover focus-visible:bg-input-focus disabled:bg-input-disabled rounded-lg transition-all duration-200 placeholder:text-description text-body focus:outline-none focus-visible:outline-none disabled:opacity-50 disabled:cursor-not-allowed";
88
+ "focus-ring w-full border bg-input hover:bg-input-hover focus-visible:bg-input-focus disabled:bg-input-disabled rounded-control transition-colors duration-150 placeholder:text-input-placeholder text-body focus:outline-none focus-visible:outline-none disabled:text-action-disabled-text disabled:cursor-not-allowed";
96
89
 
97
- return `${baseClasses} ${sizeStyles.padding} ${trailingPad} ${sizeStyles.text} ${sizeStyles.leading} ${variantClass()} ${className}`.trim();
90
+ return cn(`${baseClasses} ${sizeStyles.box} ${trailingPad} ${sizeStyles.text} ${variantClass()} ${className}`);
98
91
  });
99
92
 
100
93
  const labelClasses = $derived(
101
- () => "block text-sm font-medium text-label mb-1",
94
+ () => "block text-sm font-medium text-label mb-2",
102
95
  );
103
96
 
97
+ // Message text uses the `-text` step (700), which clears 4.5:1 on a page
98
+ // surface; the solid fill step (600) is for fills, not for small text.
104
99
  const messageClasses = $derived(() => {
105
- if (variant === "error") {
106
- return "text-error text-sm mt-1 flex items-center gap-1.5";
107
- } else if (variant === "success") {
108
- return "text-success text-sm mt-1 flex items-center gap-1.5";
109
- } else if (variant === "warning") {
110
- return "text-warning text-sm mt-1 flex items-center gap-1.5";
111
- }
112
- return "text-description text-sm mt-1 flex items-center gap-1.5";
100
+ const base = "text-sm mt-2 flex items-center gap-2";
101
+ if (variant === "error") return `text-error-text ${base}`;
102
+ if (variant === "success") return `text-success-text ${base}`;
103
+ if (variant === "warning") return `text-warning-text ${base}`;
104
+ return `text-description ${base}`;
113
105
  });
114
106
 
115
107
  const getIcon = $derived(() => {
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import ListItemRow, { type ListItemData } from "./ListItem.svelte";
3
+ import { cn } from "../util/cn.js";
3
4
 
4
5
  export type ListItem = ListItemData;
5
6
 
@@ -9,6 +10,8 @@
9
10
  ariaLabel?: string;
10
11
  selectedId?: string;
11
12
  showArrow?: boolean;
13
+ class?: string;
14
+ /** @deprecated use `class`. */
12
15
  className?: string;
13
16
  onclick?: (item: ListItem, event: MouseEvent) => void;
14
17
  }
@@ -18,13 +21,18 @@
18
21
  ariaLabel = "List items",
19
22
  selectedId = "",
20
23
  showArrow = true,
21
- className = "",
24
+ class: classAttr = "",
25
+ className: legacyClass = "",
22
26
  onclick,
23
27
  ...restProps
24
28
  }: Props = $props();
25
29
 
30
+ /** `class` is the public prop; `className` is a deprecated alias.
31
+ * Both are merged here so existing call sites keep working. */
32
+ const className = $derived(cn(`${classAttr} ${legacyClass}`));
33
+
26
34
  const listClasses = $derived(
27
- `space-y-1 overflow-hidden rounded-xl ${className}`.trim(),
35
+ cn(`space-y-1 overflow-hidden rounded-container ${className}`),
28
36
  );
29
37
  </script>
30
38
 
@@ -6,6 +6,8 @@ interface Props {
6
6
  ariaLabel?: string;
7
7
  selectedId?: string;
8
8
  showArrow?: boolean;
9
+ class?: string;
10
+ /** @deprecated use `class`. */
9
11
  className?: string;
10
12
  onclick?: (item: ListItem, event: MouseEvent) => void;
11
13
  }
@@ -38,10 +38,10 @@
38
38
 
39
39
  const itemClasses = $derived.by(() => {
40
40
  const baseClasses =
41
- "group focus-ring flex w-full items-center gap-3 rounded-xl border border-border px-4 py-3 pr-5 text-left transition-all duration-150";
41
+ "group focus-ring flex w-full items-center gap-3 rounded-container border border-border px-4 py-3 pr-5 text-left transition-all duration-150";
42
42
  const interactiveClasses = item.disabled
43
43
  ? "cursor-not-allowed opacity-50"
44
- : "cursor-pointer focus-visible:bg-base-100";
44
+ : "cursor-pointer focus-visible:bg-surface-hover";
45
45
  const selectedClasses = selected
46
46
  ? "bg-action-primary-subtle border-brand-500"
47
47
  : "";
@@ -1,15 +1,17 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from "svelte";
3
3
 
4
+ import { cn } from "../util/cn.js";
4
5
  interface Props {
5
- /** e.g. `rounded-full` for avatars; default is `rounded-md` for icons. */
6
+ /** e.g. `rounded-full` for avatars; default is `rounded-control` for icons. */
6
7
  class?: string;
7
8
  children?: Snippet;
8
9
  }
9
10
 
10
11
  let { class: className = "", children }: Props = $props();
11
12
 
12
- const roundingClass = $derived(className.trim() ? className.trim() : "rounded-md");
13
+ // cn lets a call-site radius win outright, instead of replacing the default wholesale.
14
+ const roundingClass = $derived(cn("rounded-control", className));
13
15
  </script>
14
16
 
15
17
  <span
@@ -1,6 +1,6 @@
1
1
  import type { Snippet } from "svelte";
2
2
  interface Props {
3
- /** e.g. `rounded-full` for avatars; default is `rounded-md` for icons. */
3
+ /** e.g. `rounded-full` for avatars; default is `rounded-control` for icons. */
4
4
  class?: string;
5
5
  children?: Snippet;
6
6
  }
@@ -1,9 +1,13 @@
1
1
  <script lang="ts">
2
+ import { cn } from "../util/cn.js";
3
+
2
4
  interface Props {
3
5
  src: string;
4
6
  alt?: string;
5
7
  width?: number | string;
6
8
  height?: number | string;
9
+ class?: string;
10
+ /** @deprecated use `class`. */
7
11
  className?: string;
8
12
  }
9
13
 
@@ -12,16 +16,21 @@
12
16
  alt = "",
13
17
  width = "100%",
14
18
  height = "auto",
15
- className = "",
19
+ class: classAttr = "",
20
+ className: legacyClass = "",
16
21
  children,
17
22
  ...restProps
18
23
  } = $props<Props & { children?: any }>();
24
+
25
+ /** `class` is the public prop; `className` is a deprecated alias.
26
+ * Both are merged here so existing call sites keep working. */
27
+ const className = $derived(`${classAttr} ${legacyClass}`.trim());
19
28
  </script>
20
29
 
21
30
  <img
22
31
  {src}
23
32
  {alt}
24
- class="w-full h-auto object-cover {className}"
33
+ class={cn("w-full h-auto object-cover", className)}
25
34
  style="width: {typeof width === 'number'
26
35
  ? width + 'px'
27
36
  : width}; height: {typeof height === 'number'
@@ -3,6 +3,8 @@ interface Props {
3
3
  alt?: string;
4
4
  width?: number | string;
5
5
  height?: number | string;
6
+ class?: string;
7
+ /** @deprecated use `class`. */
6
8
  className?: string;
7
9
  }
8
10
  type $$ComponentProps = Props & {
@@ -2,6 +2,8 @@
2
2
  import { generateId } from "../util/ssr-safe.js";
3
3
 
4
4
  interface Props {
5
+ /** Extra classes for the host element. */
6
+ class?: string;
5
7
  value?: number;
6
8
  max?: number;
7
9
  size?: "sm" | "md" | "lg";
@@ -9,6 +11,7 @@
9
11
  }
10
12
 
11
13
  let {
14
+ class: className = "",
12
15
  value = 0,
13
16
  max = 100,
14
17
  size = "md",
@@ -27,7 +30,7 @@
27
30
  });
28
31
  </script>
29
32
 
30
- <div>
33
+ <div class={className} {...restProps}>
31
34
  {#if label}
32
35
  <div class="flex justify-between items-center mb-2">
33
36
  <label for={progressId} class="text-sm font-medium text-label"
@@ -1,4 +1,6 @@
1
1
  interface Props {
2
+ /** Extra classes for the host element. */
3
+ class?: string;
2
4
  value?: number;
3
5
  max?: number;
4
6
  size?: "sm" | "md" | "lg";
@@ -4,6 +4,8 @@
4
4
  import { RADIO_CHECKED_DOT_CLASSES } from "./selection-control.styles";
5
5
 
6
6
  interface Props {
7
+ /** Extra classes for the host element. */
8
+ class?: string;
7
9
  id?: string;
8
10
  name?: string;
9
11
  value?: string;
@@ -16,6 +18,7 @@
16
18
  }
17
19
 
18
20
  let {
21
+ class: className = "",
19
22
  id,
20
23
  name = "",
21
24
  value = "",
@@ -46,6 +49,7 @@
46
49
  {onChange}
47
50
  {onchange}
48
51
  {mark}
52
+ class={className}
49
53
  {...restProps}
50
54
  />
51
55
 
@@ -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;