zabi-components 5.0.17 → 5.0.19

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 (67) hide show
  1. package/README.md +124 -23
  2. package/dist/SSRSafe.svelte +0 -3
  3. package/dist/atoms/Badge.svelte +19 -34
  4. package/dist/atoms/Badge.svelte.d.ts +6 -5
  5. package/dist/atoms/Button.svelte +10 -9
  6. package/dist/atoms/Button.svelte.d.ts +3 -2
  7. package/dist/atoms/Card.svelte +74 -23
  8. package/dist/atoms/Card.svelte.d.ts +3 -1
  9. package/dist/atoms/CardContent.svelte +24 -0
  10. package/dist/atoms/CardContent.svelte.d.ts +8 -0
  11. package/dist/atoms/CardDescription.svelte +24 -0
  12. package/dist/atoms/CardDescription.svelte.d.ts +8 -0
  13. package/dist/atoms/CardFooter.svelte +24 -0
  14. package/dist/atoms/CardFooter.svelte.d.ts +8 -0
  15. package/dist/atoms/CardHeader.svelte +24 -0
  16. package/dist/atoms/CardHeader.svelte.d.ts +8 -0
  17. package/dist/atoms/CardTitle.svelte +33 -0
  18. package/dist/atoms/CardTitle.svelte.d.ts +9 -0
  19. package/dist/atoms/Checkbox.svelte +0 -4
  20. package/dist/atoms/CodeBlock.svelte +0 -3
  21. package/dist/atoms/ColorPicker.svelte +33 -65
  22. package/dist/atoms/FeatureCard.svelte +0 -10
  23. package/dist/atoms/FeatureCard.svelte.d.ts +0 -8
  24. package/dist/atoms/Heading.svelte +0 -2
  25. package/dist/atoms/Input.svelte +3 -12
  26. package/dist/atoms/Input.svelte.d.ts +3 -2
  27. package/dist/atoms/Progress.svelte +0 -3
  28. package/dist/atoms/Select.svelte +0 -14
  29. package/dist/atoms/Textarea.svelte +3 -9
  30. package/dist/atoms/Textarea.svelte.d.ts +3 -1
  31. package/dist/atoms/ThemeToggle.svelte +0 -7
  32. package/dist/atoms/Toast.svelte +0 -1
  33. package/dist/atoms/Toggle.svelte +0 -6
  34. package/dist/atoms/Tooltip.svelte +0 -12
  35. package/dist/atoms/index.d.ts +5 -0
  36. package/dist/atoms/index.js +5 -1
  37. package/dist/components/atoms/index.d.ts +5 -0
  38. package/dist/components/atoms/index.d.ts.map +1 -1
  39. package/dist/components/index.d.ts.map +1 -1
  40. package/dist/components/molecules/index.d.ts.map +1 -1
  41. package/dist/components/organisms/index.d.ts.map +1 -1
  42. package/dist/index.js +0 -23
  43. package/dist/molecules/Alert.svelte +3 -13
  44. package/dist/molecules/Alert.svelte.d.ts +2 -1
  45. package/dist/molecules/ComponentDemo.svelte +0 -2
  46. package/dist/molecules/ContactForm.svelte +21 -12
  47. package/dist/molecules/Dropdown.svelte +100 -6
  48. package/dist/molecules/Dropdown.svelte.d.ts +1 -0
  49. package/dist/molecules/Form.svelte +0 -1
  50. package/dist/molecules/ImageUpload.svelte +0 -4
  51. package/dist/molecules/Modal.svelte +29 -5
  52. package/dist/molecules/SlideUp.svelte +0 -2
  53. package/dist/molecules/Tabs.svelte +0 -2
  54. package/dist/molecules/index.js +0 -1
  55. package/dist/organisms/Navbar.svelte +0 -7
  56. package/dist/organisms/Navigation.svelte +0 -6
  57. package/dist/organisms/index.js +0 -1
  58. package/dist/routes/lib/focus-utils.d.ts +23 -0
  59. package/dist/routes/lib/focus-utils.ts +112 -0
  60. package/dist/routes/lib/variant-utils.ts +3 -1
  61. package/dist/zabi-components-colors.css +246 -178
  62. package/dist/zabi-components-theme-dark-only.css +11 -2
  63. package/dist/zabi-components-theme-dark.css +11 -2
  64. package/dist/zabi-components-theme-only.css +32 -24
  65. package/dist/zabi-components-theme.css +32 -24
  66. package/dist/zabi-components.css +259 -83
  67. package/package.json +5 -4
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from "svelte";
2
+ interface Props {
3
+ className?: string;
4
+ children?: Snippet;
5
+ }
6
+ declare const CardHeader: import("svelte").Component<Props, {}, "">;
7
+ type CardHeader = ReturnType<typeof CardHeader>;
8
+ export default CardHeader;
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+
4
+ interface Props {
5
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
6
+ className?: string;
7
+ children?: Snippet;
8
+ }
9
+
10
+ let {
11
+ level = 3,
12
+ className = "",
13
+ children,
14
+ ...restProps
15
+ }: Props = $props();
16
+
17
+ const headingTag = $derived(`h${level}`);
18
+ const headingClasses = $derived(() => {
19
+ const baseClasses = "text-2xl font-semibold leading-none tracking-tight text-headline";
20
+ return `${baseClasses} ${className}`.trim();
21
+ });
22
+ </script>
23
+
24
+ <svelte:element
25
+ this={headingTag}
26
+ class={headingClasses()}
27
+ {...restProps}
28
+ >
29
+ {#if children}
30
+ {@render children()}
31
+ {/if}
32
+ </svelte:element>
33
+
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from "svelte";
2
+ interface Props {
3
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
4
+ className?: string;
5
+ children?: Snippet;
6
+ }
7
+ declare const CardTitle: import("svelte").Component<Props, {}, "">;
8
+ type CardTitle = ReturnType<typeof CardTitle>;
9
+ export default CardTitle;
@@ -1,5 +1,4 @@
1
1
  <script lang="ts">
2
- // SSR-safe ID generation
3
2
  function generateId(prefix: string = "id"): string {
4
3
  if (typeof window !== "undefined") {
5
4
  return `${prefix}-${Math.random().toString(36).substr(2, 9)}`;
@@ -25,10 +24,8 @@
25
24
  ...restProps
26
25
  }: Props = $props();
27
26
 
28
- // Generate unique ID - SSR safe (call directly, not in $state)
29
27
  const checkboxId = generateId("checkbox");
30
28
 
31
- // Checkbox container classes matching M3 design
32
29
  const checkboxContainerClasses = $derived(() => {
33
30
  const baseClasses =
34
31
  "relative inline-flex items-center justify-center w-5 h-5 rounded transition-all duration-200";
@@ -39,7 +36,6 @@
39
36
  return `${baseClasses} cursor-pointer`;
40
37
  });
41
38
 
42
- // Checkbox border and background classes matching M3 design
43
39
  const checkboxBoxClasses = $derived(() => {
44
40
  if (checked) {
45
41
  return disabled
@@ -33,7 +33,6 @@
33
33
  class="code-block relative bg-base-900 rounded-lg overflow-hidden {className}"
34
34
  {...restProps}
35
35
  >
36
- <!-- Header -->
37
36
  <div
38
37
  class="flex items-center justify-between px-4 py-2 bg-base-800 border-b border-base-700"
39
38
  >
@@ -81,7 +80,6 @@
81
80
  {/if}
82
81
  </div>
83
82
 
84
- <!-- Code content -->
85
83
  <pre class="p-4 overflow-x-auto text-sm text-base-100 leading-relaxed"><code
86
84
  class="language-{language}">{@html code}</code
87
85
  ></pre>
@@ -92,7 +90,6 @@
92
90
  font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
93
91
  }
94
92
 
95
- /* Basic syntax highlighting for common languages */
96
93
  :global(.language-svelte .token.tag),
97
94
  :global(.language-html .token.tag) {
98
95
  color: #f92672;
@@ -1,12 +1,5 @@
1
1
  <script lang="ts">
2
- // SSR-safe ID generation
3
- function generateId(prefix: string = "id"): string {
4
- if (typeof window !== "undefined") {
5
- return `${prefix}-${Math.random().toString(36).substr(2, 9)}`;
6
- } else {
7
- return `${prefix}-ssr-${Date.now()}`;
8
- }
9
- }
2
+ import Input from "./Input.svelte";
10
3
 
11
4
  interface Props {
12
5
  value?: string;
@@ -25,90 +18,65 @@
25
18
  ...restProps
26
19
  }: Props = $props();
27
20
 
28
- // Generate unique ID - SSR safe
29
- const inputId = generateId("color-picker");
30
-
31
- // Hex color validation
32
21
  function isValidHex(hex: string): boolean {
33
22
  const hexPattern = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
34
23
  return hexPattern.test(hex);
35
24
  }
36
25
 
37
- // Handle hex input changes
38
- function handleHexInput(event: Event) {
39
- const target = event.target as HTMLInputElement;
40
- const hexValue = target.value;
26
+ const variant = $derived(() => {
27
+ if (value && !isValidHex(value)) {
28
+ return "error";
29
+ }
30
+ return "default";
31
+ });
41
32
 
42
- if (hexValue === "" || isValidHex(hexValue)) {
43
- value = hexValue;
44
- if (onchange) onchange(event);
33
+ const message = $derived(() => {
34
+ if (value && !isValidHex(value)) {
35
+ return "Please enter a valid hex color (e.g., #ff0000 or #f00)";
45
36
  }
37
+ return "";
38
+ });
39
+
40
+ function handleInput(event: Event) {
41
+ if (onchange) onchange(event);
46
42
  }
47
43
 
48
- // Handle hex input blur - validate and format
49
- function handleHexBlur(event: Event) {
44
+ function handleBlur(event: Event) {
50
45
  const target = event.target as HTMLInputElement;
51
46
  let hexValue = target.value.trim();
52
47
 
53
- // Add # if missing
54
48
  if (hexValue && !hexValue.startsWith("#")) {
55
49
  hexValue = "#" + hexValue;
56
50
  }
57
51
 
58
- // Validate and update
59
52
  if (hexValue === "" || isValidHex(hexValue)) {
60
53
  value = hexValue;
61
- target.value = hexValue;
62
- } else {
63
- // Reset to current value if invalid
64
- target.value = value;
54
+ if (onchange) onchange(event);
65
55
  }
66
56
  }
67
-
68
- // Input classes
69
- const inputClasses = $derived(() => {
70
- const baseClasses =
71
- "w-full px-3 py-2 bg-input hover:bg-input-hover focus:bg-input-focus disabled:bg-input-disabled rounded-lg text-sm text-body placeholder:text-description transition-colors focus:outline-none focus:ring-2 focus:ring-offset-0 disabled:opacity-50 disabled:cursor-not-allowed";
72
- const stateClasses =
73
- isValidHex(value) || value === ""
74
- ? "border-base-300 focus:border-brand-500 focus:ring-brand-500"
75
- : "border-red-300 focus:border-red-500 focus:ring-red-500";
76
-
77
- return `${baseClasses} ${stateClasses}`.trim();
78
- });
79
57
  </script>
80
58
 
81
- <div class="space-y-2" {...restProps}>
82
- {#if label}
83
- <label for={inputId} class="block text-sm font-medium text-label">
84
- {label}
85
- </label>
86
- {/if}
87
-
88
- <div class="flex items-center space-x-2">
89
- <input
90
- id={inputId}
91
- type="text"
92
- {placeholder}
93
- {value}
94
- oninput={handleHexInput}
95
- onblur={handleHexBlur}
96
- {disabled}
97
- class={inputClasses()}
98
- aria-label="Hex color input"
99
- />
59
+ <div {...restProps}>
60
+ <div class="flex items-start gap-2">
61
+ <div class="flex-1">
62
+ <Input
63
+ bind:value
64
+ {label}
65
+ {placeholder}
66
+ {disabled}
67
+ variant={variant()}
68
+ message={message()}
69
+ oninput={handleInput}
70
+ onblur={handleBlur}
71
+ aria-label="Hex color input"
72
+ />
73
+ </div>
100
74
  {#if value && isValidHex(value)}
101
75
  <div
102
- class="w-8 h-8 rounded border-2 border-base-300 shrink-0"
76
+ class="w-8 h-8 rounded border-2 border-base-300 shrink-0 mt-6"
103
77
  style="background-color: {value};"
104
78
  aria-label="Color preview"
105
79
  ></div>
106
80
  {/if}
107
81
  </div>
108
-
109
- {#if value && !isValidHex(value)}
110
- <p class="text-xs text-red-500">
111
- Please enter a valid hex color (e.g., #ff0000 or #f00)
112
- </p>
113
- {/if}
114
82
  </div>
@@ -1,19 +1,10 @@
1
1
  <script lang="ts">
2
2
  import { onMount } from "svelte";
3
3
 
4
- /**
5
- * FeatureCard component - SSR-safe implementation
6
- * @component
7
- */
8
-
9
4
  interface Props {
10
- /** Icon to display */
11
5
  icon?: string;
12
- /** Title of the feature */
13
6
  title: string;
14
- /** Description of the feature */
15
7
  description: string;
16
- /** Additional CSS classes */
17
8
  className?: string;
18
9
  }
19
10
 
@@ -52,7 +43,6 @@
52
43
  </div>
53
44
  </div>
54
45
  {:else}
55
- <!-- SSR fallback -->
56
46
  <div
57
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}"
58
48
  {...restProps}
@@ -1,15 +1,7 @@
1
- /**
2
- * FeatureCard component - SSR-safe implementation
3
- * @component
4
- */
5
1
  interface Props {
6
- /** Icon to display */
7
2
  icon?: string;
8
- /** Title of the feature */
9
3
  title: string;
10
- /** Description of the feature */
11
4
  description: string;
12
- /** Additional CSS classes */
13
5
  className?: string;
14
6
  }
15
7
  declare const FeatureCard: import("svelte").Component<Props, {}, "">;
@@ -6,11 +6,9 @@
6
6
 
7
7
  let { level = 1, text }: Props = $props();
8
8
 
9
- // Automatic weight and size based on heading level
10
9
  const fontWeight = $derived(level <= 2 ? "700" : "500");
11
10
  const Tag = $derived(`h${level}`);
12
11
 
13
- // Size classes for different heading levels
14
12
  const sizeClasses = {
15
13
  1: "text-4xl",
16
14
  2: "text-3xl",
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { CheckCircle, AlertTriangle, AlertCircle } from "@lucide/svelte";
3
+ import type { SemanticVariant, SizeVariant } from "../../types/variants.js";
3
4
 
4
- // SSR-safe ID generation
5
5
  function generateId(prefix: string = "id"): string {
6
6
  if (typeof window !== "undefined") {
7
7
  return `${prefix}-${Math.random().toString(36).substr(2, 9)}`;
@@ -17,8 +17,8 @@
17
17
  label?: string;
18
18
  placeholder?: string;
19
19
  disabled?: boolean;
20
- size?: "sm" | "md" | "lg";
21
- variant?: "default" | "success" | "warning" | "error";
20
+ size?: SizeVariant;
21
+ variant?: SemanticVariant;
22
22
  message?: string;
23
23
  oninput?: (event: Event) => void;
24
24
  }
@@ -37,10 +37,8 @@
37
37
  ...restProps
38
38
  }: Props = $props();
39
39
 
40
- // Generate unique ID - SSR safe (call directly, not in $state)
41
40
  const inputId = generateId("input");
42
41
 
43
- // Size classes matching M3 design specifications
44
42
  const sizeClass = $derived(() => {
45
43
  if (size === "sm") {
46
44
  return {
@@ -55,7 +53,6 @@
55
53
  leading: "leading-6",
56
54
  };
57
55
  } else {
58
- // default md
59
56
  return {
60
57
  padding: "px-4 py-2.5",
61
58
  text: "text-base",
@@ -64,7 +61,6 @@
64
61
  }
65
62
  });
66
63
 
67
- // Variant classes using semantic colors
68
64
  const variantClass = $derived(() => {
69
65
  return variant === "success"
70
66
  ? "border-success focus:border-success focus:ring-success"
@@ -75,7 +71,6 @@
75
71
  : "border-0 focus:ring-2 focus:ring-brand-500"; // default - no border
76
72
  });
77
73
 
78
- // Input classes matching M3 design
79
74
  const inputClasses = $derived(() => {
80
75
  const sizeStyles = sizeClass();
81
76
  const baseClasses =
@@ -84,12 +79,10 @@
84
79
  return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.text} ${sizeStyles.leading} ${variantClass()}`.trim();
85
80
  });
86
81
 
87
- // Label classes using semantic text colors
88
82
  const labelClasses = $derived(
89
83
  () => "block text-sm font-medium text-label mb-1",
90
84
  );
91
85
 
92
- // Message classes based on variant
93
86
  const messageClasses = $derived(() => {
94
87
  if (variant === "error") {
95
88
  return "text-error text-sm mt-1 flex items-center gap-1.5";
@@ -101,7 +94,6 @@
101
94
  return "text-description text-sm mt-1 flex items-center gap-1.5";
102
95
  });
103
96
 
104
- // Get icon component based on variant
105
97
  const getIcon = $derived(() => {
106
98
  if (variant === "error") return AlertCircle;
107
99
  if (variant === "success") return CheckCircle;
@@ -113,7 +105,6 @@
113
105
  const target = event.target as HTMLInputElement;
114
106
  value = target.value;
115
107
 
116
- // Call the parent's oninput handler if provided
117
108
  if (oninput) {
118
109
  oninput(event);
119
110
  }
@@ -1,3 +1,4 @@
1
+ import type { SemanticVariant, SizeVariant } from "../../types/variants.js";
1
2
  interface Props {
2
3
  value?: string;
3
4
  type?: string;
@@ -5,8 +6,8 @@ interface Props {
5
6
  label?: string;
6
7
  placeholder?: string;
7
8
  disabled?: boolean;
8
- size?: "sm" | "md" | "lg";
9
- variant?: "default" | "success" | "warning" | "error";
9
+ size?: SizeVariant;
10
+ variant?: SemanticVariant;
10
11
  message?: string;
11
12
  oninput?: (event: Event) => void;
12
13
  }
@@ -14,17 +14,14 @@
14
14
  ...restProps
15
15
  }: Props = $props();
16
16
 
17
- // Generate unique ID - SSR safe
18
17
  let progressId = $state(
19
18
  typeof window !== "undefined"
20
19
  ? `progress-${Math.random().toString(36).substr(2, 9)}`
21
20
  : `progress-ssr-${Date.now()}`,
22
21
  );
23
22
 
24
- // Calculate percentage
25
23
  let percentage = $derived(Math.min(Math.max((value / max) * 100, 0), 100));
26
24
 
27
- // Simple size classes
28
25
  let sizeClasses = $derived({
29
26
  sm: "h-1",
30
27
  md: "h-2",
@@ -7,7 +7,6 @@
7
7
  AlertCircle,
8
8
  } from "@lucide/svelte";
9
9
 
10
- // SSR-safe ID generation
11
10
  function generateId(prefix: string = "id"): string {
12
11
  if (typeof window !== "undefined") {
13
12
  return `${prefix}-${Math.random().toString(36).substr(2, 9)}`;
@@ -47,7 +46,6 @@
47
46
 
48
47
  let isOpen = $state(false);
49
48
 
50
- // Size classes matching M3 design specifications
51
49
  const sizeClass = $derived(() => {
52
50
  if (size === "sm") {
53
51
  return {
@@ -62,7 +60,6 @@
62
60
  leading: "leading-6",
63
61
  };
64
62
  } else {
65
- // default md
66
63
  return {
67
64
  padding: "px-4 py-2.5",
68
65
  text: "text-base",
@@ -71,7 +68,6 @@
71
68
  }
72
69
  });
73
70
 
74
- // Variant classes using semantic colors
75
71
  const variantClass = $derived(() => {
76
72
  return variant === "success"
77
73
  ? "border-success focus:border-success focus:ring-success"
@@ -82,7 +78,6 @@
82
78
  : "border-0 focus:ring-2 focus:ring-brand-500"; // default - no border
83
79
  });
84
80
 
85
- // Trigger button classes matching M3 design
86
81
  const triggerClasses = $derived(() => {
87
82
  const sizeStyles = sizeClass();
88
83
  const baseClasses =
@@ -91,12 +86,10 @@
91
86
  return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.text} ${sizeStyles.leading} ${variantClass()}`.trim();
92
87
  });
93
88
 
94
- // Label classes using semantic text colors
95
89
  const labelClasses = $derived(
96
90
  () => "block text-sm font-medium text-label mb-1",
97
91
  );
98
92
 
99
- // Message classes based on variant
100
93
  const messageClasses = $derived(() => {
101
94
  if (variant === "error") {
102
95
  return "text-error text-sm mt-1 flex items-center gap-1.5";
@@ -108,7 +101,6 @@
108
101
  return "text-description text-sm mt-1 flex items-center gap-1.5";
109
102
  });
110
103
 
111
- // Get icon component based on variant
112
104
  const getIcon = $derived(() => {
113
105
  if (variant === "error") return AlertCircle;
114
106
  if (variant === "success") return CheckCircle;
@@ -116,10 +108,8 @@
116
108
  return null;
117
109
  });
118
110
 
119
- // Generate unique ID - SSR safe
120
111
  const selectId = generateId("select");
121
112
 
122
- // Get selected option label
123
113
  const selectedLabel = $derived(() => {
124
114
  if (isEmpty()) {
125
115
  return String(placeholder || "Select an option");
@@ -130,7 +120,6 @@
130
120
  : String(placeholder || "Select an option");
131
121
  });
132
122
 
133
- // Check if value is empty
134
123
  const isEmpty = $derived(() => {
135
124
  return value === undefined || value === null || value === "";
136
125
  });
@@ -140,7 +129,6 @@
140
129
  value = optionValue;
141
130
  isOpen = false;
142
131
 
143
- // Create a synthetic event for onchange
144
132
  if (onchange) {
145
133
  const syntheticEvent = new Event("change", { bubbles: true });
146
134
  Object.defineProperty(syntheticEvent, "target", {
@@ -157,7 +145,6 @@
157
145
  isOpen = !isOpen;
158
146
  }
159
147
 
160
- // Close dropdown when clicking outside
161
148
  function handleClickOutside(event: MouseEvent) {
162
149
  if (
163
150
  isOpen &&
@@ -167,7 +154,6 @@
167
154
  }
168
155
  }
169
156
 
170
- // Handle escape key
171
157
  function handleKeydown(event: KeyboardEvent) {
172
158
  if (event.key === "Escape" && isOpen) {
173
159
  isOpen = false;
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { CheckCircle, AlertTriangle, AlertCircle } from "@lucide/svelte";
3
+ import type { SemanticVariant, SizeVariant } from "../../types/variants.js";
3
4
 
4
- // SSR-safe ID generation
5
5
  function generateId(prefix: string = "id"): string {
6
6
  if (typeof window !== "undefined") {
7
7
  return `${prefix}-${Math.random().toString(36).substr(2, 9)}`;
@@ -17,7 +17,8 @@
17
17
  placeholder?: string;
18
18
  disabled?: boolean;
19
19
  rows?: number;
20
- variant?: "default" | "success" | "warning" | "error";
20
+ size?: SizeVariant;
21
+ variant?: SemanticVariant;
21
22
  message?: string;
22
23
  oninput?: (event: Event) => void;
23
24
  }
@@ -35,10 +36,8 @@
35
36
  ...restProps
36
37
  }: Props = $props();
37
38
 
38
- // Generate unique ID - SSR safe (call directly, not in $state)
39
39
  const textareaId = generateId("textarea");
40
40
 
41
- // Variant classes using semantic colors
42
41
  const variantClass = $derived(() => {
43
42
  return variant === "success"
44
43
  ? "border-success focus:border-success focus:ring-success"
@@ -49,7 +48,6 @@
49
48
  : "border-0 focus:ring-2 focus:ring-brand-500"; // default - no border
50
49
  });
51
50
 
52
- // Textarea classes matching M3 design
53
51
  const textareaClasses = $derived(() => {
54
52
  const baseClasses =
55
53
  "w-full bg-input hover:bg-input-hover focus:bg-input-focus disabled:bg-input-disabled rounded-lg transition-all duration-200 placeholder:text-description text-body focus:outline-none focus:ring-offset-0 disabled:opacity-50 disabled:cursor-not-allowed resize-y px-4 py-2.5 text-base leading-6";
@@ -57,12 +55,10 @@
57
55
  return `${baseClasses} ${variantClass()}`.trim();
58
56
  });
59
57
 
60
- // Label classes using semantic text colors
61
58
  const labelClasses = $derived(
62
59
  () => "block text-sm font-medium text-label mb-1",
63
60
  );
64
61
 
65
- // Message classes based on variant
66
62
  const messageClasses = $derived(() => {
67
63
  if (variant === "error") {
68
64
  return "text-error text-sm mt-1 flex items-center gap-1.5";
@@ -74,7 +70,6 @@
74
70
  return "text-description text-sm mt-1 flex items-center gap-1.5";
75
71
  });
76
72
 
77
- // Get icon component based on variant
78
73
  const getIcon = $derived(() => {
79
74
  if (variant === "error") return AlertCircle;
80
75
  if (variant === "success") return CheckCircle;
@@ -86,7 +81,6 @@
86
81
  const target = event.target as HTMLTextAreaElement;
87
82
  value = target.value;
88
83
 
89
- // Call the parent's oninput handler if provided
90
84
  if (oninput) {
91
85
  oninput(event);
92
86
  }
@@ -1,3 +1,4 @@
1
+ import type { SemanticVariant, SizeVariant } from "../../types/variants.js";
1
2
  interface Props {
2
3
  value?: string;
3
4
  name?: string;
@@ -5,7 +6,8 @@ interface Props {
5
6
  placeholder?: string;
6
7
  disabled?: boolean;
7
8
  rows?: number;
8
- variant?: "default" | "success" | "warning" | "error";
9
+ size?: SizeVariant;
10
+ variant?: SemanticVariant;
9
11
  message?: string;
10
12
  oninput?: (event: Event) => void;
11
13
  }
@@ -2,7 +2,6 @@
2
2
  import { onMount } from "svelte";
3
3
  import { Sun, Moon } from "@lucide/svelte";
4
4
 
5
- // SSR-safe utilities
6
5
  function safeLocalStorage(): Storage | undefined {
7
6
  return typeof window !== "undefined" ? localStorage : undefined;
8
7
  }
@@ -28,7 +27,6 @@
28
27
 
29
28
  onMount(() => {
30
29
  mounted = true;
31
- // Check for saved theme preference or default to light mode
32
30
  const storage = safeLocalStorage();
33
31
  if (storage) {
34
32
  const savedTheme = storage.getItem("theme");
@@ -71,7 +69,6 @@
71
69
  }
72
70
  }
73
71
 
74
- // Size classes matching M3 design
75
72
  const sizeClass = $derived(() => {
76
73
  if (size === "sm") {
77
74
  return {
@@ -84,7 +81,6 @@
84
81
  icon: 24
85
82
  };
86
83
  } else {
87
- // default md
88
84
  return {
89
85
  button: "w-10 h-10",
90
86
  icon: 20
@@ -92,14 +88,12 @@
92
88
  }
93
89
  });
94
90
 
95
- // Variant classes using semantic colors
96
91
  const variantClass = $derived(() => {
97
92
  if (variant === "ghost") {
98
93
  return "bg-transparent hover:bg-base-100 border-0";
99
94
  } else if (variant === "outline") {
100
95
  return "bg-base-50 hover:bg-base-100 border border-border";
101
96
  } else {
102
- // default
103
97
  return "bg-base-50 hover:bg-base-100 border-0";
104
98
  }
105
99
  });
@@ -141,7 +135,6 @@
141
135
  {/if}
142
136
  </button>
143
137
  {:else}
144
- <!-- SSR fallback -->
145
138
  <button
146
139
  class="w-10 h-10 bg-base-50 rounded-lg flex items-center justify-center text-label cursor-pointer"
147
140
  aria-label="Theme toggle"
@@ -16,7 +16,6 @@
16
16
 
17
17
  let isVisible = $state(true);
18
18
 
19
- // Simple type classes
20
19
  const typeClasses = {
21
20
  success: "bg-green-100 border-green-300 text-success",
22
21
  error: "bg-red-100 border-red-300 text-error",