svelte-comp 1.0.7 → 1.1.2

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
@@ -45,13 +45,13 @@ Add to `src/app.css`:
45
45
 
46
46
  ## 📁 Components included
47
47
 
48
- Accordion • Button • Card • Carousel • CheckBox • CodeView • ColorPicker
49
- DatePicker • Dialog • Field • FilePicker • Form • Hamburger • Menu
50
- PaginatedCard • Pagination • ProgressBarRadioSelect Slider
51
- Splitter • Switch • Tabs • Table • ThemeToggle • TimePicker
52
- Toast • Tooltip
48
+ Accordion • Button • Card • Carousel • CheckBox • CodeView • ColorPicker
49
+ DatePicker • Dialog • Field • FilePicker • Form • Hamburger • Menu
50
+ PaginatedCard • Pagination • PrimaryColorSelectProgressBarProgressCircle
51
+ Radio • Select • Slider • Splitter • Switch • Tabs • Table • ThemeToggle •
52
+ TimePicker • Toast • Tooltip
53
53
 
54
- Full component list in repository.
54
+ Full component list in repository
55
55
 
56
56
  ---
57
57
 
@@ -88,3 +88,5 @@ MIT License
88
88
  ## 🔗 Links
89
89
 
90
90
  GitHub: [https://github.com/MaestroFusion360/svelte-comp](https://github.com/MaestroFusion360/svelte-comp)
91
+
92
+ Demo: [https://maestrofusion360.github.io/svelte-comp/](https://maestrofusion360.github.io/svelte-comp/)
@@ -0,0 +1,555 @@
1
+ <script lang="ts">
2
+ import Accordion from "./lib/Accordion.svelte";
3
+ import Button from "./lib/Button.svelte";
4
+ import Card from "./lib/Card.svelte";
5
+ import Carousel from "./lib/Carousel.svelte";
6
+ import ColorPicker from "./lib/ColorPicker.svelte";
7
+ import DatePicker from "./lib/DatePicker.svelte";
8
+ import Field from "./lib/Field.svelte";
9
+ import PrimaryColorSelect from "./lib/PrimaryColorSelect.svelte";
10
+ import ProgressBar from "./lib/ProgressBar.svelte";
11
+ import ProgressCircle from "./lib/ProgressCircle.svelte";
12
+ import Select from "./lib/Select.svelte";
13
+ import Slider from "./lib/Slider.svelte";
14
+ import Switch from "./lib/Switch.svelte";
15
+ import Table from "./lib/Table.svelte";
16
+ import Tabs from "./lib/Tabs.svelte";
17
+ import ThemeToggle from "./lib/ThemeToggle.svelte";
18
+ import TimePicker from "./lib/TimePicker.svelte";
19
+ import Toast from "./lib/Toast.svelte";
20
+ import Tooltip from "./lib/Tooltip.svelte";
21
+ import type { ToastVariant } from "./lib/types";
22
+
23
+ const tabs = [
24
+ { id: "overview", label: "Overview" },
25
+ { id: "team", label: "Team" },
26
+ { id: "table", label: "Data" },
27
+ ];
28
+ let activeTab = $state(tabs[0].id);
29
+
30
+ let sliderValue = $state(68);
31
+ let autoplay = $state(true);
32
+
33
+ const planOptions = [
34
+ { label: "Starter", value: "starter" },
35
+ { label: "Pro", value: "pro" },
36
+ { label: "Enterprise", value: "enterprise" },
37
+ ];
38
+
39
+ let selectedPlan = $state(planOptions[1].value);
40
+ let featureName = $state("Dashboard 2.0");
41
+ let contactEmail = $state("team@studio.dev");
42
+ let dateValue = $state<string | null>(null);
43
+ let timeValue = $state<string | null>(null);
44
+ let accentColor = $state<string | null>("#7c3aed");
45
+
46
+ const accordionItems = [
47
+ {
48
+ title: "Composition",
49
+ content:
50
+ "Card, Tabs, Table and Carousel let you assemble complex screens without extra layout work.",
51
+ },
52
+ {
53
+ title: "Forms",
54
+ content:
55
+ "Field, Select, DatePicker, TimePicker and ColorPicker share spacing, tokens and state handling.",
56
+ },
57
+ {
58
+ title: "Feedback",
59
+ content:
60
+ "ProgressBar, ProgressCircle, Toast and Dialog keep users informed without noise.",
61
+ },
62
+ ];
63
+
64
+ const carouselItems = [
65
+ {
66
+ title: "Smooth forms",
67
+ content:
68
+ "Clean fields, tight spacing and built-in hints help ship surveys in minutes.",
69
+ },
70
+ {
71
+ title: "Smart navigation",
72
+ content:
73
+ "Tabs and Accordion keep content nearby while Carousel saves horizontal space.",
74
+ },
75
+ {
76
+ title: "Status signals",
77
+ content:
78
+ "Toast and progress indicators deliver context quickly without blocking flows.",
79
+ },
80
+ ];
81
+
82
+ const tableColumns = [
83
+ { key: "name", label: "Feature", width: "42%" },
84
+ { key: "owner", label: "Owner" },
85
+ { key: "status", label: "Status" },
86
+ { key: "eta", label: "ETA", align: "end" },
87
+ ] as const;
88
+
89
+ const tableRows = [
90
+ { id: 1, name: "Onboarding", owner: "Anna", status: "Ready", eta: "Today" },
91
+ { id: 2, name: "Theming", owner: "Mark", status: "In progress", eta: "Fri" },
92
+ { id: 3, name: "Notifications", owner: "Oleg", status: "Testing", eta: "Thu" },
93
+ { id: 4, name: "Data tables", owner: "Ira", status: "In progress", eta: "Next week" },
94
+ { id: 5, name: "Carousel", owner: "Anton", status: "Design", eta: "In two weeks" },
95
+ { id: 6, name: "Accessibility", owner: "Sasha", status: "Review", eta: "Today" },
96
+ ];
97
+
98
+ const pageSize = 4;
99
+ let tablePage = $state(1);
100
+ const totalPages = $derived(
101
+ Math.max(1, Math.ceil(tableRows.length / pageSize))
102
+ );
103
+ const pagedRows = $derived(
104
+ tableRows.slice((tablePage - 1) * pageSize, tablePage * pageSize)
105
+ );
106
+
107
+ $effect(() => {
108
+ if (tablePage > totalPages) tablePage = totalPages;
109
+ if (tablePage < 1) tablePage = 1;
110
+ });
111
+
112
+ const quality = [
113
+ { label: "UI polish", value: 86 },
114
+ { label: "Accessibility", value: 72 },
115
+ { label: "Performance", value: 64 },
116
+ ];
117
+
118
+ const team = [
119
+ { name: "Anna", role: "Product", focus: "UX flows" },
120
+ { name: "Mark", role: "Frontend", focus: "Components" },
121
+ { name: "Oleg", role: "QA", focus: "Automation" },
122
+ ];
123
+
124
+ type ToastItem = { id: number; title?: string; message: string; variant: ToastVariant };
125
+ let toasts = $state<ToastItem[]>([]);
126
+ let toastId = 0;
127
+
128
+ function pushToast(variant: ToastVariant) {
129
+ const messageMap: Record<ToastVariant, string> = {
130
+ success: "Changes saved and ready to roll out.",
131
+ info: "Components share tokens, typography, and behavior.",
132
+ warning: "Double-check disabled, focus, and hover states.",
133
+ danger: "Tests caught a blocking issue. Investigate.",
134
+ };
135
+ const titleMap: Record<ToastVariant, string> = {
136
+ success: "Success",
137
+ info: "Heads up",
138
+ warning: "Warning",
139
+ danger: "Error",
140
+ };
141
+
142
+ const id = ++toastId;
143
+ toasts = [
144
+ ...toasts,
145
+ { id, variant, title: titleMap[variant], message: messageMap[variant] },
146
+ ];
147
+ }
148
+
149
+ function removeToast(id: number) {
150
+ toasts = toasts.filter((t) => t.id !== id);
151
+ }
152
+ </script>
153
+
154
+ <main class="min-h-screen bg-[var(--color-bg-page)] text-[var(--color-text-default)]">
155
+ <div class="pointer-events-none absolute inset-0 overflow-hidden">
156
+ <div class="absolute -left-10 top-10 h-64 w-64 rounded-full bg-[var(--color-bg-primary)]/10 blur-3xl"></div>
157
+ <div class="absolute right-0 bottom-0 h-72 w-72 rounded-full bg-[var(--color-bg-warning)]/20 blur-3xl"></div>
158
+ </div>
159
+
160
+ <div class="relative mx-auto max-w-6xl space-y-8 px-6 py-10">
161
+ <section
162
+ class="relative overflow-hidden rounded-[28px] border border-[var(--border-color-default)] bg-gradient-to-br from-[var(--color-bg-surface)] via-white/70 to-[var(--color-bg-muted)] shadow-[0_20px_60px_-25px_var(--shadow-color)] dark:from-[var(--color-bg-surface)] dark:via-slate-900/70 dark:to-slate-900/50"
163
+ >
164
+ <div
165
+ class="absolute inset-0 bg-[radial-gradient(circle_at_20%_20%,rgba(99,102,241,0.18),transparent_35%),radial-gradient(circle_at_80%_0%,rgba(16,185,129,0.14),transparent_25%)]"
166
+ ></div>
167
+
168
+ <div class="relative grid gap-8 p-8 md:p-10 lg:grid-cols-[1.1fr_0.9fr]">
169
+ <div class="space-y-4">
170
+ <p class="text-xs uppercase tracking-[0.25em] text-[var(--color-text-muted)]">
171
+ svelte-comp kit
172
+ </p>
173
+ <h1 class="text-3xl font-bold leading-tight md:text-4xl">
174
+ Component showcase
175
+ </h1>
176
+ <p class="text-[var(--color-text-muted)] md:w-3/4">
177
+ Toggle theme, tweak the primary color, and try the main lib components in a cohesive layout.
178
+ </p>
179
+
180
+ <div class="flex flex-wrap gap-3">
181
+ <Button variant="primary" onClick={() => pushToast("info")} sz="lg">
182
+ Launch interactive
183
+ </Button>
184
+ <Button variant="ghost" onClick={() => pushToast("warning")} sz="lg">
185
+ Remind to review
186
+ </Button>
187
+ </div>
188
+
189
+ <div class="flex flex-wrap gap-3 text-sm text-[var(--color-text-muted)]">
190
+ <span
191
+ class="inline-flex items-center gap-2 rounded-full border border-[var(--border-color-default)] bg-[var(--color-bg-surface)] px-3 py-2"
192
+ >
193
+ <span class="h-2 w-2 animate-pulse rounded-full bg-[var(--color-bg-primary)]"></span>
194
+ Live preview is on
195
+ </span>
196
+ <span
197
+ class="inline-flex items-center gap-2 rounded-full border border-[var(--border-color-default)] bg-[var(--color-bg-surface)] px-3 py-2"
198
+ >
199
+ <span class="h-2 w-2 rounded-full bg-[var(--color-bg-secondary)]"></span>
200
+ {tabs.length} sections below
201
+ </span>
202
+ </div>
203
+ </div>
204
+
205
+ <div
206
+ class="space-y-4 rounded-2xl border border-[var(--border-color-default)] bg-white/70 p-5 shadow-lg backdrop-blur dark:bg-slate-900/60"
207
+ >
208
+ <div class="flex items-start justify-between gap-3">
209
+ <div>
210
+ <p class="text-xs uppercase tracking-[0.15em] text-[var(--color-text-muted)]">
211
+ Theme
212
+ </p>
213
+ <p class="text-lg font-semibold">Mode and accent</p>
214
+ </div>
215
+
216
+ <Tooltip text="Toggles document-level theme" position="bottom">
217
+ <ThemeToggle class="relative shadow-sm" sz="sm" />
218
+ </Tooltip>
219
+ </div>
220
+
221
+ <PrimaryColorSelect class="w-full" />
222
+
223
+ <div
224
+ class="flex items-center justify-between rounded-xl border border-[var(--border-color-default)] bg-[var(--color-bg-muted)]/70 px-4 py-3"
225
+ >
226
+ <div>
227
+ <p class="text-xs uppercase tracking-[0.15em] text-[var(--color-text-muted)]">
228
+ Autoplay
229
+ </p>
230
+ <p class="text-sm font-medium">Carousel</p>
231
+ </div>
232
+ <Switch
233
+ checked={autoplay}
234
+ onChange={(v) => (autoplay = v)}
235
+ sz="sm"
236
+ rightLabel={autoplay ? "On" : "Off"}
237
+ />
238
+ </div>
239
+
240
+ <div class="grid gap-3 md:grid-cols-2">
241
+ <ProgressCircle value={sliderValue} label="Readiness" />
242
+ <ProgressBar value={sliderValue} label="Sprint focus" />
243
+ </div>
244
+ </div>
245
+ </div>
246
+ </section>
247
+
248
+ {#snippet controlsHeader()}
249
+ <div class="flex items-center justify-between gap-2">
250
+ <div>
251
+ <p class="text-xs uppercase tracking-[0.15em] text-[var(--color-text-muted)]">
252
+ Actions
253
+ </p>
254
+ <h2 class="text-lg font-semibold leading-tight">Quick triggers</h2>
255
+ </div>
256
+ <Tooltip text="Buttons use the full variant set" position="left">
257
+ <span
258
+ class="inline-flex h-9 w-9 items-center justify-center rounded-full bg-[var(--color-bg-muted)] text-sm font-semibold text-[var(--color-text-default)]"
259
+ >
260
+ ?
261
+ </span>
262
+ </Tooltip>
263
+ </div>
264
+ {/snippet}
265
+
266
+ {#snippet formHeader()}
267
+ <div class="flex items-center justify-between">
268
+ <div>
269
+ <p class="text-xs uppercase tracking-[0.15em] text-[var(--color-text-muted)]">
270
+ Form
271
+ </p>
272
+ <h2 class="text-lg font-semibold leading-tight">Mini brief</h2>
273
+ </div>
274
+ <Tooltip text="Field, Select, DatePicker, TimePicker and ColorPicker" position="left">
275
+ <span
276
+ class="inline-flex h-8 w-8 items-center justify-center rounded-full bg-[var(--color-bg-muted)] text-sm font-semibold text-[var(--color-text-default)]"
277
+ >
278
+ i
279
+ </span>
280
+ </Tooltip>
281
+ </div>
282
+ {/snippet}
283
+
284
+ <div class="grid gap-6 lg:grid-cols-2">
285
+ <Card header={controlsHeader}>
286
+ <div class="space-y-4">
287
+ <div class="flex flex-wrap gap-2">
288
+ <Button variant="primary" onClick={() => pushToast("success")}>Primary</Button>
289
+ <Button variant="secondary">Secondary</Button>
290
+ <Button variant="ghost">Ghost</Button>
291
+ <Button variant="pill">Pill</Button>
292
+ <Button variant="danger" onClick={() => pushToast("danger")}>Danger</Button>
293
+ </div>
294
+
295
+ <div
296
+ class="flex items-center justify-between gap-3 rounded-xl border border-[var(--border-color-default)] px-4 py-3"
297
+ >
298
+ <div class="space-y-1">
299
+ <p class="text-sm font-medium">Live mode</p>
300
+ <p class="text-xs text-[var(--color-text-muted)]">
301
+ Keep autoplay and progress linked across the page
302
+ </p>
303
+ </div>
304
+ <Switch checked={autoplay} onChange={(v) => (autoplay = v)} sz="sm" />
305
+ </div>
306
+
307
+ <div class="space-y-2">
308
+ <div class="flex items-center justify-between text-sm">
309
+ <span class="text-[var(--color-text-muted)]">Drag to refresh progress</span>
310
+ <span class="font-mono text-[var(--color-text-default)]">{sliderValue}%</span>
311
+ </div>
312
+ <Slider
313
+ value={sliderValue}
314
+ min={0}
315
+ max={100}
316
+ onInput={(v) => (sliderValue = v)}
317
+ showValue={false}
318
+ />
319
+ </div>
320
+
321
+ <div class="grid gap-3 md:grid-cols-2">
322
+ <ProgressBar value={sliderValue} label="Iteration" />
323
+ <ProgressCircle value={sliderValue} label="Readiness" />
324
+ </div>
325
+
326
+ <div class="flex flex-wrap gap-2">
327
+ <Button variant="success" onClick={() => pushToast("success")} sz="sm">
328
+ Success toast
329
+ </Button>
330
+ <Button variant="warning" onClick={() => pushToast("warning")} sz="sm">
331
+ Warning toast
332
+ </Button>
333
+ <Button variant="info" onClick={() => pushToast("info")} sz="sm">
334
+ Info toast
335
+ </Button>
336
+ </div>
337
+ </div>
338
+ </Card>
339
+
340
+ <Card header={formHeader}>
341
+ <div class="space-y-4">
342
+ <div class="grid gap-3 md:grid-cols-2">
343
+ <Field
344
+ label="Name"
345
+ value={featureName}
346
+ onChange={(v) => (featureName = String(v))}
347
+ />
348
+ <Field
349
+ label="Contact email"
350
+ type="email"
351
+ value={contactEmail}
352
+ onChange={(v) => (contactEmail = String(v))}
353
+ />
354
+ </div>
355
+
356
+ <Select
357
+ label="Plan"
358
+ options={planOptions}
359
+ value={selectedPlan}
360
+ placeholder="Choose a plan"
361
+ onChange={(v) => (selectedPlan = v)}
362
+ />
363
+
364
+ <div class="grid gap-3 md:grid-cols-3">
365
+ <DatePicker
366
+ label="Launch date"
367
+ value={dateValue}
368
+ onChange={(v) => (dateValue = v)}
369
+ />
370
+ <TimePicker
371
+ label="Release time"
372
+ value={timeValue}
373
+ onChange={(v) => (timeValue = v)}
374
+ />
375
+ <ColorPicker
376
+ label="Accent"
377
+ value={accentColor}
378
+ onChange={(v) => (accentColor = v)}
379
+ />
380
+ </div>
381
+
382
+ <div class="flex justify-end gap-3">
383
+ <Button
384
+ variant="ghost"
385
+ onClick={() => {
386
+ featureName = "Dashboard 2.0";
387
+ selectedPlan = planOptions[1].value;
388
+ contactEmail = "team@studio.dev";
389
+ dateValue = null;
390
+ timeValue = null;
391
+ accentColor = "#7c3aed";
392
+ }}
393
+ >
394
+ Reset
395
+ </Button>
396
+ <Button variant="primary" onClick={() => pushToast("success")}>
397
+ Save
398
+ </Button>
399
+ </div>
400
+ </div>
401
+ </Card>
402
+ </div>
403
+
404
+ {#snippet dataHeader()}
405
+ <div class="flex items-center justify-between gap-3">
406
+ <div>
407
+ <p class="text-xs uppercase tracking-[0.15em] text-[var(--color-text-muted)]">
408
+ Data
409
+ </p>
410
+ <h2 class="text-lg font-semibold leading-tight">Sprint snapshot</h2>
411
+ </div>
412
+ <Button variant="secondary" sz="sm" onClick={() => pushToast("info")}>
413
+ Refresh
414
+ </Button>
415
+ </div>
416
+ {/snippet}
417
+
418
+ {#snippet accordionHeader()}
419
+ <div class="flex items-center justify-between gap-3">
420
+ <div>
421
+ <p class="text-xs uppercase tracking-[0.15em] text-[var(--color-text-muted)]">
422
+ Details
423
+ </p>
424
+ <h3 class="text-lg font-semibold leading-tight">Sections</h3>
425
+ </div>
426
+ </div>
427
+ {/snippet}
428
+
429
+ {#snippet carouselHeader()}
430
+ <div class="flex items-center justify-between gap-3">
431
+ <div>
432
+ <p class="text-xs uppercase tracking-[0.15em] text-[var(--color-text-muted)]">
433
+ Carousel
434
+ </p>
435
+ <h3 class="text-lg font-semibold leading-tight">Stories</h3>
436
+ </div>
437
+ <span class="text-xs text-[var(--color-text-muted)]">
438
+ Autoplay {autoplay ? "on" : "off"}
439
+ </span>
440
+ </div>
441
+ {/snippet}
442
+
443
+ <div class="grid gap-6 lg:grid-cols-[1.2fr_0.8fr]">
444
+ <Card header={dataHeader} class="h-full">
445
+ <Tabs
446
+ tabs={tabs}
447
+ activeTab={activeTab}
448
+ onChange={(id) => (activeTab = id)}
449
+ variant="underline"
450
+ fitted={true}
451
+ >
452
+ {#if activeTab === "overview"}
453
+ <div class="grid gap-4 md:grid-cols-3">
454
+ <div
455
+ class="rounded-xl border border-[var(--border-color-default)] bg-[var(--color-bg-surface)] p-4"
456
+ >
457
+ <p class="text-xs uppercase tracking-[0.1em] text-[var(--color-text-muted)]">
458
+ Readiness
459
+ </p>
460
+ <p class="text-2xl font-bold">{sliderValue}%</p>
461
+ <p class="text-sm text-[var(--color-text-muted)]">
462
+ Synced with the slider above
463
+ </p>
464
+ </div>
465
+
466
+ <div
467
+ class="rounded-xl border border-[var(--border-color-default)] bg-[var(--color-bg-surface)] p-4"
468
+ >
469
+ <p class="text-xs uppercase tracking-[0.1em] text-[var(--color-text-muted)]">
470
+ Plan
471
+ </p>
472
+ <p class="text-lg font-semibold">{selectedPlan}</p>
473
+ <p class="text-sm text-[var(--color-text-muted)]">
474
+ Contact: {contactEmail}
475
+ </p>
476
+ </div>
477
+
478
+ <div
479
+ class="rounded-xl border border-[var(--border-color-default)] bg-[var(--color-bg-surface)] p-4"
480
+ >
481
+ <p class="text-xs uppercase tracking-[0.1em] text-[var(--color-text-muted)]">
482
+ Release window
483
+ </p>
484
+ <p class="text-lg font-semibold">
485
+ {dateValue ?? "Not selected yet"}
486
+ </p>
487
+ <p class="text-sm text-[var(--color-text-muted)]">
488
+ {timeValue ?? "Time is being planned"}
489
+ </p>
490
+ </div>
491
+ </div>
492
+
493
+ <div class="mt-4 grid gap-3 md:grid-cols-3">
494
+ {#each quality as metric (metric.label)}
495
+ <ProgressBar value={metric.value} label={metric.label} sz="sm" />
496
+ {/each}
497
+ </div>
498
+ {:else if activeTab === "team"}
499
+ <div class="space-y-3">
500
+ {#each team as person (person.name)}
501
+ <div
502
+ class="flex items-center justify-between rounded-xl border border-[var(--border-color-default)] bg-[var(--color-bg-surface)] px-4 py-3"
503
+ >
504
+ <div>
505
+ <p class="font-semibold">{person.name}</p>
506
+ <p class="text-sm text-[var(--color-text-muted)]">{person.role}</p>
507
+ </div>
508
+ <span
509
+ class="rounded-full bg-[var(--color-bg-muted)] px-3 py-1 text-xs text-[var(--color-text-default)]"
510
+ >
511
+ {person.focus}
512
+ </span>
513
+ </div>
514
+ {/each}
515
+ </div>
516
+ {:else}
517
+ <Table
518
+ columns={tableColumns}
519
+ rows={pagedRows}
520
+ variant="zebra"
521
+ pagination={{ currentPage: tablePage, totalPages, onPageChange: (p) => (tablePage = p) }}
522
+ />
523
+ {/if}
524
+ </Tabs>
525
+ </Card>
526
+
527
+ <div class="grid gap-6">
528
+ <Card header={accordionHeader}>
529
+ <Accordion items={accordionItems} multiple={true} defaultOpen={[0]} />
530
+ </Card>
531
+
532
+ <Card header={carouselHeader}>
533
+ <Carousel
534
+ items={carouselItems}
535
+ autoplay={autoplay}
536
+ interval={4200}
537
+ showDots={true}
538
+ showArrows={true}
539
+ sz="sm"
540
+ />
541
+ </Card>
542
+ </div>
543
+ </div>
544
+ </div>
545
+
546
+ {#each toasts as toast (toast.id)}
547
+ <Toast
548
+ title={toast.title}
549
+ message={toast.message}
550
+ variant={toast.variant}
551
+ onClose={() => removeToast(toast.id)}
552
+ timeout={3500}
553
+ />
554
+ {/each}
555
+ </main>
@@ -0,0 +1,3 @@
1
+ declare const App: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type App = ReturnType<typeof App>;
3
+ export default App;
package/dist/app.css ADDED
@@ -0,0 +1,3 @@
1
+ /* src/app.css */
2
+
3
+ @import "tailwindcss";
@@ -29,7 +29,7 @@
29
29
  * @note Supports touch gestures (swipe left/right).
30
30
  * @note Autoplay pauses automatically when unmounted.
31
31
  * @note Uses `Card.svelte` internally for slide structure.
32
- * @note Navigation dots and arrows appear only if there’s more than one item.
32
+ * @note Navigation dots and arrows appear only if theres more than one item.
33
33
  * @note Accessible via `aria-label`, `aria-current`, and keyboard focus on controls.
34
34
  */
35
35
  import Card from "./Card.svelte";
@@ -123,12 +123,15 @@
123
123
  const arrowClass = $derived(
124
124
  cx(
125
125
  arrowSize[sz],
126
- "rounded-full bg-[var(--color-bg-surface)] shadow-lg flex items-center justify-center [color:var(--color-text-default)] hover:bg-[var(--color-bg-hover)] transition-colors"
126
+ "rounded-full bg-[var(--color-bg-surface)] shadow-lg flex items-center justify-center [color:var(--color-text-default)] hover:bg-[var(--color-bg-hover)] transition-colors focus-visible:ring-2 focus-visible:ring-[var(--border-color-focus)] focus:outline-none"
127
127
  )
128
128
  );
129
129
 
130
130
  const dotClass = $derived(
131
- cx(dotSize[sz], "rounded-full transition-all duration-200")
131
+ cx(
132
+ dotSize[sz],
133
+ "rounded-full transition-all duration-200 focus-visible:ring-2 focus-visible:ring-[var(--border-color-focus)] focus:outline-none"
134
+ )
132
135
  );
133
136
 
134
137
  $effect(() => {
@@ -35,7 +35,7 @@
35
35
  * @note Clicking the custom box while `indeterminate` clears it and sets `checked=true`.
36
36
  * @note `invalid` maps to `aria-invalid`; `describedBy` maps to `aria-describedby`.
37
37
  * @note SVG check and dash are inline; colors adapt per `variant` (`neutral` uses border color).
38
- * @note Sizes scale the control box (`xs в†’ xl`).
38
+ * @note Sizes scale the control box (`xs xl`).
39
39
  */
40
40
  import type { HTMLInputAttributes } from "svelte/elements";
41
41
  import { type SizeKey, type ComponentVariant, TEXT } from "./types";
@@ -33,7 +33,7 @@
33
33
  * @note Clicking the custom box while `indeterminate` clears it and sets `checked=true`.
34
34
  * @note `invalid` maps to `aria-invalid`; `describedBy` maps to `aria-describedby`.
35
35
  * @note SVG check and dash are inline; colors adapt per `variant` (`neutral` uses border color).
36
- * @note Sizes scale the control box (`xs в†’ xl`).
36
+ * @note Sizes scale the control box (`xs xl`).
37
37
  */
38
38
  import type { HTMLInputAttributes } from "svelte/elements";
39
39
  import { type SizeKey, type ComponentVariant } from "./types";