zabi-components 5.0.14 → 5.0.17

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.
package/README.md CHANGED
@@ -40,6 +40,57 @@ npm install svelte@^5.38.10
40
40
  npm install @sveltejs/kit@^2.0.0 # Optional, for SvelteKit projects
41
41
  ```
42
42
 
43
+ ## Theme Setup
44
+
45
+ Zabi Components includes a comprehensive theme system with Tailwind CSS v4. You need to import the theme before using components.
46
+
47
+ ### Quick Setup
48
+
49
+ **For projects with existing Tailwind CSS:**
50
+
51
+ ```css
52
+ /* app.css */
53
+ @import "tailwindcss";
54
+ @import 'zabi-components/theme-only';
55
+ @import 'zabi-components/dist/zabi-components.css';
56
+ ```
57
+
58
+ **For standalone projects (no Tailwind setup):**
59
+
60
+ ```css
61
+ /* app.css */
62
+ @import 'zabi-components/theme';
63
+ @import 'zabi-components/dist/zabi-components.css';
64
+ ```
65
+
66
+ ### With Dark Mode
67
+
68
+ ```css
69
+ @import "tailwindcss";
70
+ @import 'zabi-components/theme-only';
71
+ @import 'zabi-components/theme-dark-only'; /* Add dark mode support */
72
+ @import 'zabi-components/dist/zabi-components.css';
73
+ ```
74
+
75
+ ### Extending the Theme
76
+
77
+ You can extend the theme with custom colors and fonts:
78
+
79
+ ```css
80
+ @import "tailwindcss";
81
+ @import 'zabi-components/theme-only';
82
+
83
+ /* Your custom theme extensions */
84
+ @theme {
85
+ --font-family-title: 'Your Font', sans-serif;
86
+ --color-custom-primary: #ff0000;
87
+ }
88
+
89
+ @import 'zabi-components/dist/zabi-components.css';
90
+ ```
91
+
92
+ > 📖 **Complete Theme Guide**: See [THEME.md](./THEME.md) for comprehensive documentation on theme usage, customization, dark mode, and advanced patterns.
93
+
43
94
  ## Import Methods
44
95
 
45
96
  Zabi Components supports multiple import patterns:
@@ -45,18 +45,18 @@
45
45
  // Note: Using text-white instead of text-inverse to ensure light text in both light and dark modes
46
46
  const variantClass =
47
47
  variant === "success"
48
- ? "bg-success border-[var(--color-success)] text-white"
48
+ ? "bg-success border-success text-card"
49
49
  : variant === "warning"
50
- ? "bg-warning-weak border-[var(--color-warning-weak)] text-white"
50
+ ? "bg-warning border-warning text-card"
51
51
  : variant === "error"
52
- ? "bg-error border-[var(--color-error)] text-white"
52
+ ? "bg-error border-error text-card"
53
53
  : variant === "info"
54
- ? "bg-info border-[var(--color-info)] text-white"
54
+ ? "bg-info border-info text-card"
55
55
  : variant === "neutral"
56
- ? "bg-neutral border-[var(--color-neutral)] text-white"
56
+ ? "bg-neutral border-neutral text-card"
57
57
  : variant === "energetic"
58
- ? "bg-energetic-weak border-[var(--color-energetic-weak)] text-white"
59
- : "bg-secondary border-[var(--color-secondary)] text-white"; // default
58
+ ? "bg-energetic border-energetic text-card"
59
+ : "bg-secondary border-secondary text-card"; // default
60
60
 
61
61
  return `${baseClasses} ${sizeClass} ${variantClass}`.trim();
62
62
  });
@@ -75,22 +75,22 @@
75
75
  <span class={classes()}>
76
76
  {#if showIcon}
77
77
  {#if variant === "success"}
78
- <Check size={iconSize()} class="{iconSpacingClass()} text-white" />
78
+ <Check size={iconSize()} class="{iconSpacingClass()} text-card" />
79
79
  {:else if variant === "warning"}
80
80
  <AlertTriangle
81
81
  size={iconSize()}
82
- class="{iconSpacingClass()} text-white"
82
+ class="{iconSpacingClass()} text-card"
83
83
  />
84
84
  {:else if variant === "error"}
85
- <X size={iconSize()} class="{iconSpacingClass()} text-white" />
85
+ <X size={iconSize()} class="{iconSpacingClass()} text-card" />
86
86
  {:else if variant === "info"}
87
- <Info size={iconSize()} class="{iconSpacingClass()} text-white" />
87
+ <Info size={iconSize()} class="{iconSpacingClass()} text-card" />
88
88
  {:else if variant === "neutral"}
89
- <Info size={iconSize()} class="{iconSpacingClass()} text-white" />
89
+ <Info size={iconSize()} class="{iconSpacingClass()} text-card" />
90
90
  {:else if variant === "energetic"}
91
- <Info size={iconSize()} class="{iconSpacingClass()} text-white" />
91
+ <Info size={iconSize()} class="{iconSpacingClass()} text-card" />
92
92
  {:else}
93
- <Info size={iconSize()} class="{iconSpacingClass()} text-white" />
93
+ <Info size={iconSize()} class="{iconSpacingClass()} text-card" />
94
94
  {/if}
95
95
  {/if}
96
96
  {text}
@@ -32,28 +32,28 @@
32
32
  leading: "leading-5",
33
33
  tracking: "tracking-[0.1px]",
34
34
  radius: "rounded-lg",
35
- gap: "gap-2"
35
+ gap: "gap-2",
36
36
  };
37
37
  } else if (size === "lg") {
38
38
  return {
39
- padding: "px-12 py-8",
40
- text: "text-2xl",
39
+ padding: "px-8 py-4",
40
+ text: "text-lg",
41
41
  font: "font-normal",
42
42
  leading: "leading-8",
43
43
  tracking: "tracking-normal",
44
- radius: "rounded-2xl",
45
- gap: "gap-3"
44
+ radius: "rounded-xl",
45
+ gap: "gap-3",
46
46
  };
47
47
  } else {
48
48
  // default md
49
49
  return {
50
- padding: "px-6 py-4",
50
+ padding: "px-5 py-3",
51
51
  text: "text-base",
52
52
  font: "font-medium",
53
53
  leading: "leading-6",
54
54
  tracking: "tracking-[0.15px]",
55
55
  radius: "rounded-lg",
56
- gap: "gap-2"
56
+ gap: "gap-2",
57
57
  };
58
58
  }
59
59
  });
@@ -67,7 +67,7 @@
67
67
  : variant === "danger"
68
68
  ? "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"
69
69
  : variant === "ghost"
70
- ? "bg-transparent text-headline hover:bg-surface-hover active:bg-surface-active focus:ring-2 focus:ring-stone-500 focus:ring-offset-2 disabled:text-disabled"
70
+ ? "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"
71
71
  : "bg-action-primary text-inverse hover:bg-action-primary-hover active:bg-action-primary-active focus:ring-2 focus:ring-brand-500 focus:ring-offset-2"; // default primary
72
72
  });
73
73
 
@@ -28,9 +28,9 @@
28
28
 
29
29
  const cardClasses = $derived(() => {
30
30
  const baseClasses =
31
- "rounded-lg transition-all duration-200 min-w-64 bg-surface-elevated shadow-sm";
31
+ "rounded-lg transition-all duration-200 min-w-64 bg-card shadow-sm";
32
32
  const interactiveClasses = onclick
33
- ? "cursor-pointer hover:shadow-lg hover:bg-surface-hover"
33
+ ? "cursor-pointer hover:shadow-lg hover:bg-card-hover"
34
34
  : "";
35
35
  const widthClasses = fullWidth ? "w-full" : "";
36
36
 
@@ -30,8 +30,9 @@
30
30
 
31
31
  // Checkbox container classes matching M3 design
32
32
  const checkboxContainerClasses = $derived(() => {
33
- const baseClasses = "relative inline-flex items-center justify-center w-5 h-5 rounded transition-all duration-200";
34
-
33
+ const baseClasses =
34
+ "relative inline-flex items-center justify-center w-5 h-5 rounded transition-all duration-200";
35
+
35
36
  if (disabled) {
36
37
  return `${baseClasses} opacity-50 cursor-not-allowed`;
37
38
  }
@@ -41,13 +42,13 @@
41
42
  // Checkbox border and background classes matching M3 design
42
43
  const checkboxBoxClasses = $derived(() => {
43
44
  if (checked) {
44
- return disabled
45
- ? "border-0 bg-brand-300"
46
- : "border-0 bg-brand-600";
45
+ return disabled
46
+ ? "border-0 opacity-50 bg-brand-500"
47
+ : "border-0 bg-brand-500";
47
48
  }
48
49
  return disabled
49
- ? "border-2 border-stone-400 bg-transparent"
50
- : "border-2 border-stone-400 bg-transparent";
50
+ ? "border-2 border-base-400 bg-transparent"
51
+ : "border-2 border-base-400 bg-transparent";
51
52
  });
52
53
 
53
54
  function handleChange(event: Event) {
@@ -63,7 +64,9 @@
63
64
  <div class="flex items-center gap-2">
64
65
  <label
65
66
  for={checkboxId}
66
- class="flex items-center gap-2 cursor-pointer {disabled ? 'opacity-50 cursor-not-allowed' : ''}"
67
+ class="flex items-center gap-2 cursor-pointer {disabled
68
+ ? 'opacity-50 cursor-not-allowed'
69
+ : ''}"
67
70
  >
68
71
  <div class={checkboxContainerClasses()}>
69
72
  <input
@@ -78,16 +81,16 @@
78
81
  />
79
82
  <div class="absolute inset-0 {checkboxBoxClasses()} rounded"></div>
80
83
  {#if checked}
81
- <svg
82
- class="absolute w-3 h-3 text-white pointer-events-none z-10"
83
- fill="none"
84
- stroke="currentColor"
84
+ <svg
85
+ class="absolute w-3 h-3 text-base-50 pointer-events-none z-10"
86
+ fill="none"
87
+ stroke="currentColor"
85
88
  viewBox="0 0 24 24"
86
89
  >
87
- <path
88
- stroke-linecap="round"
89
- stroke-linejoin="round"
90
- stroke-width="2.5"
90
+ <path
91
+ stroke-linecap="round"
92
+ stroke-linejoin="round"
93
+ stroke-width="2.5"
91
94
  d="M5 13l4 4L19 7"
92
95
  />
93
96
  </svg>
@@ -30,12 +30,12 @@
30
30
  </script>
31
31
 
32
32
  <div
33
- class="code-block relative bg-gray-900 rounded-lg overflow-hidden {className}"
33
+ class="code-block relative bg-base-900 rounded-lg overflow-hidden {className}"
34
34
  {...restProps}
35
35
  >
36
36
  <!-- Header -->
37
37
  <div
38
- class="flex items-center justify-between px-4 py-2 bg-gray-800 border-b border-gray-700"
38
+ class="flex items-center justify-between px-4 py-2 bg-base-800 border-b border-base-700"
39
39
  >
40
40
  <div class="flex items-center gap-2">
41
41
  <div class="w-3 h-3 rounded-full bg-red-500"></div>
@@ -45,7 +45,7 @@
45
45
  {#if showCopyButton}
46
46
  <button
47
47
  onclick={copyToClipboard}
48
- class="flex items-center gap-2 px-3 py-1 text-xs text-gray-300 hover:text-white hover:bg-gray-700 rounded transition-colors duration-200"
48
+ 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"
49
49
  aria-label="Copy code to clipboard"
50
50
  >
51
51
  {#if copied}
@@ -82,7 +82,7 @@
82
82
  </div>
83
83
 
84
84
  <!-- Code content -->
85
- <pre class="p-4 overflow-x-auto text-sm text-gray-100 leading-relaxed"><code
85
+ <pre class="p-4 overflow-x-auto text-sm text-base-100 leading-relaxed"><code
86
86
  class="language-{language}">{@html code}</code
87
87
  ></pre>
88
88
  </div>
@@ -68,11 +68,11 @@
68
68
  // Input classes
69
69
  const inputClasses = $derived(() => {
70
70
  const baseClasses =
71
- "w-full px-3 py-2 border rounded-lg text-sm transition-colors focus:outline-none focus:ring-2 focus:ring-brand-200";
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
72
  const stateClasses =
73
73
  isValidHex(value) || value === ""
74
- ? "border-gray-300 focus:border-brand-500"
75
- : "border-red-300 focus:border-red-500 focus:ring-red-200";
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
76
 
77
77
  return `${baseClasses} ${stateClasses}`.trim();
78
78
  });
@@ -99,7 +99,7 @@
99
99
  />
100
100
  {#if value && isValidHex(value)}
101
101
  <div
102
- class="w-8 h-8 rounded border-2 border-gray-300 shrink-0"
102
+ class="w-8 h-8 rounded border-2 border-base-300 shrink-0"
103
103
  style="background-color: {value};"
104
104
  aria-label="Color preview"
105
105
  ></div>
@@ -34,7 +34,7 @@
34
34
 
35
35
  {#if mounted}
36
36
  <div
37
- class="p-6 rounded-lg bg-surface-level-0 border border-border hover:border-primary/20 hover:shadow-sm transition-colors duration-200 {className}"
37
+ class="p-6 rounded-lg bg-base-50 border border-border hover:border-primary/20 hover:shadow-sm transition-colors duration-200 {className}"
38
38
  {...restProps}
39
39
  >
40
40
  <div class="flex items-start gap-4">
@@ -54,7 +54,7 @@
54
54
  {:else}
55
55
  <!-- SSR fallback -->
56
56
  <div
57
- class="p-6 rounded-lg bg-gray-100 border border-gray-300 hover:border-gray-400 hover:shadow-sm transition-colors duration-200 {className}"
57
+ 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
58
  {...restProps}
59
59
  >
60
60
  <div class="flex items-start gap-4">
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { CheckCircle, AlertTriangle, AlertCircle } from "@lucide/svelte";
3
-
3
+
4
4
  // SSR-safe ID generation
5
5
  function generateId(prefix: string = "id"): string {
6
6
  if (typeof window !== "undefined") {
@@ -46,20 +46,20 @@
46
46
  return {
47
47
  padding: "px-4 py-2",
48
48
  text: "text-sm",
49
- leading: "leading-5"
49
+ leading: "leading-5",
50
50
  };
51
51
  } else if (size === "lg") {
52
52
  return {
53
53
  padding: "px-4 py-3",
54
54
  text: "text-base",
55
- leading: "leading-6"
55
+ leading: "leading-6",
56
56
  };
57
57
  } else {
58
58
  // default md
59
59
  return {
60
60
  padding: "px-4 py-2.5",
61
61
  text: "text-base",
62
- leading: "leading-6"
62
+ leading: "leading-6",
63
63
  };
64
64
  }
65
65
  });
@@ -79,7 +79,7 @@
79
79
  const inputClasses = $derived(() => {
80
80
  const sizeStyles = sizeClass();
81
81
  const baseClasses =
82
- "w-full bg-brand-100 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";
82
+ "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";
83
83
 
84
84
  return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.text} ${sizeStyles.leading} ${variantClass()}`.trim();
85
85
  });
@@ -44,7 +44,7 @@
44
44
 
45
45
  <div
46
46
  id={progressId}
47
- class="w-full bg-gray-200 rounded-full overflow-hidden {sizeClasses[
47
+ class="w-full bg-base-200 rounded-full overflow-hidden {sizeClasses[
48
48
  size
49
49
  ]}"
50
50
  role="progressbar"
@@ -1,6 +1,11 @@
1
1
  <script lang="ts">
2
2
  import Dropdown from "../molecules/Dropdown.svelte";
3
- import { ChevronDown, CheckCircle, AlertTriangle, AlertCircle } from "@lucide/svelte";
3
+ import {
4
+ ChevronDown,
5
+ CheckCircle,
6
+ AlertTriangle,
7
+ AlertCircle,
8
+ } from "@lucide/svelte";
4
9
 
5
10
  // SSR-safe ID generation
6
11
  function generateId(prefix: string = "id"): string {
@@ -48,20 +53,20 @@
48
53
  return {
49
54
  padding: "px-4 py-2",
50
55
  text: "text-sm",
51
- leading: "leading-5"
56
+ leading: "leading-5",
52
57
  };
53
58
  } else if (size === "lg") {
54
59
  return {
55
60
  padding: "px-4 py-3",
56
61
  text: "text-base",
57
- leading: "leading-6"
62
+ leading: "leading-6",
58
63
  };
59
64
  } else {
60
65
  // default md
61
66
  return {
62
67
  padding: "px-4 py-2.5",
63
68
  text: "text-base",
64
- leading: "leading-6"
69
+ leading: "leading-6",
65
70
  };
66
71
  }
67
72
  });
@@ -81,7 +86,7 @@
81
86
  const triggerClasses = $derived(() => {
82
87
  const sizeStyles = sizeClass();
83
88
  const baseClasses =
84
- "w-full bg-brand-100 rounded-lg transition-all duration-200 text-body focus:outline-none focus:ring-offset-0 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-between";
89
+ "w-full bg-input hover:bg-input-hover focus:bg-input-focus disabled:bg-input-disabled rounded-lg transition-all duration-200 text-body focus:outline-none focus:ring-offset-0 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-between";
85
90
 
86
91
  return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.text} ${sizeStyles.leading} ${variantClass()}`.trim();
87
92
  });
@@ -120,7 +125,9 @@
120
125
  return String(placeholder || "Select an option");
121
126
  }
122
127
  const selected = options.find((opt) => opt.value === value);
123
- return selected?.label ? String(selected.label) : String(placeholder || "Select an option");
128
+ return selected?.label
129
+ ? String(selected.label)
130
+ : String(placeholder || "Select an option");
124
131
  });
125
132
 
126
133
  // Check if value is empty
@@ -138,7 +145,7 @@
138
145
  const syntheticEvent = new Event("change", { bubbles: true });
139
146
  Object.defineProperty(syntheticEvent, "target", {
140
147
  value: { value: optionValue },
141
- enumerable: true
148
+ enumerable: true,
142
149
  });
143
150
  (onchange as (event: Event) => void)(syntheticEvent);
144
151
  }
@@ -152,7 +159,10 @@
152
159
 
153
160
  // Close dropdown when clicking outside
154
161
  function handleClickOutside(event: MouseEvent) {
155
- if (isOpen && !(event.target as HTMLElement).closest('.select-container')) {
162
+ if (
163
+ isOpen &&
164
+ !(event.target as HTMLElement).closest(".select-container")
165
+ ) {
156
166
  isOpen = false;
157
167
  }
158
168
  }
@@ -172,7 +182,7 @@
172
182
  <label for={selectId} class={labelClasses()}>{label}</label>
173
183
  {/if}
174
184
 
175
- <Dropdown isOpen={isOpen} placement="bottom-start">
185
+ <Dropdown {isOpen} placement="bottom-start">
176
186
  {#snippet trigger()}
177
187
  <button
178
188
  type="button"
@@ -184,12 +194,21 @@
184
194
  aria-expanded={isOpen}
185
195
  aria-describedby={message ? `${selectId}-message` : undefined}
186
196
  >
187
- <span class="text-left flex-1 {isEmpty() ? 'text-description' : 'text-body'}">
188
- {isEmpty() ? placeholder : (options.find((opt) => opt.value === value)?.label || placeholder)}
197
+ <span
198
+ class="text-left flex-1 {isEmpty()
199
+ ? 'text-description'
200
+ : 'text-body'}"
201
+ >
202
+ {isEmpty()
203
+ ? placeholder
204
+ : options.find((opt) => opt.value === value)?.label ||
205
+ placeholder}
189
206
  </span>
190
207
  <ChevronDown
191
208
  size={20}
192
- class="text-description transition-transform duration-200 {isOpen ? 'rotate-180' : ''}"
209
+ class="text-description transition-transform duration-200 {isOpen
210
+ ? 'rotate-180'
211
+ : ''}"
193
212
  />
194
213
  </button>
195
214
  {/snippet}
@@ -198,7 +217,11 @@
198
217
  {#each options as option (option.value)}
199
218
  <button
200
219
  type="button"
201
- class="w-full text-left px-4 py-2 text-body hover:bg-brand-50 transition-colors rounded-md my-0.5 {option.disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'} {value === option.value ? 'bg-brand-50' : ''}"
220
+ class="w-full text-left px-4 py-2 text-body hover:bg-base-100 transition-colors rounded-md my-0.5 {option.disabled
221
+ ? 'opacity-50 cursor-not-allowed'
222
+ : 'cursor-pointer'} {value === option.value
223
+ ? 'bg-base-100'
224
+ : ''}"
202
225
  onclick={() => handleOptionClick(option.value)}
203
226
  disabled={option.disabled}
204
227
  >
@@ -217,4 +240,4 @@
217
240
  <span>{message}</span>
218
241
  </p>
219
242
  {/if}
220
- </div>
243
+ </div>
@@ -14,7 +14,7 @@
14
14
  </script>
15
15
 
16
16
  <div
17
- class="animate-pulse bg-gray-200 rounded {className}"
17
+ class="animate-pulse bg-base-200 rounded {className}"
18
18
  style="width: {width}; height: {height};"
19
19
  {...restProps}
20
20
  ></div>
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { CheckCircle, AlertTriangle, AlertCircle } from "@lucide/svelte";
3
-
3
+
4
4
  // SSR-safe ID generation
5
5
  function generateId(prefix: string = "id"): string {
6
6
  if (typeof window !== "undefined") {
@@ -52,7 +52,7 @@
52
52
  // Textarea classes matching M3 design
53
53
  const textareaClasses = $derived(() => {
54
54
  const baseClasses =
55
- "w-full bg-brand-100 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";
55
+ "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";
56
56
 
57
57
  return `${baseClasses} ${variantClass()}`.trim();
58
58
  });
@@ -95,12 +95,12 @@
95
95
  // Variant classes using semantic colors
96
96
  const variantClass = $derived(() => {
97
97
  if (variant === "ghost") {
98
- return "bg-transparent hover:bg-surface-hover border-0";
98
+ return "bg-transparent hover:bg-base-100 border-0";
99
99
  } else if (variant === "outline") {
100
- return "bg-surface-elevated hover:bg-surface-hover border border-border";
100
+ return "bg-base-50 hover:bg-base-100 border border-border";
101
101
  } else {
102
102
  // default
103
- return "bg-surface-elevated hover:bg-surface-hover border-0";
103
+ return "bg-base-50 hover:bg-base-100 border-0";
104
104
  }
105
105
  });
106
106
 
@@ -121,7 +121,7 @@
121
121
  focus:ring-2
122
122
  focus:ring-brand-500
123
123
  focus:ring-offset-2
124
- focus:ring-offset-surface
124
+ focus:ring-offset-base-50
125
125
  `.trim().replace(/\s+/g, " ");
126
126
  });
127
127
  </script>
@@ -143,7 +143,7 @@
143
143
  {:else}
144
144
  <!-- SSR fallback -->
145
145
  <button
146
- class="w-10 h-10 bg-surface-elevated rounded-lg flex items-center justify-center text-label cursor-pointer"
146
+ class="w-10 h-10 bg-base-50 rounded-lg flex items-center justify-center text-label cursor-pointer"
147
147
  aria-label="Theme toggle"
148
148
  type="button"
149
149
  {...restProps}
@@ -46,7 +46,7 @@
46
46
  {#if closable}
47
47
  <button
48
48
  type="button"
49
- class="ml-3 text-gray-400 hover:text-gray-600"
49
+ class="ml-3 text-base-400 hover:text-base-600"
50
50
  onclick={closeToast}
51
51
  aria-label="Close notification"
52
52
  >
@@ -39,21 +39,19 @@
39
39
  if (onchange) onchange({ checked });
40
40
  }
41
41
 
42
- // Toggle button classes matching M3 design
43
42
  const toggleButtonClasses = $derived(() =>
44
43
  [
45
44
  "relative inline-flex w-10 h-6 flex-shrink-0 cursor-pointer rounded-full border-0 transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2",
46
- checked ? "bg-brand-600" : "bg-stone-300",
45
+ checked ? "bg-action-primary" : "bg-base-400",
47
46
  disabled && "opacity-50 cursor-not-allowed",
48
47
  ]
49
48
  .filter(Boolean)
50
49
  .join(" "),
51
50
  );
52
51
 
53
- // Toggle thumb classes matching M3 design
54
52
  const toggleThumbClasses = $derived(() => {
55
53
  const baseClasses =
56
- "pointer-events-none absolute top-0.5 left-0.5 w-5 h-5 rounded-full bg-white transition-transform duration-200 ease-in-out";
54
+ "pointer-events-none absolute top-0.5 left-0.5 w-5 h-5 rounded-full bg-card transition-transform duration-200 ease-in-out";
57
55
  const positionClasses = checked ? "translate-x-4" : "translate-x-0";
58
56
 
59
57
  return `${baseClasses} ${positionClasses}`.trim();