svelte-toggle-switch 2.0.0 → 2.1.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.
- package/CHANGELOG.md +69 -0
- package/package.json +6 -4
- package/src/lib/Switch.svelte +126 -9
- package/svelte-toggle-switch-2.0.0.tgz +0 -0
- package/svelte-toggle-switch-2.1.0.tgz +0 -0
- package/CLAUDE.md +0 -165
- package/src/App.svelte +0 -409
- package/src/vite-env.d.ts +0 -2
- package/tsconfig.json +0 -14
- package/tsconfig.node.json +0 -9
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [2.1.0] - 2026-01-02
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Event Dispatchers**: Added `on:change`, `on:focus`, and `on:blur` event handlers for better integration with frameworks and form libraries
|
|
12
|
+
- **Custom Text Labels**: New `onText` and `offText` props to customize the text displayed in the inner design (default: "ON"/"OFF")
|
|
13
|
+
- **Form Validation Support**:
|
|
14
|
+
- `helperText` prop - Display helper text below the switch
|
|
15
|
+
- `errorText` prop - Display error message when validation fails
|
|
16
|
+
- `required` prop - Mark the switch as required
|
|
17
|
+
- `error` prop - Show error state with red styling
|
|
18
|
+
- `name` prop - Form field name for submission
|
|
19
|
+
- **New Color Schemes**: Added `yellow`, `indigo`, and `teal` color schemes (now 9 total color schemes)
|
|
20
|
+
- **Enhanced Accessibility**:
|
|
21
|
+
- `tabIndex` prop for custom keyboard navigation order
|
|
22
|
+
- `aria-required` and `aria-invalid` attributes for form validation
|
|
23
|
+
- Improved screen reader support with helper text announcements
|
|
24
|
+
- **Demo Application**: Comprehensive SvelteKit demo with interactive playground at svelte-toggle-switch.ishansasika.dev
|
|
25
|
+
- Live preview with all props
|
|
26
|
+
- Real-time code generation
|
|
27
|
+
- 15+ quick example presets
|
|
28
|
+
- Icon picker with 10+ preset icons
|
|
29
|
+
- Complete documentation
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
- Updated component to dispatch custom events with detailed payload
|
|
33
|
+
- Multi-select now dispatches change events properly
|
|
34
|
+
- Helper text and error text display dynamically below all switch designs
|
|
35
|
+
- Error state now includes visual border color change
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
- Multi-select change events now trigger correctly
|
|
39
|
+
- Focus and blur events properly propagate
|
|
40
|
+
- ARIA attributes correctly reference helper text for screen readers
|
|
41
|
+
|
|
42
|
+
## [2.0.0] - 2025-12-14
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
- Complete rewrite with TypeScript support
|
|
46
|
+
- 5 design variants (slider/ios, inner, modern, material, multi)
|
|
47
|
+
- 6 color schemes (blue, green, red, purple, orange, pink)
|
|
48
|
+
- 5 size variants (xs, sm, md, lg, xl)
|
|
49
|
+
- Custom icon support with showIcons, onIcon, and offIcon props
|
|
50
|
+
- Comprehensive accessibility with ARIA attributes
|
|
51
|
+
- Loading and disabled states
|
|
52
|
+
- Shadow and outline visual effects
|
|
53
|
+
- Customizable animations (duration and easing)
|
|
54
|
+
- Label positioning (left/right)
|
|
55
|
+
- Multi-option mode with radio group styling
|
|
56
|
+
|
|
57
|
+
### Breaking Changes
|
|
58
|
+
- Migrated from Svelte 4 to Svelte 5 with runes support
|
|
59
|
+
- Component props now use TypeScript types
|
|
60
|
+
- Removed deprecated v1.x props
|
|
61
|
+
|
|
62
|
+
## [1.0.0] - 2022-05-29
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
- Initial release
|
|
66
|
+
- Basic toggle switch functionality
|
|
67
|
+
- iOS-style design
|
|
68
|
+
- ON/OFF states
|
|
69
|
+
- Simple customization options
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-toggle-switch",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "vite",
|
|
@@ -34,9 +34,11 @@
|
|
|
34
34
|
"svelte component",
|
|
35
35
|
"ui component",
|
|
36
36
|
"accessible",
|
|
37
|
-
"typescript"
|
|
37
|
+
"typescript",
|
|
38
|
+
"form validation",
|
|
39
|
+
"events"
|
|
38
40
|
],
|
|
39
|
-
"author": "Ishan Karunaratne <ishansasika@gmail.com> (https://ishansasika.
|
|
41
|
+
"author": "Ishan Karunaratne <ishansasika@gmail.com> (https://ishansasika.dev)",
|
|
40
42
|
"license": "MIT",
|
|
41
|
-
"homepage": "https://
|
|
43
|
+
"homepage": "https://svelte-toggle-switch.ishansasika.dev"
|
|
42
44
|
}
|
package/src/lib/Switch.svelte
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { createEventDispatcher } from 'svelte';
|
|
3
|
+
|
|
4
|
+
const dispatch = createEventDispatcher();
|
|
5
|
+
|
|
2
6
|
// Core Props
|
|
3
7
|
export let value: boolean | string = false;
|
|
4
8
|
export let label: string = '';
|
|
@@ -11,7 +15,7 @@
|
|
|
11
15
|
export let size: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number = 'md';
|
|
12
16
|
export let color: string = '#007AFF';
|
|
13
17
|
export let offColor: string = '#E5E7EB';
|
|
14
|
-
export let colorScheme: 'blue' | 'green' | 'red' | 'purple' | 'orange' | 'pink' | 'custom' = 'blue';
|
|
18
|
+
export let colorScheme: 'blue' | 'green' | 'red' | 'purple' | 'orange' | 'pink' | 'yellow' | 'indigo' | 'teal' | 'custom' = 'blue';
|
|
15
19
|
|
|
16
20
|
// State Props
|
|
17
21
|
export let disabled: boolean = false;
|
|
@@ -31,6 +35,8 @@
|
|
|
31
35
|
export let ariaLabel: string = '';
|
|
32
36
|
export let ariaDescribedBy: string = '';
|
|
33
37
|
export let id: string = '';
|
|
38
|
+
export let name: string = '';
|
|
39
|
+
export let tabIndex: number = 0;
|
|
34
40
|
|
|
35
41
|
// Advanced Props
|
|
36
42
|
export let labelPosition: 'left' | 'right' = 'right';
|
|
@@ -38,11 +44,22 @@
|
|
|
38
44
|
export let shadow: boolean = false;
|
|
39
45
|
export let outline: boolean = false;
|
|
40
46
|
|
|
47
|
+
// v2.1.0 New Features
|
|
48
|
+
// Custom Text Labels
|
|
49
|
+
export let onText: string = 'ON';
|
|
50
|
+
export let offText: string = 'OFF';
|
|
51
|
+
|
|
52
|
+
// Form Validation
|
|
53
|
+
export let helperText: string = '';
|
|
54
|
+
export let errorText: string = '';
|
|
55
|
+
export let required: boolean = false;
|
|
56
|
+
export let error: boolean = false;
|
|
57
|
+
|
|
41
58
|
// Internal state
|
|
42
59
|
let checked: boolean = typeof value === 'boolean' ? value : value === 'on';
|
|
43
60
|
const uniqueID = id || `switch-${Math.floor(Math.random() * 1000000)}`;
|
|
44
61
|
|
|
45
|
-
// Color schemes
|
|
62
|
+
// Color schemes (v2.1.0 - added yellow, indigo, teal)
|
|
46
63
|
const colorSchemes = {
|
|
47
64
|
blue: '#007AFF',
|
|
48
65
|
green: '#10B981',
|
|
@@ -50,6 +67,9 @@
|
|
|
50
67
|
purple: '#8B5CF6',
|
|
51
68
|
orange: '#F97316',
|
|
52
69
|
pink: '#EC4899',
|
|
70
|
+
yellow: '#F59E0B',
|
|
71
|
+
indigo: '#6366F1',
|
|
72
|
+
teal: '#14B8A6',
|
|
53
73
|
custom: color
|
|
54
74
|
};
|
|
55
75
|
|
|
@@ -83,7 +103,11 @@
|
|
|
83
103
|
if (design !== 'multi') {
|
|
84
104
|
const newChecked = !checked;
|
|
85
105
|
checked = newChecked;
|
|
86
|
-
|
|
106
|
+
const newValue = newChecked ? (typeof value === 'boolean' ? true : 'on') : (typeof value === 'boolean' ? false : 'off');
|
|
107
|
+
value = newValue;
|
|
108
|
+
|
|
109
|
+
// v2.1.0 - Dispatch change event
|
|
110
|
+
dispatch('change', { value: newValue, checked: newChecked });
|
|
87
111
|
}
|
|
88
112
|
}
|
|
89
113
|
|
|
@@ -95,10 +119,28 @@
|
|
|
95
119
|
if (design !== 'multi') {
|
|
96
120
|
const newChecked = !checked;
|
|
97
121
|
checked = newChecked;
|
|
98
|
-
|
|
122
|
+
const newValue = newChecked ? (typeof value === 'boolean' ? true : 'on') : (typeof value === 'boolean' ? false : 'off');
|
|
123
|
+
value = newValue;
|
|
124
|
+
|
|
125
|
+
// v2.1.0 - Dispatch change event
|
|
126
|
+
dispatch('change', { value: newValue, checked: newChecked });
|
|
99
127
|
}
|
|
100
128
|
}
|
|
101
129
|
}
|
|
130
|
+
|
|
131
|
+
// v2.1.0 - Focus/Blur event handlers
|
|
132
|
+
function handleFocus(event: FocusEvent) {
|
|
133
|
+
dispatch('focus', { event });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function handleBlur(event: FocusEvent) {
|
|
137
|
+
dispatch('blur', { event });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// v2.1.0 - Multi-select change handler
|
|
141
|
+
function handleMultiChange() {
|
|
142
|
+
dispatch('change', { value });
|
|
143
|
+
}
|
|
102
144
|
</script>
|
|
103
145
|
|
|
104
146
|
{#if design === 'inner'}
|
|
@@ -117,16 +159,22 @@
|
|
|
117
159
|
aria-checked={checked}
|
|
118
160
|
aria-label={ariaLabel || label}
|
|
119
161
|
aria-labelledby={label ? `${uniqueID}-label` : undefined}
|
|
120
|
-
aria-describedby={ariaDescribedBy || undefined}
|
|
162
|
+
aria-describedby={ariaDescribedBy || (helperText || errorText ? `${uniqueID}-helper` : undefined)}
|
|
163
|
+
aria-required={required}
|
|
164
|
+
aria-invalid={error}
|
|
121
165
|
{disabled}
|
|
166
|
+
tabindex={tabIndex}
|
|
122
167
|
class="switch switch--inner"
|
|
123
168
|
class:checked
|
|
124
169
|
class:loading
|
|
125
170
|
class:rounded
|
|
126
171
|
class:shadow
|
|
127
172
|
class:outline
|
|
173
|
+
class:error
|
|
128
174
|
on:click={handleClick}
|
|
129
175
|
on:keydown={handleKeyDown}
|
|
176
|
+
on:focus={handleFocus}
|
|
177
|
+
on:blur={handleBlur}
|
|
130
178
|
style="
|
|
131
179
|
--active-color: {activeColor};
|
|
132
180
|
--off-color: {offColor};
|
|
@@ -137,12 +185,17 @@
|
|
|
137
185
|
{#if loading}
|
|
138
186
|
<span class="spinner"></span>
|
|
139
187
|
{:else}
|
|
140
|
-
<span class="switch-text">{checked ?
|
|
188
|
+
<span class="switch-text">{checked ? onText : offText}</span>
|
|
141
189
|
{/if}
|
|
142
190
|
</button>
|
|
143
191
|
{#if label && labelPosition === 'right'}
|
|
144
192
|
<span class="switch-label" id="{uniqueID}-label">{label}</span>
|
|
145
193
|
{/if}
|
|
194
|
+
{#if helperText || errorText}
|
|
195
|
+
<span class="switch-helper-text" id="{uniqueID}-helper" class:error-text={error}>
|
|
196
|
+
{error && errorText ? errorText : helperText}
|
|
197
|
+
</span>
|
|
198
|
+
{/if}
|
|
146
199
|
</div>
|
|
147
200
|
|
|
148
201
|
{:else if design === 'slider' || design === 'ios'}
|
|
@@ -161,15 +214,21 @@
|
|
|
161
214
|
aria-checked={checked}
|
|
162
215
|
aria-label={ariaLabel || label}
|
|
163
216
|
aria-labelledby={label ? `${uniqueID}-label` : undefined}
|
|
164
|
-
aria-describedby={ariaDescribedBy || undefined}
|
|
217
|
+
aria-describedby={ariaDescribedBy || (helperText || errorText ? `${uniqueID}-helper` : undefined)}
|
|
218
|
+
aria-required={required}
|
|
219
|
+
aria-invalid={error}
|
|
165
220
|
{disabled}
|
|
221
|
+
tabindex={tabIndex}
|
|
166
222
|
class="switch switch--slider"
|
|
167
223
|
class:checked
|
|
168
224
|
class:loading
|
|
169
225
|
class:shadow
|
|
170
226
|
class:outline
|
|
227
|
+
class:error
|
|
171
228
|
on:click={handleClick}
|
|
172
229
|
on:keydown={handleKeyDown}
|
|
230
|
+
on:focus={handleFocus}
|
|
231
|
+
on:blur={handleBlur}
|
|
173
232
|
style="
|
|
174
233
|
--active-color: {activeColor};
|
|
175
234
|
--off-color: {offColor};
|
|
@@ -190,6 +249,11 @@
|
|
|
190
249
|
{#if label && labelPosition === 'right'}
|
|
191
250
|
<span class="switch-label" id="{uniqueID}-label">{label}</span>
|
|
192
251
|
{/if}
|
|
252
|
+
{#if helperText || errorText}
|
|
253
|
+
<span class="switch-helper-text" id="{uniqueID}-helper" class:error-text={error}>
|
|
254
|
+
{error && errorText ? errorText : helperText}
|
|
255
|
+
</span>
|
|
256
|
+
{/if}
|
|
193
257
|
</div>
|
|
194
258
|
|
|
195
259
|
{:else if design === 'modern'}
|
|
@@ -208,15 +272,21 @@
|
|
|
208
272
|
aria-checked={checked}
|
|
209
273
|
aria-label={ariaLabel || label}
|
|
210
274
|
aria-labelledby={label ? `${uniqueID}-label` : undefined}
|
|
211
|
-
aria-describedby={ariaDescribedBy || undefined}
|
|
275
|
+
aria-describedby={ariaDescribedBy || (helperText || errorText ? `${uniqueID}-helper` : undefined)}
|
|
276
|
+
aria-required={required}
|
|
277
|
+
aria-invalid={error}
|
|
212
278
|
{disabled}
|
|
279
|
+
tabindex={tabIndex}
|
|
213
280
|
class="switch switch--modern"
|
|
214
281
|
class:checked
|
|
215
282
|
class:loading
|
|
216
283
|
class:shadow
|
|
217
284
|
class:outline
|
|
285
|
+
class:error
|
|
218
286
|
on:click={handleClick}
|
|
219
287
|
on:keydown={handleKeyDown}
|
|
288
|
+
on:focus={handleFocus}
|
|
289
|
+
on:blur={handleBlur}
|
|
220
290
|
style="
|
|
221
291
|
--active-color: {activeColor};
|
|
222
292
|
--off-color: {offColor};
|
|
@@ -243,6 +313,11 @@
|
|
|
243
313
|
{#if label && labelPosition === 'right'}
|
|
244
314
|
<span class="switch-label" id="{uniqueID}-label">{label}</span>
|
|
245
315
|
{/if}
|
|
316
|
+
{#if helperText || errorText}
|
|
317
|
+
<span class="switch-helper-text" id="{uniqueID}-helper" class:error-text={error}>
|
|
318
|
+
{error && errorText ? errorText : helperText}
|
|
319
|
+
</span>
|
|
320
|
+
{/if}
|
|
246
321
|
</div>
|
|
247
322
|
|
|
248
323
|
{:else if design === 'material'}
|
|
@@ -261,15 +336,21 @@
|
|
|
261
336
|
aria-checked={checked}
|
|
262
337
|
aria-label={ariaLabel || label}
|
|
263
338
|
aria-labelledby={label ? `${uniqueID}-label` : undefined}
|
|
264
|
-
aria-describedby={ariaDescribedBy || undefined}
|
|
339
|
+
aria-describedby={ariaDescribedBy || (helperText || errorText ? `${uniqueID}-helper` : undefined)}
|
|
340
|
+
aria-required={required}
|
|
341
|
+
aria-invalid={error}
|
|
265
342
|
{disabled}
|
|
343
|
+
tabindex={tabIndex}
|
|
266
344
|
class="switch switch--material"
|
|
267
345
|
class:checked
|
|
268
346
|
class:loading
|
|
269
347
|
class:shadow
|
|
270
348
|
class:outline
|
|
349
|
+
class:error
|
|
271
350
|
on:click={handleClick}
|
|
272
351
|
on:keydown={handleKeyDown}
|
|
352
|
+
on:focus={handleFocus}
|
|
353
|
+
on:blur={handleBlur}
|
|
273
354
|
style="
|
|
274
355
|
--active-color: {activeColor};
|
|
275
356
|
--off-color: {offColor};
|
|
@@ -288,6 +369,11 @@
|
|
|
288
369
|
{#if label && labelPosition === 'right'}
|
|
289
370
|
<span class="switch-label" id="{uniqueID}-label">{label}</span>
|
|
290
371
|
{/if}
|
|
372
|
+
{#if helperText || errorText}
|
|
373
|
+
<span class="switch-helper-text" id="{uniqueID}-helper" class:error-text={error}>
|
|
374
|
+
{error && errorText ? errorText : helperText}
|
|
375
|
+
</span>
|
|
376
|
+
{/if}
|
|
291
377
|
</div>
|
|
292
378
|
|
|
293
379
|
{:else if design === 'multi'}
|
|
@@ -317,9 +403,13 @@
|
|
|
317
403
|
<input
|
|
318
404
|
type="radio"
|
|
319
405
|
id="{uniqueID}-{option}"
|
|
406
|
+
{name}
|
|
320
407
|
value={option}
|
|
321
408
|
bind:group={value}
|
|
409
|
+
on:change={handleMultiChange}
|
|
322
410
|
{disabled}
|
|
411
|
+
{required}
|
|
412
|
+
tabindex={tabIndex}
|
|
323
413
|
class="switch-multi-input"
|
|
324
414
|
/>
|
|
325
415
|
<label
|
|
@@ -333,6 +423,11 @@
|
|
|
333
423
|
{/each}
|
|
334
424
|
</div>
|
|
335
425
|
</div>
|
|
426
|
+
{#if helperText || errorText}
|
|
427
|
+
<span class="switch-helper-text" id="{uniqueID}-helper" class:error-text={error}>
|
|
428
|
+
{error && errorText ? errorText : helperText}
|
|
429
|
+
</span>
|
|
430
|
+
{/if}
|
|
336
431
|
</div>
|
|
337
432
|
{/if}
|
|
338
433
|
|
|
@@ -367,6 +462,19 @@
|
|
|
367
462
|
color: #374151;
|
|
368
463
|
}
|
|
369
464
|
|
|
465
|
+
/* v2.1.0 - Helper Text */
|
|
466
|
+
.switch-helper-text {
|
|
467
|
+
display: block;
|
|
468
|
+
font-size: 0.875em;
|
|
469
|
+
color: #6B7280;
|
|
470
|
+
margin-top: 0.375em;
|
|
471
|
+
line-height: 1.4;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.switch-helper-text.error-text {
|
|
475
|
+
color: #EF4444;
|
|
476
|
+
}
|
|
477
|
+
|
|
370
478
|
/* Base Switch */
|
|
371
479
|
.switch {
|
|
372
480
|
position: relative;
|
|
@@ -393,6 +501,15 @@
|
|
|
393
501
|
border: 1px solid #D1D5DB;
|
|
394
502
|
}
|
|
395
503
|
|
|
504
|
+
/* v2.1.0 - Error State */
|
|
505
|
+
.switch.error {
|
|
506
|
+
border-color: #EF4444;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.switch.error:focus-visible {
|
|
510
|
+
outline-color: #EF4444;
|
|
511
|
+
}
|
|
512
|
+
|
|
396
513
|
/* Inner Design */
|
|
397
514
|
.switch--inner {
|
|
398
515
|
padding: 0.4em 0.8em;
|
|
Binary file
|
|
Binary file
|
package/CLAUDE.md
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
-
|
|
5
|
-
## Project Overview
|
|
6
|
-
|
|
7
|
-
This is an NPM package providing a comprehensive, accessible toggle switch component for Svelte applications. The package has been completely rewritten in version 2.0 with TypeScript support, multiple design variants, extensive customization options, and enhanced accessibility features.
|
|
8
|
-
|
|
9
|
-
## Development Commands
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
# Install dependencies
|
|
13
|
-
npm install
|
|
14
|
-
|
|
15
|
-
# Start development server with hot reload
|
|
16
|
-
npm run dev
|
|
17
|
-
|
|
18
|
-
# Build the package for distribution
|
|
19
|
-
npm run build
|
|
20
|
-
|
|
21
|
-
# Preview production build
|
|
22
|
-
npm run preview
|
|
23
|
-
|
|
24
|
-
# Run TypeScript type checking
|
|
25
|
-
npm run check
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Tech Stack
|
|
29
|
-
|
|
30
|
-
- **Svelte 5.16.0**: Latest Svelte with runes support
|
|
31
|
-
- **TypeScript 5.7.3**: Full type safety
|
|
32
|
-
- **Vite 6.0.5**: Fast build tooling
|
|
33
|
-
- **svelte-check**: TypeScript checking for Svelte files
|
|
34
|
-
|
|
35
|
-
## Architecture
|
|
36
|
-
|
|
37
|
-
### Component Structure
|
|
38
|
-
|
|
39
|
-
The main component is located at `src/lib/Switch.svelte`. This single component supports five distinct design variants controlled by the `design` prop:
|
|
40
|
-
|
|
41
|
-
- **slider/ios**: iOS-style toggle switch (default)
|
|
42
|
-
- **inner**: Toggle button with visible ON/OFF text
|
|
43
|
-
- **modern**: Enhanced slider with optional track icons
|
|
44
|
-
- **material**: Google Material Design inspired toggle
|
|
45
|
-
- **multi**: Radio group styled as a segmented control
|
|
46
|
-
|
|
47
|
-
### Component API Design
|
|
48
|
-
|
|
49
|
-
The component uses a comprehensive props-based API with full TypeScript support:
|
|
50
|
-
|
|
51
|
-
#### Core Props
|
|
52
|
-
- `value`: Two-way bound state (boolean or string for multi design)
|
|
53
|
-
- `label`: Accessible label text
|
|
54
|
-
- `design`: Design variant selector
|
|
55
|
-
|
|
56
|
-
#### Styling System
|
|
57
|
-
- **Color Schemes**: 6 built-in schemes (blue, green, red, purple, orange, pink) + custom
|
|
58
|
-
- **Size Variants**: xs, sm, md, lg, xl (mapped to rem values) or custom number
|
|
59
|
-
- **Visual Options**: shadow, outline, rounded, labelPosition
|
|
60
|
-
|
|
61
|
-
#### State Management
|
|
62
|
-
- `disabled`: Prevents interaction, shows disabled UI
|
|
63
|
-
- `loading`: Shows spinner animation, prevents interaction
|
|
64
|
-
- `readonly`: Prevents changes but shows current state
|
|
65
|
-
|
|
66
|
-
#### Icon System
|
|
67
|
-
- `showIcons`: Enable icon display
|
|
68
|
-
- `onIcon`/`offIcon`: Customizable icons (text/emoji/symbols)
|
|
69
|
-
- Icons displayed differently per design variant
|
|
70
|
-
|
|
71
|
-
#### Animation Customization
|
|
72
|
-
- `animationDuration`: Transition duration in ms (default 300)
|
|
73
|
-
- `animationEasing`: CSS easing function (default 'ease-in-out')
|
|
74
|
-
|
|
75
|
-
#### Accessibility
|
|
76
|
-
- `ariaLabel`: Custom ARIA label
|
|
77
|
-
- `ariaDescribedBy`: Links to description element
|
|
78
|
-
- `id`: Custom element ID (auto-generated if not provided)
|
|
79
|
-
|
|
80
|
-
### State Management
|
|
81
|
-
|
|
82
|
-
The component uses Svelte's reactive statements (`$:`) to sync internal and external state:
|
|
83
|
-
|
|
84
|
-
1. **For slider/inner/modern/material designs**:
|
|
85
|
-
- Internal `checked` boolean tracks state
|
|
86
|
-
- Syncs with `value` prop (boolean or 'on'/'off' string)
|
|
87
|
-
- Two-way binding via `bind:value`
|
|
88
|
-
|
|
89
|
-
2. **For multi design**:
|
|
90
|
-
- Uses native radio input `bind:group`
|
|
91
|
-
- Value is one of the strings from `options` array
|
|
92
|
-
- No internal checked state needed
|
|
93
|
-
|
|
94
|
-
3. **Unique IDs**:
|
|
95
|
-
- Auto-generated using `Math.floor(Math.random() * 1000000)`
|
|
96
|
-
- Used for ARIA attributes (`aria-labelledby`, `id` attributes)
|
|
97
|
-
- Can be overridden via `id` prop
|
|
98
|
-
|
|
99
|
-
### Accessibility Implementation
|
|
100
|
-
|
|
101
|
-
Comprehensive ARIA and keyboard support:
|
|
102
|
-
|
|
103
|
-
- **ARIA Roles**: `role="switch"` for toggles, `role="radiogroup"` for multi
|
|
104
|
-
- **State Attributes**: `aria-checked` reflects current state
|
|
105
|
-
- **Labeling**: `aria-labelledby` connects visual labels, `aria-describedby` for descriptions
|
|
106
|
-
- **Keyboard Navigation**: Space and Enter keys toggle state
|
|
107
|
-
- **Focus Management**: `:focus-visible` for keyboard-only focus rings
|
|
108
|
-
- **Disabled State**: Properly communicated to assistive technologies
|
|
109
|
-
|
|
110
|
-
### Styling Architecture
|
|
111
|
-
|
|
112
|
-
All styles are scoped within the component using Svelte's `<style>` block:
|
|
113
|
-
|
|
114
|
-
- **CSS Custom Properties**: Used for theming (`--active-color`, `--off-color`, `--animation-duration`, `--animation-easing`)
|
|
115
|
-
- **Design-Specific Classes**: `.switch--slider`, `.switch--inner`, `.switch--modern`, `.switch--material`, `.switch-multi`
|
|
116
|
-
- **State Classes**: `.checked`, `.disabled`, `.loading`, `.shadow`, `.outline`
|
|
117
|
-
- **Conditional Rendering**: `{#if design === 'variant'}` blocks for each design
|
|
118
|
-
- **No External Dependencies**: Self-contained styling, no CSS framework required
|
|
119
|
-
|
|
120
|
-
### TypeScript Integration
|
|
121
|
-
|
|
122
|
-
The component is written in TypeScript (`<script lang="ts">`):
|
|
123
|
-
|
|
124
|
-
- All props have explicit type annotations
|
|
125
|
-
- Union types for variant options (design, size, colorScheme)
|
|
126
|
-
- Event handlers properly typed (MouseEvent, KeyboardEvent)
|
|
127
|
-
- Exported types available for consumers
|
|
128
|
-
|
|
129
|
-
### Demo Application
|
|
130
|
-
|
|
131
|
-
`src/App.svelte` serves as a comprehensive showcase of all component features:
|
|
132
|
-
|
|
133
|
-
- Organized into sections by feature category
|
|
134
|
-
- Interactive examples with state display
|
|
135
|
-
- Beautiful gradient background with card-based layout
|
|
136
|
-
- Responsive design for mobile devices
|
|
137
|
-
- Not included in the distributed package (development only)
|
|
138
|
-
|
|
139
|
-
## Build Configuration
|
|
140
|
-
|
|
141
|
-
### Vite Config
|
|
142
|
-
- Library mode enabled with entry point `src/lib/Switch.svelte`
|
|
143
|
-
- Package name: 'SvelteToggleSwitch'
|
|
144
|
-
- Standard Svelte plugin configuration
|
|
145
|
-
|
|
146
|
-
### TypeScript Config
|
|
147
|
-
- Extends `@tsconfig/svelte` base configuration
|
|
148
|
-
- ESNext target for modern JavaScript
|
|
149
|
-
- Includes all `.svelte`, `.ts`, `.js`, and `.d.ts` files in `src/`
|
|
150
|
-
- Composite project with references to `tsconfig.node.json`
|
|
151
|
-
|
|
152
|
-
### Package Configuration
|
|
153
|
-
- Version 2.0.0 (major rewrite)
|
|
154
|
-
- Peer dependency: Svelte 3.x, 4.x, or 5.x
|
|
155
|
-
- Development dependencies all on latest versions
|
|
156
|
-
- MIT licensed
|
|
157
|
-
|
|
158
|
-
## Key Design Decisions
|
|
159
|
-
|
|
160
|
-
1. **Single Component Approach**: All variants in one component file for easier maintenance
|
|
161
|
-
2. **CSS-in-JS via Custom Properties**: Enables dynamic theming without style duplication
|
|
162
|
-
3. **Progressive Enhancement**: Basic functionality without JavaScript, enhanced with interactivity
|
|
163
|
-
4. **Accessibility First**: ARIA and keyboard support built-in, not added later
|
|
164
|
-
5. **TypeScript Throughout**: Type safety for better DX and fewer runtime errors
|
|
165
|
-
6. **Zero Dependencies**: No runtime dependencies beyond Svelte peer dependency
|
package/src/App.svelte
DELETED
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import Switch from './lib/Switch.svelte';
|
|
3
|
-
|
|
4
|
-
// State variables for different examples
|
|
5
|
-
let basicSwitch = false;
|
|
6
|
-
let colorSwitch = false;
|
|
7
|
-
let sizeSwitch = false;
|
|
8
|
-
let disabledSwitch = false;
|
|
9
|
-
let loadingSwitch = false;
|
|
10
|
-
let iconSwitch = false;
|
|
11
|
-
let multiSwitch = 'Option 1';
|
|
12
|
-
let labelPositionSwitch = true;
|
|
13
|
-
let animationSwitch = false;
|
|
14
|
-
let innerSwitch = false;
|
|
15
|
-
let modernSwitch = false;
|
|
16
|
-
let materialSwitch = false;
|
|
17
|
-
|
|
18
|
-
// Color schemes
|
|
19
|
-
let greenSwitch = false;
|
|
20
|
-
let redSwitch = false;
|
|
21
|
-
let purpleSwitch = false;
|
|
22
|
-
let orangeSwitch = false;
|
|
23
|
-
let pinkSwitch = false;
|
|
24
|
-
|
|
25
|
-
// Sizes
|
|
26
|
-
let xsSwitch = false;
|
|
27
|
-
let smSwitch = false;
|
|
28
|
-
let mdSwitch = false;
|
|
29
|
-
let lgSwitch = false;
|
|
30
|
-
let xlSwitch = false;
|
|
31
|
-
</script>
|
|
32
|
-
|
|
33
|
-
<main>
|
|
34
|
-
<header>
|
|
35
|
-
<h1>Svelte Toggle Switch</h1>
|
|
36
|
-
<p class="subtitle">A comprehensive, accessible toggle switch component for Svelte</p>
|
|
37
|
-
</header>
|
|
38
|
-
|
|
39
|
-
<section>
|
|
40
|
-
<h2>Design Variants</h2>
|
|
41
|
-
<div class="grid">
|
|
42
|
-
<div class="card">
|
|
43
|
-
<h3>Slider (iOS Style)</h3>
|
|
44
|
-
<Switch bind:value={basicSwitch} label="Enable notifications" design="slider" />
|
|
45
|
-
<p class="state">State: {basicSwitch}</p>
|
|
46
|
-
</div>
|
|
47
|
-
|
|
48
|
-
<div class="card">
|
|
49
|
-
<h3>Inner</h3>
|
|
50
|
-
<Switch bind:value={innerSwitch} label="Dark mode" design="inner" />
|
|
51
|
-
<p class="state">State: {innerSwitch}</p>
|
|
52
|
-
</div>
|
|
53
|
-
|
|
54
|
-
<div class="card">
|
|
55
|
-
<h3>Modern</h3>
|
|
56
|
-
<Switch bind:value={modernSwitch} label="Auto-save" design="modern" />
|
|
57
|
-
<p class="state">State: {modernSwitch}</p>
|
|
58
|
-
</div>
|
|
59
|
-
|
|
60
|
-
<div class="card">
|
|
61
|
-
<h3>Material</h3>
|
|
62
|
-
<Switch bind:value={materialSwitch} label="Bluetooth" design="material" />
|
|
63
|
-
<p class="state">State: {materialSwitch}</p>
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
</section>
|
|
67
|
-
|
|
68
|
-
<section>
|
|
69
|
-
<h2>Color Schemes</h2>
|
|
70
|
-
<div class="grid">
|
|
71
|
-
<div class="card">
|
|
72
|
-
<h3>Blue (Default)</h3>
|
|
73
|
-
<Switch bind:value={colorSwitch} label="Blue theme" colorScheme="blue" />
|
|
74
|
-
</div>
|
|
75
|
-
|
|
76
|
-
<div class="card">
|
|
77
|
-
<h3>Green</h3>
|
|
78
|
-
<Switch bind:value={greenSwitch} label="Green theme" colorScheme="green" />
|
|
79
|
-
</div>
|
|
80
|
-
|
|
81
|
-
<div class="card">
|
|
82
|
-
<h3>Red</h3>
|
|
83
|
-
<Switch bind:value={redSwitch} label="Red theme" colorScheme="red" />
|
|
84
|
-
</div>
|
|
85
|
-
|
|
86
|
-
<div class="card">
|
|
87
|
-
<h3>Purple</h3>
|
|
88
|
-
<Switch bind:value={purpleSwitch} label="Purple theme" colorScheme="purple" />
|
|
89
|
-
</div>
|
|
90
|
-
|
|
91
|
-
<div class="card">
|
|
92
|
-
<h3>Orange</h3>
|
|
93
|
-
<Switch bind:value={orangeSwitch} label="Orange theme" colorScheme="orange" />
|
|
94
|
-
</div>
|
|
95
|
-
|
|
96
|
-
<div class="card">
|
|
97
|
-
<h3>Pink</h3>
|
|
98
|
-
<Switch bind:value={pinkSwitch} label="Pink theme" colorScheme="pink" />
|
|
99
|
-
</div>
|
|
100
|
-
</div>
|
|
101
|
-
</section>
|
|
102
|
-
|
|
103
|
-
<section>
|
|
104
|
-
<h2>Size Variants</h2>
|
|
105
|
-
<div class="grid">
|
|
106
|
-
<div class="card">
|
|
107
|
-
<h3>Extra Small</h3>
|
|
108
|
-
<Switch bind:value={xsSwitch} label="XS Size" size="xs" />
|
|
109
|
-
</div>
|
|
110
|
-
|
|
111
|
-
<div class="card">
|
|
112
|
-
<h3>Small</h3>
|
|
113
|
-
<Switch bind:value={smSwitch} label="Small Size" size="sm" />
|
|
114
|
-
</div>
|
|
115
|
-
|
|
116
|
-
<div class="card">
|
|
117
|
-
<h3>Medium (Default)</h3>
|
|
118
|
-
<Switch bind:value={mdSwitch} label="Medium Size" size="md" />
|
|
119
|
-
</div>
|
|
120
|
-
|
|
121
|
-
<div class="card">
|
|
122
|
-
<h3>Large</h3>
|
|
123
|
-
<Switch bind:value={lgSwitch} label="Large Size" size="lg" />
|
|
124
|
-
</div>
|
|
125
|
-
|
|
126
|
-
<div class="card">
|
|
127
|
-
<h3>Extra Large</h3>
|
|
128
|
-
<Switch bind:value={xlSwitch} label="XL Size" size="xl" />
|
|
129
|
-
</div>
|
|
130
|
-
</div>
|
|
131
|
-
</section>
|
|
132
|
-
|
|
133
|
-
<section>
|
|
134
|
-
<h2>States</h2>
|
|
135
|
-
<div class="grid">
|
|
136
|
-
<div class="card">
|
|
137
|
-
<h3>Disabled</h3>
|
|
138
|
-
<Switch bind:value={disabledSwitch} label="Disabled switch" disabled />
|
|
139
|
-
</div>
|
|
140
|
-
|
|
141
|
-
<div class="card">
|
|
142
|
-
<h3>Loading</h3>
|
|
143
|
-
<Switch bind:value={loadingSwitch} label="Loading state" loading />
|
|
144
|
-
</div>
|
|
145
|
-
|
|
146
|
-
<div class="card">
|
|
147
|
-
<h3>Read-only</h3>
|
|
148
|
-
<Switch bind:value={disabledSwitch} label="Read-only switch" readonly />
|
|
149
|
-
</div>
|
|
150
|
-
</div>
|
|
151
|
-
</section>
|
|
152
|
-
|
|
153
|
-
<section>
|
|
154
|
-
<h2>Icon Support</h2>
|
|
155
|
-
<div class="grid">
|
|
156
|
-
<div class="card">
|
|
157
|
-
<h3>Slider with Icons</h3>
|
|
158
|
-
<Switch
|
|
159
|
-
bind:value={iconSwitch}
|
|
160
|
-
label="Wi-Fi"
|
|
161
|
-
design="slider"
|
|
162
|
-
showIcons={true}
|
|
163
|
-
onIcon="✓"
|
|
164
|
-
offIcon="✕"
|
|
165
|
-
/>
|
|
166
|
-
</div>
|
|
167
|
-
|
|
168
|
-
<div class="card">
|
|
169
|
-
<h3>Modern with Custom Icons</h3>
|
|
170
|
-
<Switch
|
|
171
|
-
bind:value={iconSwitch}
|
|
172
|
-
label="Airplane Mode"
|
|
173
|
-
design="modern"
|
|
174
|
-
showIcons={true}
|
|
175
|
-
onIcon="✈"
|
|
176
|
-
offIcon="✕"
|
|
177
|
-
colorScheme="purple"
|
|
178
|
-
/>
|
|
179
|
-
</div>
|
|
180
|
-
|
|
181
|
-
<div class="card">
|
|
182
|
-
<h3>Custom Emoji Icons</h3>
|
|
183
|
-
<Switch
|
|
184
|
-
bind:value={iconSwitch}
|
|
185
|
-
label="Theme"
|
|
186
|
-
design="slider"
|
|
187
|
-
showIcons={true}
|
|
188
|
-
onIcon="🌙"
|
|
189
|
-
offIcon="☀"
|
|
190
|
-
colorScheme="orange"
|
|
191
|
-
size="lg"
|
|
192
|
-
/>
|
|
193
|
-
</div>
|
|
194
|
-
</div>
|
|
195
|
-
</section>
|
|
196
|
-
|
|
197
|
-
<section>
|
|
198
|
-
<h2>Multi-option Switch</h2>
|
|
199
|
-
<div class="card">
|
|
200
|
-
<h3>Toggle Group</h3>
|
|
201
|
-
<Switch
|
|
202
|
-
bind:value={multiSwitch}
|
|
203
|
-
label="Select an option"
|
|
204
|
-
design="multi"
|
|
205
|
-
options={['Option 1', 'Option 2', 'Option 3']}
|
|
206
|
-
/>
|
|
207
|
-
<p class="state">Selected: {multiSwitch}</p>
|
|
208
|
-
</div>
|
|
209
|
-
|
|
210
|
-
<div class="card">
|
|
211
|
-
<h3>View Mode Selector</h3>
|
|
212
|
-
<Switch
|
|
213
|
-
bind:value={multiSwitch}
|
|
214
|
-
label="View"
|
|
215
|
-
design="multi"
|
|
216
|
-
options={['Grid', 'List']}
|
|
217
|
-
size="sm"
|
|
218
|
-
colorScheme="green"
|
|
219
|
-
/>
|
|
220
|
-
</div>
|
|
221
|
-
</section>
|
|
222
|
-
|
|
223
|
-
<section>
|
|
224
|
-
<h2>Advanced Features</h2>
|
|
225
|
-
<div class="grid">
|
|
226
|
-
<div class="card">
|
|
227
|
-
<h3>Label Position</h3>
|
|
228
|
-
<Switch
|
|
229
|
-
bind:value={labelPositionSwitch}
|
|
230
|
-
label="Label on left"
|
|
231
|
-
labelPosition="left"
|
|
232
|
-
/>
|
|
233
|
-
</div>
|
|
234
|
-
|
|
235
|
-
<div class="card">
|
|
236
|
-
<h3>With Shadow</h3>
|
|
237
|
-
<Switch bind:value={sizeSwitch} label="Shadow effect" shadow />
|
|
238
|
-
</div>
|
|
239
|
-
|
|
240
|
-
<div class="card">
|
|
241
|
-
<h3>With Outline</h3>
|
|
242
|
-
<Switch bind:value={sizeSwitch} label="Outline style" outline />
|
|
243
|
-
</div>
|
|
244
|
-
|
|
245
|
-
<div class="card">
|
|
246
|
-
<h3>Custom Animation</h3>
|
|
247
|
-
<Switch
|
|
248
|
-
bind:value={animationSwitch}
|
|
249
|
-
label="Slow animation"
|
|
250
|
-
animationDuration={800}
|
|
251
|
-
animationEasing="cubic-bezier(0.68, -0.55, 0.265, 1.55)"
|
|
252
|
-
colorScheme="pink"
|
|
253
|
-
/>
|
|
254
|
-
</div>
|
|
255
|
-
|
|
256
|
-
<div class="card">
|
|
257
|
-
<h3>Custom Colors</h3>
|
|
258
|
-
<Switch
|
|
259
|
-
bind:value={animationSwitch}
|
|
260
|
-
label="Custom palette"
|
|
261
|
-
colorScheme="custom"
|
|
262
|
-
color="#FF6B6B"
|
|
263
|
-
offColor="#F0F0F0"
|
|
264
|
-
/>
|
|
265
|
-
</div>
|
|
266
|
-
</div>
|
|
267
|
-
</section>
|
|
268
|
-
|
|
269
|
-
<footer>
|
|
270
|
-
<p>Built with Svelte • TypeScript • Vite</p>
|
|
271
|
-
<p>
|
|
272
|
-
<a href="https://github.com/ishansasika/svelte-toggle-switch" target="_blank" rel="noopener">
|
|
273
|
-
View on GitHub
|
|
274
|
-
</a>
|
|
275
|
-
</p>
|
|
276
|
-
</footer>
|
|
277
|
-
</main>
|
|
278
|
-
|
|
279
|
-
<style>
|
|
280
|
-
:root {
|
|
281
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
|
282
|
-
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
283
|
-
line-height: 1.6;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
main {
|
|
287
|
-
max-width: 1200px;
|
|
288
|
-
margin: 0 auto;
|
|
289
|
-
padding: 2rem;
|
|
290
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
291
|
-
min-height: 100vh;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
header {
|
|
295
|
-
text-align: center;
|
|
296
|
-
margin-bottom: 3rem;
|
|
297
|
-
color: white;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
h1 {
|
|
301
|
-
font-size: 3rem;
|
|
302
|
-
margin: 0;
|
|
303
|
-
font-weight: 700;
|
|
304
|
-
letter-spacing: -0.02em;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
.subtitle {
|
|
308
|
-
font-size: 1.25rem;
|
|
309
|
-
opacity: 0.9;
|
|
310
|
-
margin-top: 0.5rem;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
section {
|
|
314
|
-
background: white;
|
|
315
|
-
border-radius: 1rem;
|
|
316
|
-
padding: 2rem;
|
|
317
|
-
margin-bottom: 2rem;
|
|
318
|
-
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
h2 {
|
|
322
|
-
margin-top: 0;
|
|
323
|
-
color: #1f2937;
|
|
324
|
-
font-size: 1.75rem;
|
|
325
|
-
margin-bottom: 1.5rem;
|
|
326
|
-
border-bottom: 2px solid #e5e7eb;
|
|
327
|
-
padding-bottom: 0.5rem;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
h3 {
|
|
331
|
-
color: #4b5563;
|
|
332
|
-
font-size: 1rem;
|
|
333
|
-
margin-top: 0;
|
|
334
|
-
margin-bottom: 1rem;
|
|
335
|
-
font-weight: 600;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
.grid {
|
|
339
|
-
display: grid;
|
|
340
|
-
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
341
|
-
gap: 1.5rem;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
.card {
|
|
345
|
-
background: #f9fafb;
|
|
346
|
-
padding: 1.5rem;
|
|
347
|
-
border-radius: 0.75rem;
|
|
348
|
-
border: 1px solid #e5e7eb;
|
|
349
|
-
transition: all 0.3s ease;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
.card:hover {
|
|
353
|
-
border-color: #d1d5db;
|
|
354
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
|
355
|
-
transform: translateY(-2px);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
.state {
|
|
359
|
-
margin-top: 1rem;
|
|
360
|
-
padding: 0.5rem;
|
|
361
|
-
background: white;
|
|
362
|
-
border-radius: 0.375rem;
|
|
363
|
-
font-size: 0.875rem;
|
|
364
|
-
color: #6b7280;
|
|
365
|
-
font-family: 'Courier New', monospace;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
footer {
|
|
369
|
-
text-align: center;
|
|
370
|
-
color: white;
|
|
371
|
-
padding: 2rem 0;
|
|
372
|
-
margin-top: 2rem;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
footer p {
|
|
376
|
-
margin: 0.5rem 0;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
footer a {
|
|
380
|
-
color: white;
|
|
381
|
-
text-decoration: underline;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
footer a:hover {
|
|
385
|
-
opacity: 0.8;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
@media (max-width: 768px) {
|
|
389
|
-
main {
|
|
390
|
-
padding: 1rem;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
h1 {
|
|
394
|
-
font-size: 2rem;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
.subtitle {
|
|
398
|
-
font-size: 1rem;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
section {
|
|
402
|
-
padding: 1.5rem;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
.grid {
|
|
406
|
-
grid-template-columns: 1fr;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
</style>
|
package/src/vite-env.d.ts
DELETED
package/tsconfig.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "@tsconfig/svelte/tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"target": "ESNext",
|
|
5
|
-
"useDefineForClassFields": true,
|
|
6
|
-
"module": "ESNext",
|
|
7
|
-
"resolveJsonModule": true,
|
|
8
|
-
"allowJs": true,
|
|
9
|
-
"checkJs": true,
|
|
10
|
-
"isolatedModules": true,
|
|
11
|
-
"moduleDetection": "force"
|
|
12
|
-
},
|
|
13
|
-
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
|
|
14
|
-
}
|