# svelte-toggle-switch v3.0.0 Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Release v3.0.0 of `svelte-toggle-switch` with Svelte 5 runes migration, typed callback events, three new design variants (neon, flip, pill), and three new interaction features (drag momentum, long press, confirm toggle).

**Architecture:** Migrate `src/lib/Switch.svelte` from Svelte Options API (`export let`, `createEventDispatcher`, `$:`) to Svelte 5 runes (`$props`, `$state`, `$derived`, `$effect`). Add new design variants as `{#if design === 'x'}` blocks with scoped CSS. Add new interaction props layered onto the existing click/touch handler system. Update demo homepage, playground metadata, docs, CHANGELOG, and README.

**Tech Stack:** Svelte 5.16.0, TypeScript 5.7.3, Vite 6.0.5, SvelteKit (demo), Tailwind CSS (demo)

## Global Constraints

- Branch: `release/v3.0.0-runes-new-variants-interactions`
- Version bump target: `2.3.1` → `3.0.0` in `package.json`
- Never add `Co-Authored-By: Claude` lines to commits
- All non-event props must remain backward-compatible (same names, same defaults)
- Event API breaking change: `on:change/focus/blur` → `onchange/onfocus/onblur` callback props
- New design values added to `design` union: `'flip' | 'neon' | 'pill'`
- Verify after each task: `npm run check` from project root (svelte-check)
- Build verify after component tasks: `npm run build` from project root
- No external runtime dependencies introduced

---

## File Map

| File | Action | Purpose |
|------|--------|---------|
| `src/lib/Switch.svelte` | Modify | Runes migration + all new variants + all new interactions |
| `src/lib/index.ts` | Create | Barrel export with `SwitchProps` type + `Switch` component |
| `demo/src/lib/data/componentMetadata.ts` | Modify | Add new design options + new interaction props to playground |
| `demo/src/routes/+page.svelte` | Modify | Add v3.0.0 showcase section + update feature list |
| `demo/src/routes/docs/+page.svelte` | Modify | Add new API reference for v3.0.0 props |
| `CHANGELOG.md` | Modify | Add v3.0.0 entry at top |
| `README.md` | Modify | Update feature list, version badge, new variants/interactions |
| `package.json` | Modify | Bump version to `3.0.0` |
| `CLAUDE.md` | Modify | Update version references to v3.0.0 |

---

## Task 1: Svelte 5 Runes Migration + TypeScript Barrel Export

**Files:**
- Modify: `src/lib/Switch.svelte` (script block only, lines 1–384)
- Create: `src/lib/index.ts`

**What changes:**
- `import { createEventDispatcher } from 'svelte'` → removed
- `const dispatch = createEventDispatcher()` → removed
- All `export let propName: Type = default` → single `let { propName = default, ... } = $props<SwitchProps>()`
- Add `onchange`, `onfocus`, `onblur` as optional callback props
- `let checked: boolean = ...` → `let checked = $state(typeof value === 'boolean' ? value : value === 'on')`
- `let isDarkMode = false` → `let isDarkMode = $state(false)`
- `let touchStartX = 0; let touchCurrentX = 0; let isDragging = false; let dragOffset = 0` → all `$state(0)` / `$state(false)`
- `let showSuccess = false; let showError = false; let isTooltipVisible = false` → all `$state(false)`
- `let tooltipTimeoutId` → `$state<ReturnType<typeof setTimeout> | null>(null)`
- `let isRTL = false` → `$state(false)`
- `$: activeColor = (...)()` → `let activeColor = $derived(...)`
- `$: darkOffColor = isDarkMode ? '#374151' : offColor` → `let darkOffColor = $derived(isDarkMode ? '#374151' : offColor)`
- `$: if (design !== 'multi') { ... }` → `$effect(() => { ... })`
- `dispatch('change', payload)` → `onchange?.(payload)`
- `dispatch('focus', payload)` → `onfocus?.(payload)`
- `dispatch('blur', payload)` → `onblur?.(payload)`
- All template DOM handlers: `on:click` → `onclick`, `on:keydown` → `onkeydown`, `on:focus` → `onfocus`, `on:blur` → `onblur`, `on:mouseenter` → `onmouseenter`, `on:mouseleave` → `onmouseleave`, `on:touchstart` → `ontouchstart`, `on:touchmove` → `ontouchmove`, `on:touchend` → `ontouchend`, `on:change` → `onchange` (for radio inputs in multi)

**Interfaces:**
- Produces: `Switch` default export, `SwitchProps` named export from `src/lib/index.ts`
- `onchange?: (payload: { value: boolean | string; checked: boolean }) => void`
- `onfocus?: (payload: { event: FocusEvent }) => void`
- `onblur?: (payload: { event: FocusEvent }) => void`

- [ ] **Step 1: Migrate the script block**

Replace the entire `<script lang="ts">` block in `src/lib/Switch.svelte` with:

```svelte
<script lang="ts">
	// ── v3.0.0: Svelte 5 runes migration ──────────────────────────────

	// Prop types
	type ColorScheme = 'blue' | 'green' | 'red' | 'purple' | 'orange' | 'pink' | 'yellow' | 'indigo' | 'teal' | 'custom';
	type Design = 'inner' | 'slider' | 'ios' | 'modern' | 'material' | 'multi' | 'neon' | 'flip' | 'pill';
	type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number;
	type PulseIntensity = 'subtle' | 'normal' | 'strong';
	type SkeletonAnimation = 'shimmer' | 'pulse' | 'wave';
	type TooltipPosition = 'top' | 'bottom' | 'left' | 'right';
	type HapticPattern = 'light' | 'medium' | 'heavy' | number[];
	type GradientPreset = 'sunset' | 'ocean' | 'forest' | 'berry' | 'fire' | 'custom';
	type Dir = 'ltr' | 'rtl' | 'auto';

	let {
		// Core
		value = $bindable<boolean | string>(false),
		label = '',
		design = 'slider' as Design,
		options = [] as string[],
		// Styling
		size = 'md' as Size,
		color = '#007AFF',
		offColor = '#E5E7EB',
		colorScheme = 'blue' as ColorScheme,
		// State
		disabled = false,
		loading = false,
		readonly = false,
		// Icons
		showIcons = false,
		onIcon = '✓',
		offIcon = '✕',
		// Animation
		animationDuration = 300,
		animationEasing = 'ease-in-out',
		// Accessibility
		ariaLabel = '',
		ariaDescribedBy = '',
		id = '',
		name = '',
		tabIndex = 0,
		// Advanced
		labelPosition = 'right' as 'left' | 'right',
		rounded = true,
		shadow = false,
		outline = false,
		// v2.1.0
		onText = 'ON',
		offText = 'OFF',
		helperText = '',
		errorText = '',
		required = false,
		error = false,
		// v2.2.0
		darkMode = false as boolean | 'auto',
		gradient = false,
		gradientPreset = 'sunset' as GradientPreset,
		customGradient = '',
		swipeToToggle = false,
		swipeThreshold = 50,
		dir = 'auto' as Dir,
		// v2.3.0
		pulse = false,
		pulseColor = '',
		pulseIntensity = 'normal' as PulseIntensity,
		showSuccessAnimation = false,
		showErrorAnimation = false,
		successDuration = 1500,
		hapticFeedback = false,
		hapticPattern = 'light' as HapticPattern,
		skeleton = false,
		skeletonAnimation = 'shimmer' as SkeletonAnimation,
		tooltip = '',
		tooltipPosition = 'top' as TooltipPosition,
		tooltipDelay = 300,
		// v3.0.0 – New design variant props
		flipFrontContent = 'OFF',
		flipBackContent = 'ON',
		// v3.0.0 – New interaction props
		dragMomentum = false,
		longPress = false,
		longPressDuration = 600,
		confirmToggle = false,
		confirmMessage = 'Are you sure?',
		onconfirm = undefined as (() => Promise<boolean>) | undefined,
		// v3.0.0 – Callback events (replaces createEventDispatcher)
		onchange = undefined as ((payload: { value: boolean | string; checked: boolean }) => void) | undefined,
		onfocus = undefined as ((payload: { event: FocusEvent }) => void) | undefined,
		onblur = undefined as ((payload: { event: FocusEvent }) => void) | undefined,
	} = $props();

	// ── Internal state ─────────────────────────────────────────────────
	let checked = $state(typeof value === 'boolean' ? value : value === 'on');
	const uniqueID = id || `switch-${Math.floor(Math.random() * 1000000)}`;

	// Dark mode
	let isDarkMode = $state(false);
	if (typeof window !== 'undefined') {
		if (darkMode === 'auto') {
			isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
			window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
				isDarkMode = e.matches;
			});
		} else {
			isDarkMode = darkMode === true;
		}
	}

	// Gradient presets
	const gradientPresets = {
		sunset: 'linear-gradient(135deg, #FF6B6B 0%, #FFE66D 100%)',
		ocean: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
		forest: 'linear-gradient(135deg, #10B981 0%, #059669 100%)',
		berry: 'linear-gradient(135deg, #EC4899 0%, #8B5CF6 100%)',
		fire: 'linear-gradient(135deg, #F97316 0%, #EF4444 100%)',
		custom: customGradient
	};

	// Touch gesture state
	let touchStartX = $state(0);
	let touchCurrentX = $state(0);
	let isDragging = $state(false);
	let dragOffset = $state(0);

	// Animation states
	let showSuccess = $state(false);
	let showError = $state(false);
	let isTooltipVisible = $state(false);
	let tooltipTimeoutId = $state<ReturnType<typeof setTimeout> | null>(null);

	// v3.0.0 – Interaction states
	let isPending = $state(false);
	let showConfirmPrompt = $state(false);
	let longPressProgress = $state(0);
	let longPressTimer = $state<ReturnType<typeof setTimeout> | null>(null);
	let longPressInterval = $state<ReturnType<typeof setInterval> | null>(null);
	let dragStartX = $state(0);
	let dragStartY = $state(0);
	let dragStartTime = $state(0);
	let isDragActive = $state(false);
	let dragCurrentX = $state(0);
	let dragThumbOffset = $state(0);

	// RTL detection
	let isRTL = $state(false);
	if (typeof document !== 'undefined') {
		if (dir === 'auto') {
			isRTL = document.documentElement.dir === 'rtl' || document.body.dir === 'rtl';
		} else {
			isRTL = dir === 'rtl';
		}
	}

	// Color schemes
	const colorSchemes: Record<string, string> = {
		blue: '#007AFF', green: '#10B981', red: '#EF4444', purple: '#8B5CF6',
		orange: '#F97316', pink: '#EC4899', yellow: '#F59E0B', indigo: '#6366F1',
		teal: '#14B8A6', custom: color
	};
	const darkColorSchemes: Record<string, string> = {
		blue: '#0A84FF', green: '#30D158', red: '#FF453A', purple: '#BF5AF2',
		orange: '#FF9F0A', pink: '#FF375F', yellow: '#FFD60A', indigo: '#5E5CE6',
		teal: '#40C8E0', custom: color
	};
	// v3.0.0 – Neon glow colors per scheme
	const neonColorSchemes: Record<string, string> = {
		blue: '#00BFFF', green: '#39FF14', red: '#FF073A', purple: '#CF4AFF',
		orange: '#FF6600', pink: '#FF69B4', yellow: '#FFE000', indigo: '#7B61FF',
		teal: '#00FFEF', custom: color
	};

	// Derived values
	let activeColor = $derived((() => {
		if (gradient) {
			return gradientPreset === 'custom' ? customGradient : gradientPresets[gradientPreset];
		}
		if (isDarkMode) {
			return colorScheme === 'custom' ? color : darkColorSchemes[colorScheme];
		}
		return colorScheme === 'custom' ? color : colorSchemes[colorScheme];
	})());

	let neonColor = $derived(colorScheme === 'custom' ? color : neonColorSchemes[colorScheme]);

	let darkOffColor = $derived(isDarkMode ? '#374151' : offColor);

	// Size map
	const sizeMap: Record<string, number> = { xs: 0.75, sm: 0.875, md: 1, lg: 1.25, xl: 1.5 };
	const fontSize = typeof size === 'number' ? size : sizeMap[size];

	// Pulse intensity
	const pulseIntensityMap = {
		subtle: { scale: 1.02, opacity: 0.3 },
		normal: { scale: 1.05, opacity: 0.5 },
		strong: { scale: 1.1, opacity: 0.7 }
	};

	// Haptic patterns
	const hapticPatterns: Record<string, number[]> = {
		light: [10], medium: [20], heavy: [30, 10, 30]
	};

	// Sync checked ← value (external changes)
	$effect(() => {
		if (design !== 'multi') {
			const newChecked = typeof value === 'boolean' ? value : value === 'on';
			if (newChecked !== checked) checked = newChecked;
		}
	});

	// ── Helpers ────────────────────────────────────────────────────────
	function triggerHaptic() {
		if (!hapticFeedback || typeof navigator === 'undefined' || !navigator.vibrate) return;
		const pattern = typeof hapticPattern === 'object' ? hapticPattern : hapticPatterns[hapticPattern as string];
		try { navigator.vibrate(pattern); } catch { /* not supported */ }
	}

	function triggerSuccessAnimation() {
		if (!showSuccessAnimation) return;
		showSuccess = true;
		setTimeout(() => { showSuccess = false; }, successDuration);
	}

	function triggerErrorAnimation() {
		if (!showErrorAnimation) return;
		showError = true;
		setTimeout(() => { showError = false; }, 500);
	}

	function handleMouseEnter() {
		if (!tooltip) return;
		tooltipTimeoutId = setTimeout(() => { isTooltipVisible = true; }, tooltipDelay);
	}

	function handleMouseLeave() {
		if (tooltipTimeoutId) { clearTimeout(tooltipTimeoutId); tooltipTimeoutId = null; }
		isTooltipVisible = false;
	}

	function commitToggle() {
		if (disabled || loading || readonly || skeleton) return;
		if (design !== 'multi') {
			const newChecked = !checked;
			checked = newChecked;
			const newValue = newChecked
				? (typeof value === 'boolean' ? true : 'on')
				: (typeof value === 'boolean' ? false : 'off');
			value = newValue;
			triggerHaptic();
			triggerSuccessAnimation();
			onchange?.({ value: newValue, checked: newChecked });
		}
	}

	// ── v3.0.0 – confirmToggle ─────────────────────────────────────────
	async function handleConfirmToggle() {
		if (onconfirm) {
			isPending = true;
			const confirmed = await onconfirm();
			isPending = false;
			if (confirmed) commitToggle();
		} else {
			showConfirmPrompt = true;
		}
	}

	function confirmYes() {
		showConfirmPrompt = false;
		commitToggle();
	}

	function confirmNo() {
		showConfirmPrompt = false;
	}

	// ── v3.0.0 – longPress ────────────────────────────────────────────
	function startLongPress() {
		if (disabled || loading || readonly || skeleton) return;
		longPressProgress = 0;
		const step = 50;
		longPressInterval = setInterval(() => {
			longPressProgress = Math.min(100, longPressProgress + (step / longPressDuration) * 100);
		}, step);
		longPressTimer = setTimeout(() => {
			cancelLongPress();
			if (confirmToggle) {
				handleConfirmToggle();
			} else {
				commitToggle();
			}
		}, longPressDuration);
	}

	function cancelLongPress() {
		if (longPressTimer) { clearTimeout(longPressTimer); longPressTimer = null; }
		if (longPressInterval) { clearInterval(longPressInterval); longPressInterval = null; }
		longPressProgress = 0;
	}

	// ── v3.0.0 – dragMomentum ─────────────────────────────────────────
	function handleDragStart(event: PointerEvent) {
		if (!dragMomentum || disabled || loading || readonly || skeleton) return;
		isDragActive = true;
		dragStartX = event.clientX;
		dragStartY = event.clientY;
		dragStartTime = Date.now();
		dragCurrentX = event.clientX;
		dragThumbOffset = 0;
		(event.currentTarget as HTMLElement)?.setPointerCapture(event.pointerId);
	}

	function handleDragMove(event: PointerEvent) {
		if (!dragMomentum || !isDragActive) return;
		dragCurrentX = event.clientX;
		const diff = isRTL ? dragStartX - event.clientX : event.clientX - dragStartX;
		dragThumbOffset = Math.max(-40, Math.min(40, diff));
	}

	function handleDragEnd(event: PointerEvent) {
		if (!dragMomentum || !isDragActive) return;
		isDragActive = false;
		const elapsed = Math.max(1, Date.now() - dragStartTime);
		const totalDiff = isRTL ? dragStartX - event.clientX : event.clientX - dragStartX;
		const velocity = totalDiff / elapsed; // px/ms
		dragThumbOffset = 0;

		const shouldToggleByVelocity = Math.abs(velocity) > 0.3;
		const shouldToggleByPosition = Math.abs(totalDiff) > 20;

		if (shouldToggleByVelocity || shouldToggleByPosition) {
			const shouldBeOn = totalDiff > 0;
			const currentlyOn = checked;
			if (shouldBeOn !== currentlyOn) commitToggle();
		}
	}

	// ── Main click handler ─────────────────────────────────────────────
	function handleClick(event: MouseEvent) {
		if (disabled || loading || readonly || skeleton) {
			event.preventDefault();
			return;
		}
		if (longPress) return; // long press mode; click disabled
		if (dragMomentum && Math.abs(dragCurrentX - dragStartX) > 5) return; // was a drag, not a click

		if (design !== 'multi') {
			if (confirmToggle) {
				handleConfirmToggle();
			} else {
				commitToggle();
			}
		}
	}

	function handleKeyDown(event: KeyboardEvent) {
		if (disabled || loading || readonly || skeleton) return;
		if (event.key === ' ' || event.key === 'Enter') {
			event.preventDefault();
			if (design !== 'multi') {
				if (confirmToggle) {
					handleConfirmToggle();
				} else {
					commitToggle();
				}
			}
		}
	}

	function handleFocusEvent(event: FocusEvent) {
		onfocus?.({ event });
	}

	function handleBlurEvent(event: FocusEvent) {
		onblur?.({ event });
	}

	function handleMultiChange() {
		onchange?.({ value: value as string, checked: true });
	}

	// Touch gesture handlers (existing swipeToToggle)
	function handleTouchStart(event: TouchEvent) {
		if (!swipeToToggle || disabled || loading || readonly || skeleton) return;
		touchStartX = event.touches[0].clientX;
		touchCurrentX = touchStartX;
		isDragging = true;
	}

	function handleTouchMove(event: TouchEvent) {
		if (!swipeToToggle || !isDragging || disabled || loading || readonly || skeleton) return;
		touchCurrentX = event.touches[0].clientX;
		const diff = touchCurrentX - touchStartX;
		const adjustedDiff = isRTL ? -diff : diff;
		dragOffset = Math.max(-swipeThreshold, Math.min(swipeThreshold, adjustedDiff));
	}

	function handleTouchEnd(event: TouchEvent) {
		if (!swipeToToggle || !isDragging || disabled || loading || readonly || skeleton) return;
		const diff = touchCurrentX - touchStartX;
		const adjustedDiff = isRTL ? -diff : diff;
		if (Math.abs(adjustedDiff) >= swipeThreshold) {
			const shouldBeChecked = adjustedDiff > 0;
			if (shouldBeChecked !== checked) {
				checked = shouldBeChecked;
				const newValue = checked
					? (typeof value === 'boolean' ? true : 'on')
					: (typeof value === 'boolean' ? false : 'off');
				value = newValue;
				triggerHaptic();
				triggerSuccessAnimation();
				onchange?.({ value: newValue, checked });
			}
		}
		isDragging = false;
		dragOffset = 0;
		touchStartX = 0;
		touchCurrentX = 0;
	}
</script>
```

- [ ] **Step 2: Update all template event handlers**

In every `{#if design === '...'}` block, replace the event attribute syntax on `<button>`, `<input>`, and container `<div>` elements:

| Old | New |
|-----|-----|
| `on:click={handleClick}` | `onclick={handleClick}` |
| `on:keydown={handleKeyDown}` | `onkeydown={handleKeyDown}` |
| `on:focus={handleFocus}` | `onfocus={handleFocusEvent}` |
| `on:blur={handleBlur}` | `onblur={handleBlurEvent}` |
| `on:mouseenter={handleMouseEnter}` | `onmouseenter={handleMouseEnter}` |
| `on:mouseleave={handleMouseLeave}` | `onmouseleave={handleMouseLeave}` |
| `on:touchstart={handleTouchStart}` | `ontouchstart={handleTouchStart}` |
| `on:touchmove={handleTouchMove}` | `ontouchmove={handleTouchMove}` |
| `on:touchend={handleTouchEnd}` | `ontouchend={handleTouchEnd}` |
| `on:change={handleMultiChange}` (radio input) | `onchange={handleMultiChange}` |

Also update the `playground/[componentId]/+page.svelte` which uses `export let data` — change to `let { data } = $props()`.

- [ ] **Step 3: Create `src/lib/index.ts`**

```typescript
export { default as Switch } from './Switch.svelte';

export type SwitchProps = {
	value?: boolean | string;
	label?: string;
	design?: 'inner' | 'slider' | 'ios' | 'modern' | 'material' | 'multi' | 'neon' | 'flip' | 'pill';
	options?: string[];
	size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number;
	color?: string;
	offColor?: string;
	colorScheme?: 'blue' | 'green' | 'red' | 'purple' | 'orange' | 'pink' | 'yellow' | 'indigo' | 'teal' | 'custom';
	disabled?: boolean;
	loading?: boolean;
	readonly?: boolean;
	showIcons?: boolean;
	onIcon?: string;
	offIcon?: string;
	animationDuration?: number;
	animationEasing?: string;
	ariaLabel?: string;
	ariaDescribedBy?: string;
	id?: string;
	name?: string;
	tabIndex?: number;
	labelPosition?: 'left' | 'right';
	rounded?: boolean;
	shadow?: boolean;
	outline?: boolean;
	onText?: string;
	offText?: string;
	helperText?: string;
	errorText?: string;
	required?: boolean;
	error?: boolean;
	darkMode?: boolean | 'auto';
	gradient?: boolean;
	gradientPreset?: 'sunset' | 'ocean' | 'forest' | 'berry' | 'fire' | 'custom';
	customGradient?: string;
	swipeToToggle?: boolean;
	swipeThreshold?: number;
	dir?: 'ltr' | 'rtl' | 'auto';
	pulse?: boolean;
	pulseColor?: string;
	pulseIntensity?: 'subtle' | 'normal' | 'strong';
	showSuccessAnimation?: boolean;
	showErrorAnimation?: boolean;
	successDuration?: number;
	hapticFeedback?: boolean;
	hapticPattern?: 'light' | 'medium' | 'heavy' | number[];
	skeleton?: boolean;
	skeletonAnimation?: 'shimmer' | 'pulse' | 'wave';
	tooltip?: string;
	tooltipPosition?: 'top' | 'bottom' | 'left' | 'right';
	tooltipDelay?: number;
	// v3.0.0
	flipFrontContent?: string;
	flipBackContent?: string;
	dragMomentum?: boolean;
	longPress?: boolean;
	longPressDuration?: number;
	confirmToggle?: boolean;
	confirmMessage?: string;
	onconfirm?: () => Promise<boolean>;
	onchange?: (payload: { value: boolean | string; checked: boolean }) => void;
	onfocus?: (payload: { event: FocusEvent }) => void;
	onblur?: (payload: { event: FocusEvent }) => void;
};
```

- [ ] **Step 4: Verify TypeScript passes**

```bash
cd /Users/ishankarunaratne/Documents/Projects/ishansasika/svelte-toggle-switch
npm run check
```

Expected: 0 errors. Fix any type errors before proceeding.

- [ ] **Step 5: Verify build passes**

```bash
npm run build
```

Expected: Build completes without errors. `dist/` folder updated.

- [ ] **Step 6: Commit**

```bash
git add src/lib/Switch.svelte src/lib/index.ts
git commit -m "feat: Migrate to Svelte 5 runes and add typed callback events"
```

---

## Task 2: Neon Design Variant

**Files:**
- Modify: `src/lib/Switch.svelte` (template + style block)

**Interfaces:**
- Consumes: `neonColor` ($derived from Task 1), `checked`, `activeColor`, `uniqueID`, all standard props
- Produces: `design="neon"` renders a cyberpunk glow slider

- [ ] **Step 1: Add neon template block**

Add this block in `Switch.svelte` between the `material` and `multi` `{:else if}` blocks (before `{:else if design === 'multi'}`):

```svelte
{:else if design === 'neon'}
	<div
		class="switch-container"
		class:disabled
		class:readonly
		class:dark={isDarkMode}
		class:skeleton
		style="font-size: {fontSize}rem; direction: {isRTL ? 'rtl' : 'ltr'};"
		onmouseenter={handleMouseEnter}
		onmouseleave={handleMouseLeave}
		role="presentation"
	>
		{#if label && labelPosition === 'left'}
			<span class="switch-label" class:skeleton-text={skeleton} id="{uniqueID}-label">{skeleton ? '' : label}</span>
		{/if}
		<div class="switch-wrapper">
			{#if skeleton}
				<div class="switch-skeleton switch-skeleton--slider" class:skeleton-shimmer={skeletonAnimation === 'shimmer'} class:skeleton-pulse={skeletonAnimation === 'pulse'} class:skeleton-wave={skeletonAnimation === 'wave'}></div>
			{:else}
				<button
					type="button"
					role="switch"
					aria-checked={checked}
					aria-label={ariaLabel || label}
					aria-labelledby={label ? `${uniqueID}-label` : undefined}
					aria-describedby={ariaDescribedBy || (helperText || errorText ? `${uniqueID}-helper` : undefined)}
					aria-required={required}
					aria-invalid={error}
					{disabled}
					tabindex={tabIndex}
					class="switch switch--neon"
					class:checked
					class:loading
					class:shadow
					class:outline
					class:error
					class:pulse
					class:pulse-subtle={pulse && pulseIntensity === 'subtle'}
					class:pulse-normal={pulse && pulseIntensity === 'normal'}
					class:pulse-strong={pulse && pulseIntensity === 'strong'}
					class:success-animation={showSuccess}
					class:error-animation={showError}
					class:long-press-active={longPress && longPressProgress > 0}
					class:pending={isPending}
					onclick={handleClick}
					onkeydown={handleKeyDown}
					onfocus={handleFocusEvent}
					onblur={handleBlurEvent}
					onpointerdown={dragMomentum ? handleDragStart : longPress ? startLongPress : undefined}
					onpointermove={dragMomentum ? handleDragMove : undefined}
					onpointerup={dragMomentum ? handleDragEnd : longPress ? cancelLongPress : undefined}
					onpointerleave={longPress ? cancelLongPress : undefined}
					style="
						--active-color: {activeColor};
						--neon-color: {neonColor};
						--off-color: {darkOffColor};
						--animation-duration: {animationDuration}ms;
						--animation-easing: {animationEasing};
						--pulse-color: {pulseColor || neonColor};
						--long-press-progress: {longPressProgress}%;
					"
				>
					<span class="switch-track switch-track--neon">
						<span class="switch-thumb switch-thumb--neon">
							{#if loading}
								<span class="spinner-small"></span>
							{:else if showSuccess}
								<span class="success-icon-small">✓</span>
							{:else if showIcons}
								<span class="switch-icon">{checked ? onIcon : offIcon}</span>
							{/if}
							{#if longPress && longPressProgress > 0}
								<svg class="long-press-ring" viewBox="0 0 36 36">
									<circle class="long-press-ring-track" cx="18" cy="18" r="16" />
									<circle class="long-press-ring-fill" cx="18" cy="18" r="16"
										stroke-dasharray="100.5"
										stroke-dashoffset="{100.5 - (longPressProgress / 100) * 100.5}" />
								</svg>
							{/if}
						</span>
					</span>
				</button>
			{/if}
			{#if tooltip && isTooltipVisible}
				<div class="switch-tooltip" class:tooltip-top={tooltipPosition === 'top'} class:tooltip-bottom={tooltipPosition === 'bottom'} class:tooltip-left={tooltipPosition === 'left'} class:tooltip-right={tooltipPosition === 'right'}>
					{tooltip}
				</div>
			{/if}
			{#if showConfirmPrompt}
				<div class="confirm-prompt">
					<span class="confirm-message">{confirmMessage}</span>
					<button class="confirm-btn confirm-btn--yes" onclick={confirmYes}>Yes</button>
					<button class="confirm-btn confirm-btn--no" onclick={confirmNo}>No</button>
				</div>
			{/if}
		</div>
		{#if label && labelPosition === 'right'}
			<span class="switch-label" class:skeleton-text={skeleton} id="{uniqueID}-label">{skeleton ? '' : label}</span>
		{/if}
		{#if helperText || errorText}
			<span class="switch-helper-text" id="{uniqueID}-helper" class:error-text={error}>
				{error && errorText ? errorText : helperText}
			</span>
		{/if}
	</div>
```

- [ ] **Step 2: Add neon CSS**

Append to the `<style>` block in `Switch.svelte`:

```css
/* v3.0.0 – Neon Design */
.switch--neon {
	padding: 0;
	background: transparent;
	width: 3.5em;
	height: 2em;
}

.switch-track--neon {
	position: relative;
	display: block;
	width: 100%;
	height: 100%;
	background-color: #0d0d0d;
	border-radius: 1em;
	border: 1px solid var(--neon-color);
	box-shadow: 0 0 6px var(--neon-color), inset 0 0 6px rgba(0,0,0,0.8);
	transition: box-shadow var(--animation-duration) var(--animation-easing),
	            border-color var(--animation-duration) var(--animation-easing);
}

.switch--neon.checked .switch-track--neon {
	box-shadow: 0 0 12px var(--neon-color), 0 0 24px var(--neon-color), inset 0 0 6px rgba(0,0,0,0.8);
	border-color: var(--neon-color);
}

.switch-thumb--neon {
	position: absolute;
	top: 0.15em;
	left: 0.15em;
	width: 1.7em;
	height: 1.7em;
	background-color: #1a1a1a;
	border-radius: 50%;
	transition: transform var(--animation-duration) var(--animation-easing),
	            box-shadow var(--animation-duration) var(--animation-easing);
	box-shadow: 0 0 4px var(--neon-color), 0 2px 4px rgba(0,0,0,0.6);
	display: flex;
	align-items: center;
	justify-content: center;
}

.switch--neon.checked .switch-thumb--neon {
	transform: translateX(1.5em);
	box-shadow: 0 0 10px var(--neon-color), 0 0 20px var(--neon-color);
	background-color: var(--neon-color);
}
```

- [ ] **Step 3: Verify TypeScript and build**

```bash
npm run check && npm run build
```

Expected: 0 errors.

- [ ] **Step 4: Commit**

```bash
git add src/lib/Switch.svelte
git commit -m "feat: Add neon design variant with cyberpunk glow effects"
```

---

## Task 3: Flip Design Variant

**Files:**
- Modify: `src/lib/Switch.svelte` (template + style block)

**Interfaces:**
- Consumes: `flipFrontContent`, `flipBackContent`, `checked`, `activeColor`, all standard state/props from Task 1
- Produces: `design="flip"` renders a 3D card flip toggle

- [ ] **Step 1: Add flip template block**

Add after the `neon` block (before `{:else if design === 'multi'}`):

```svelte
{:else if design === 'flip'}
	<div
		class="switch-container"
		class:disabled
		class:readonly
		class:dark={isDarkMode}
		class:skeleton
		style="font-size: {fontSize}rem; direction: {isRTL ? 'rtl' : 'ltr'};"
		onmouseenter={handleMouseEnter}
		onmouseleave={handleMouseLeave}
		role="presentation"
	>
		{#if label && labelPosition === 'left'}
			<span class="switch-label" class:skeleton-text={skeleton} id="{uniqueID}-label">{skeleton ? '' : label}</span>
		{/if}
		<div class="switch-wrapper">
			{#if skeleton}
				<div class="switch-skeleton switch-skeleton--slider" class:skeleton-shimmer={skeletonAnimation === 'shimmer'} class:skeleton-pulse={skeletonAnimation === 'pulse'} class:skeleton-wave={skeletonAnimation === 'wave'}></div>
			{:else}
				<button
					type="button"
					role="switch"
					aria-checked={checked}
					aria-label={ariaLabel || label}
					aria-labelledby={label ? `${uniqueID}-label` : undefined}
					aria-describedby={ariaDescribedBy || (helperText || errorText ? `${uniqueID}-helper` : undefined)}
					aria-required={required}
					aria-invalid={error}
					{disabled}
					tabindex={tabIndex}
					class="switch switch--flip"
					class:checked
					class:loading
					class:shadow
					class:error
					class:pulse
					class:pulse-subtle={pulse && pulseIntensity === 'subtle'}
					class:pulse-normal={pulse && pulseIntensity === 'normal'}
					class:pulse-strong={pulse && pulseIntensity === 'strong'}
					class:pending={isPending}
					onclick={handleClick}
					onkeydown={handleKeyDown}
					onfocus={handleFocusEvent}
					onblur={handleBlurEvent}
					onpointerdown={longPress ? startLongPress : undefined}
					onpointerup={longPress ? cancelLongPress : undefined}
					onpointerleave={longPress ? cancelLongPress : undefined}
					style="
						--active-color: {activeColor};
						--off-color: {darkOffColor};
						--animation-duration: {animationDuration}ms;
						--animation-easing: {animationEasing};
						--pulse-color: {pulseColor || activeColor};
						--long-press-progress: {longPressProgress}%;
					"
				>
					<div class="flip-card">
						<div class="flip-card-inner">
							<div class="flip-card-front">
								{#if loading}
									<span class="spinner"></span>
								{:else}
									{flipFrontContent}
								{/if}
							</div>
							<div class="flip-card-back">
								{#if loading}
									<span class="spinner"></span>
								{:else}
									{flipBackContent}
								{/if}
							</div>
						</div>
					</div>
					{#if longPress && longPressProgress > 0}
						<svg class="long-press-ring long-press-ring--flip" viewBox="0 0 36 36">
							<circle class="long-press-ring-track" cx="18" cy="18" r="16" />
							<circle class="long-press-ring-fill" cx="18" cy="18" r="16"
								stroke-dasharray="100.5"
								stroke-dashoffset="{100.5 - (longPressProgress / 100) * 100.5}" />
						</svg>
					{/if}
				</button>
			{/if}
			{#if tooltip && isTooltipVisible}
				<div class="switch-tooltip" class:tooltip-top={tooltipPosition === 'top'} class:tooltip-bottom={tooltipPosition === 'bottom'} class:tooltip-left={tooltipPosition === 'left'} class:tooltip-right={tooltipPosition === 'right'}>
					{tooltip}
				</div>
			{/if}
			{#if showConfirmPrompt}
				<div class="confirm-prompt">
					<span class="confirm-message">{confirmMessage}</span>
					<button class="confirm-btn confirm-btn--yes" onclick={confirmYes}>Yes</button>
					<button class="confirm-btn confirm-btn--no" onclick={confirmNo}>No</button>
				</div>
			{/if}
		</div>
		{#if label && labelPosition === 'right'}
			<span class="switch-label" class:skeleton-text={skeleton} id="{uniqueID}-label">{skeleton ? '' : label}</span>
		{/if}
		{#if helperText || errorText}
			<span class="switch-helper-text" id="{uniqueID}-helper" class:error-text={error}>
				{error && errorText ? errorText : helperText}
			</span>
		{/if}
	</div>
```

- [ ] **Step 2: Add flip CSS**

Append to the `<style>` block:

```css
/* v3.0.0 – Flip Design */
.switch--flip {
	padding: 0;
	background: transparent;
	width: 4em;
	height: 2.4em;
	perspective: 600px;
}

.flip-card {
	width: 100%;
	height: 100%;
}

.flip-card-inner {
	position: relative;
	width: 100%;
	height: 100%;
	transform-style: preserve-3d;
	transition: transform var(--animation-duration) var(--animation-easing);
	border-radius: 0.4em;
}

.switch--flip.checked .flip-card-inner {
	transform: rotateY(180deg);
}

.flip-card-front,
.flip-card-back {
	position: absolute;
	width: 100%;
	height: 100%;
	backface-visibility: hidden;
	-webkit-backface-visibility: hidden;
	border-radius: 0.4em;
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 600;
	font-size: 0.875em;
	user-select: none;
	box-shadow: 0 2px 6px rgba(0,0,0,0.15);
}

.flip-card-front {
	background-color: var(--off-color);
	color: #6B7280;
}

.flip-card-back {
	background-color: var(--active-color);
	color: white;
	transform: rotateY(180deg);
}
```

- [ ] **Step 3: Verify TypeScript and build**

```bash
npm run check && npm run build
```

Expected: 0 errors.

- [ ] **Step 4: Commit**

```bash
git add src/lib/Switch.svelte
git commit -m "feat: Add flip design variant with 3D card flip animation"
```

---

## Task 4: Pill Design Variant

**Files:**
- Modify: `src/lib/Switch.svelte` (template + style block)

**Interfaces:**
- Consumes: `onText`, `offText`, `checked`, `activeColor`, all standard state/props from Task 1
- Produces: `design="pill"` renders two connected pill buttons for binary ON/OFF

- [ ] **Step 1: Add pill template block**

Add after the `flip` block (before `{:else if design === 'multi'}`):

```svelte
{:else if design === 'pill'}
	<div
		class="switch-container"
		class:disabled
		class:readonly
		class:dark={isDarkMode}
		class:skeleton
		style="font-size: {fontSize}rem; direction: {isRTL ? 'rtl' : 'ltr'};"
		onmouseenter={handleMouseEnter}
		onmouseleave={handleMouseLeave}
		role="presentation"
	>
		{#if label && labelPosition === 'left'}
			<span class="switch-label" class:skeleton-text={skeleton} id="{uniqueID}-label">{skeleton ? '' : label}</span>
		{/if}
		<div class="switch-wrapper">
			{#if skeleton}
				<div class="switch-skeleton switch-skeleton--multi" class:skeleton-shimmer={skeletonAnimation === 'shimmer'} class:skeleton-pulse={skeletonAnimation === 'pulse'} class:skeleton-wave={skeletonAnimation === 'wave'}></div>
			{:else}
				<div
					class="switch-pill"
					class:shadow
					class:outline
					class:gradient
					class:pulse
					class:pulse-subtle={pulse && pulseIntensity === 'subtle'}
					class:pulse-normal={pulse && pulseIntensity === 'normal'}
					class:pulse-strong={pulse && pulseIntensity === 'strong'}
					class:pending={isPending}
					role="group"
					aria-label={ariaLabel || label}
					style="
						--active-color: {activeColor};
						--off-color: {darkOffColor};
						--animation-duration: {animationDuration}ms;
						--animation-easing: {animationEasing};
						--pulse-color: {pulseColor || activeColor};
					"
				>
					<button
						type="button"
						class="pill-btn pill-btn--off"
						class:pill-active={!checked}
						{disabled}
						tabindex={!checked ? 0 : tabIndex}
						onclick={() => {
							if (!disabled && !loading && !readonly && !skeleton && checked) {
								if (confirmToggle) { handleConfirmToggle(); } else { commitToggle(); }
							}
						}}
						onkeydown={(e) => { if (e.key === 'ArrowRight' || e.key === 'ArrowDown') { e.preventDefault(); if (checked) commitToggle(); } }}
					>
						{#if loading && !checked}
							<span class="spinner"></span>
						{:else}
							{offText}
						{/if}
					</button>
					<button
						type="button"
						class="pill-btn pill-btn--on"
						class:pill-active={checked}
						{disabled}
						tabindex={checked ? 0 : tabIndex}
						onclick={() => {
							if (!disabled && !loading && !readonly && !skeleton && !checked) {
								if (confirmToggle) { handleConfirmToggle(); } else { commitToggle(); }
							}
						}}
						onkeydown={(e) => { if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') { e.preventDefault(); if (!checked) commitToggle(); } }}
					>
						{#if loading && checked}
							<span class="spinner"></span>
						{:else}
							{onText}
						{/if}
					</button>
				</div>
			{/if}
			{#if tooltip && isTooltipVisible}
				<div class="switch-tooltip" class:tooltip-top={tooltipPosition === 'top'} class:tooltip-bottom={tooltipPosition === 'bottom'} class:tooltip-left={tooltipPosition === 'left'} class:tooltip-right={tooltipPosition === 'right'}>
					{tooltip}
				</div>
			{/if}
			{#if showConfirmPrompt}
				<div class="confirm-prompt">
					<span class="confirm-message">{confirmMessage}</span>
					<button class="confirm-btn confirm-btn--yes" onclick={confirmYes}>Yes</button>
					<button class="confirm-btn confirm-btn--no" onclick={confirmNo}>No</button>
				</div>
			{/if}
		</div>
		{#if label && labelPosition === 'right'}
			<span class="switch-label" class:skeleton-text={skeleton} id="{uniqueID}-label">{skeleton ? '' : label}</span>
		{/if}
		{#if helperText || errorText}
			<span class="switch-helper-text" id="{uniqueID}-helper" class:error-text={error}>
				{error && errorText ? errorText : helperText}
			</span>
		{/if}
	</div>
```

- [ ] **Step 2: Add pill CSS**

Append to the `<style>` block:

```css
/* v3.0.0 – Pill Design */
.switch-pill {
	display: inline-flex;
	background-color: var(--off-color);
	border-radius: 2em;
	padding: 0.2em;
	gap: 0;
}

.switch-pill.shadow {
	box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.switch-pill.outline {
	border: 1px solid #D1D5DB;
}

.pill-btn {
	padding: 0.4em 1em;
	border: none;
	background: transparent;
	border-radius: 2em;
	cursor: pointer;
	font-size: 0.875em;
	font-weight: 500;
	color: #9CA3AF;
	transition: all var(--animation-duration) var(--animation-easing);
	font-family: inherit;
	user-select: none;
}

.pill-btn:disabled {
	cursor: not-allowed;
}

.pill-btn:focus-visible {
	outline: 2px solid var(--active-color);
	outline-offset: 2px;
}

.pill-btn.pill-active {
	background-color: var(--active-color);
	color: white;
	box-shadow: 0 1px 3px rgba(0,0,0,0.15);
}

.switch-pill.gradient .pill-btn.pill-active {
	background: var(--active-color);
}
```

- [ ] **Step 3: Add shared v3.0.0 CSS (interaction states, confirm prompt, long-press ring)**

Append to the `<style>` block:

```css
/* v3.0.0 – Pending state (confirmToggle) */
.switch.pending .switch-track,
.switch.pending .switch-track-material {
	background-color: #F59E0B !important;
}

.switch.pending {
	animation: pending-pulse 1s infinite;
}

@keyframes pending-pulse {
	0%, 100% { opacity: 1; }
	50% { opacity: 0.7; }
}

/* v3.0.0 – Confirm prompt */
.confirm-prompt {
	position: absolute;
	left: calc(100% + 8px);
	top: 50%;
	transform: translateY(-50%);
	display: flex;
	align-items: center;
	gap: 0.4em;
	background: white;
	border: 1px solid #E5E7EB;
	border-radius: 0.5em;
	padding: 0.3em 0.5em;
	box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
	white-space: nowrap;
	z-index: 1001;
	animation: tooltip-fade-in 0.15s ease-out;
}

.confirm-message {
	font-size: 0.75em;
	color: #374151;
}

.confirm-btn {
	border: none;
	border-radius: 0.25em;
	padding: 0.2em 0.5em;
	font-size: 0.7em;
	font-weight: 600;
	cursor: pointer;
	font-family: inherit;
}

.confirm-btn--yes {
	background-color: #10B981;
	color: white;
}

.confirm-btn--no {
	background-color: #E5E7EB;
	color: #374151;
}

/* v3.0.0 – Long-press progress ring */
.long-press-ring {
	position: absolute;
	top: -0.15em;
	left: -0.15em;
	width: calc(100% + 0.3em);
	height: calc(100% + 0.3em);
	pointer-events: none;
}

.long-press-ring--flip {
	top: -0.25em;
	left: -0.25em;
	width: calc(100% + 0.5em);
	height: calc(100% + 0.5em);
}

.long-press-ring-track {
	fill: none;
	stroke: rgba(0,0,0,0.1);
	stroke-width: 2;
}

.long-press-ring-fill {
	fill: none;
	stroke: var(--active-color);
	stroke-width: 2;
	stroke-linecap: round;
	transform: rotate(-90deg);
	transform-origin: 50% 50%;
	transition: stroke-dashoffset 0.05s linear;
}

/* v3.0.0 – dragMomentum thumb offset */
.switch-thumb.drag-active,
.switch-thumb-modern.drag-active,
.switch-thumb-material.drag-active {
	transition: none;
}
```

- [ ] **Step 4: Verify TypeScript and build**

```bash
npm run check && npm run build
```

Expected: 0 errors.

- [ ] **Step 5: Commit**

```bash
git add src/lib/Switch.svelte
git commit -m "feat: Add pill design variant and shared v3.0.0 interaction CSS"
```

---

## Task 5: Wire dragMomentum to Existing Designs

**Files:**
- Modify: `src/lib/Switch.svelte` (template — existing slider/ios, modern, material blocks)

**Interfaces:**
- Consumes: `dragMomentum`, `handleDragStart`, `handleDragMove`, `handleDragEnd`, `dragThumbOffset`, `isDragActive` from Task 1
- Produces: Slider/modern/material thumbs track pointer drag when `dragMomentum=true`

- [ ] **Step 1: Add pointer event handlers to slider block**

In the `design === 'slider' || design === 'ios'` button element, add after `ontouchend`:

```svelte
onpointerdown={dragMomentum ? handleDragStart : undefined}
onpointermove={dragMomentum ? handleDragMove : undefined}
onpointerup={dragMomentum ? handleDragEnd : undefined}
```

Also update the `switch-thumb` span style to include drag offset:

```svelte
<span class="switch-thumb" class:dragging={isDragging} class:drag-active={isDragActive}
  style={isDragActive ? `transform: translateX(${(checked ? 1.5 : 0) + dragThumbOffset / 16}em)` : ''}>
```

- [ ] **Step 2: Add pointer event handlers to modern block**

In the `design === 'modern'` button element, add the same three `onpointerdown/move/up` handlers. Update `switch-thumb-modern`:

```svelte
<span class="switch-thumb-modern" class:dragging={isDragging} class:drag-active={isDragActive}
  style={isDragActive ? `transform: translateX(${(checked ? 1.8 : 0) + dragThumbOffset / 16}em)` : ''}>
```

- [ ] **Step 3: Add pointer event handlers to material block**

In the `design === 'material'` button element, add the same three `onpointerdown/move/up` handlers. Update `switch-thumb-material`:

```svelte
<span class="switch-thumb-material" class:dragging={isDragging} class:drag-active={isDragActive}
  style={isDragActive ? `transform: translateY(-50%) translateX(${(checked ? 2 : 0) + dragThumbOffset / 16}em)` : ''}>
```

- [ ] **Step 4: Also add longPress to slider/modern/material/inner blocks**

In each of the four existing design button elements, add:
```svelte
onpointerdown={!dragMomentum && longPress ? startLongPress : (dragMomentum ? handleDragStart : undefined)}
onpointerup={!dragMomentum && longPress ? cancelLongPress : (dragMomentum ? handleDragEnd : undefined)}
onpointerleave={longPress ? cancelLongPress : undefined}
```

And add the progress ring SVG inside each thumb span (slider, modern, material) and inside the inner button:
```svelte
{#if longPress && longPressProgress > 0}
  <svg class="long-press-ring" viewBox="0 0 36 36">
    <circle class="long-press-ring-track" cx="18" cy="18" r="16" />
    <circle class="long-press-ring-fill" cx="18" cy="18" r="16"
      stroke-dasharray="100.5"
      stroke-dashoffset="{100.5 - (longPressProgress / 100) * 100.5}" />
  </svg>
{/if}
```

And add `confirmToggle` support to each design's wrapper div — add the confirm prompt after the tooltip in every `switch-wrapper`:
```svelte
{#if showConfirmPrompt}
  <div class="confirm-prompt">
    <span class="confirm-message">{confirmMessage}</span>
    <button class="confirm-btn confirm-btn--yes" onclick={confirmYes}>Yes</button>
    <button class="confirm-btn confirm-btn--no" onclick={confirmNo}>No</button>
  </div>
{/if}
```

- [ ] **Step 5: Verify TypeScript and build**

```bash
npm run check && npm run build
```

Expected: 0 errors.

- [ ] **Step 6: Commit**

```bash
git add src/lib/Switch.svelte
git commit -m "feat: Add dragMomentum, longPress, and confirmToggle to all design variants"
```

---

## Task 6: Update Demo Homepage

**Files:**
- Modify: `demo/src/routes/+page.svelte`

- [ ] **Step 1: Update the features array and hero**

In `demo/src/routes/+page.svelte`, update the `features` array to reflect v3.0.0:

```typescript
const features = [
	{ icon: '🎨', title: '8 Design Variants', description: 'Slider/iOS, Inner, Modern, Material, Multi, Neon, Flip, Pill' },
	{ icon: '🌈', title: '9 Color Schemes', description: 'Blue, green, red, purple, orange, pink, yellow, indigo, teal + custom' },
	{ icon: '📏', title: '5 Size Options', description: 'From XS to XL, or custom sizes for perfect fit' },
	{ icon: '🌙', title: 'Dark Mode + Gradients', description: 'Auto dark mode detection, 5 gradient presets' },
	{ icon: '📱', title: 'Touch & Drag', description: 'Swipe-to-toggle, drag momentum, haptic feedback, RTL support' },
	{ icon: '🔒', title: 'Confirm to Toggle', description: 'Async confirmation callback or inline Yes/No prompt' },
	{ icon: '⏳', title: 'Long Press', description: 'Animated progress ring — prevents accidental toggles' },
	{ icon: '♿', title: 'Fully Accessible', description: 'ARIA labels, keyboard navigation, screen reader support' },
	{ icon: '🎯', title: 'TypeScript First', description: 'Full SwitchProps type + Svelte 5 runes throughout' }
];
```

- [ ] **Step 2: Add v3.0.0 showcase section**

Add a new section after the existing feature sections (before the footer/CTA), showcasing the three new design variants. Add this before the closing `</section>` of the last content section:

```svelte
<!-- v3.0.0 New Variants Showcase -->
<section class="bg-gradient-to-br from-gray-900 to-gray-800 py-20">
  <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
    <div class="text-center mb-12">
      <span class="inline-block bg-purple-600 text-white text-xs font-semibold px-3 py-1 rounded-full mb-4">v3.0.0</span>
      <h2 class="text-3xl font-bold text-white mb-4">3 New Design Variants</h2>
      <p class="text-gray-400 max-w-2xl mx-auto">Neon glow, 3D card flip, and binary pill button — all with the same powerful API.</p>
    </div>
    <div class="grid grid-cols-1 md:grid-cols-3 gap-8">
      <div class="bg-gray-800 rounded-2xl p-8 text-center border border-gray-700">
        <h3 class="text-white font-semibold mb-6">Neon</h3>
        <div class="flex justify-center mb-4">
          <Switch design="neon" colorScheme="blue" bind:value={neonDemo} label="" darkMode={true} />
        </div>
        <p class="text-gray-400 text-sm">Cyberpunk glow with color-matched bloom</p>
        <code class="text-purple-400 text-xs">design="neon"</code>
      </div>
      <div class="bg-gray-800 rounded-2xl p-8 text-center border border-gray-700">
        <h3 class="text-white font-semibold mb-6">Flip</h3>
        <div class="flex justify-center mb-4">
          <Switch design="flip" colorScheme="green" bind:value={flipDemo} label="" />
        </div>
        <p class="text-gray-400 text-sm">3D card flip with custom face content</p>
        <code class="text-purple-400 text-xs">design="flip"</code>
      </div>
      <div class="bg-gray-800 rounded-2xl p-8 text-center border border-gray-700">
        <h3 class="text-white font-semibold mb-6">Pill</h3>
        <div class="flex justify-center mb-4">
          <Switch design="pill" colorScheme="purple" bind:value={pillDemo} label="" />
        </div>
        <p class="text-gray-400 text-sm">Binary segmented control with pill buttons</p>
        <code class="text-purple-400 text-xs">design="pill"</code>
      </div>
    </div>
  </div>
</section>
```

Add the three demo state variables to the `<script>` block:
```typescript
let neonDemo = false;
let flipDemo = false;
let pillDemo = false;
```

Add the Switch import if not already present:
```typescript
import Switch from '@switch/Switch.svelte';
```

- [ ] **Step 3: Verify demo builds**

```bash
cd demo && npm run build && cd ..
```

Expected: 0 errors.

- [ ] **Step 4: Commit**

```bash
git add demo/src/routes/+page.svelte
git commit -m "docs: Add v3.0.0 variant showcase and update feature list on homepage"
```

---

## Task 7: Update Playground Metadata

**Files:**
- Modify: `demo/src/lib/data/componentMetadata.ts`

- [ ] **Step 1: Add new design options to `design` prop definition**

In `componentMetadata.ts`, find the `design` prop definition and update `options`:

```typescript
{
  name: 'design',
  type: 'string',
  control: {
    type: 'select',
    options: ['slider', 'ios', 'inner', 'modern', 'material', 'multi', 'neon', 'flip', 'pill']
  },
  description: 'Design variant of the switch',
  defaultValue: 'slider',
  category: 'basic'
},
```

- [ ] **Step 2: Add v3.0.0 props to `propDefinitions`**

Append to the `propDefinitions` array:

```typescript
// v3.0.0 – Flip props
{
  name: 'flipFrontContent',
  type: 'string',
  control: { type: 'text' },
  description: 'Content shown on the front (OFF) face of flip variant',
  defaultValue: 'OFF',
  category: 'variants'
},
{
  name: 'flipBackContent',
  type: 'string',
  control: { type: 'text' },
  description: 'Content shown on the back (ON) face of flip variant',
  defaultValue: 'ON',
  category: 'variants'
},
// v3.0.0 – Interaction props
{
  name: 'dragMomentum',
  type: 'boolean',
  control: { type: 'boolean' },
  description: 'Thumb follows pointer; snaps by velocity on release',
  defaultValue: false,
  category: 'interactions'
},
{
  name: 'longPress',
  type: 'boolean',
  control: { type: 'boolean' },
  description: 'Hold to toggle with animated progress ring',
  defaultValue: false,
  category: 'interactions'
},
{
  name: 'longPressDuration',
  type: 'number',
  control: { type: 'number' },
  description: 'Hold duration in ms before toggle fires',
  defaultValue: 600,
  category: 'interactions'
},
{
  name: 'confirmToggle',
  type: 'boolean',
  control: { type: 'boolean' },
  description: 'Require confirmation before toggling',
  defaultValue: false,
  category: 'interactions'
},
{
  name: 'confirmMessage',
  type: 'string',
  control: { type: 'text' },
  description: 'Message shown in inline confirm prompt',
  defaultValue: 'Are you sure?',
  category: 'interactions'
},
```

- [ ] **Step 3: Update `defaultProps` to include new v3.0.0 props**

Add to `defaultProps`:
```typescript
// v3.0.0
flipFrontContent: 'OFF',
flipBackContent: 'ON',
dragMomentum: false,
longPress: false,
longPressDuration: 600,
confirmToggle: false,
confirmMessage: 'Are you sure?',
```

- [ ] **Step 4: Update description**

Change the `description` field:
```typescript
description: 'A comprehensive, accessible toggle switch component with 8 design variants, Svelte 5 runes, typed callback events, and advanced interaction modes',
```

- [ ] **Step 5: Verify demo builds**

```bash
cd demo && npm run build && cd ..
```

- [ ] **Step 6: Commit**

```bash
git add demo/src/lib/data/componentMetadata.ts
git commit -m "docs: Update playground metadata for v3.0.0 new props and design variants"
```

---

## Task 8: Update Docs, CHANGELOG, README, and Version Bump

**Files:**
- Modify: `demo/src/routes/docs/+page.svelte`
- Modify: `CHANGELOG.md`
- Modify: `README.md`
- Modify: `package.json`
- Modify: `CLAUDE.md`

- [ ] **Step 1: Add v3.0.0 section to docs page**

In `demo/src/routes/docs/+page.svelte`, add a new section for v3.0.0 features. Insert after the existing "Advanced Features" section:

```svelte
<section id="v3-features" class="mb-16">
  <h2 class="text-2xl font-bold text-gray-900 mb-6">v3.0.0 – New Features</h2>

  <h3 class="text-xl font-semibold text-gray-800 mb-4">New Design Variants</h3>
  <div class="bg-gray-50 rounded-xl p-6 mb-6">
    <table class="w-full text-sm">
      <thead><tr><th class="text-left py-2">Value</th><th class="text-left py-2">Description</th><th class="text-left py-2">New Props</th></tr></thead>
      <tbody>
        <tr><td class="py-2 font-mono text-purple-700">neon</td><td class="py-2">Cyberpunk glow with color-matched bloom</td><td class="py-2">—</td></tr>
        <tr><td class="py-2 font-mono text-purple-700">flip</td><td class="py-2">3D card that rotates 180° on toggle</td><td class="py-2"><code>flipFrontContent</code>, <code>flipBackContent</code></td></tr>
        <tr><td class="py-2 font-mono text-purple-700">pill</td><td class="py-2">Binary segmented control</td><td class="py-2">Uses existing <code>onText</code>/<code>offText</code></td></tr>
      </tbody>
    </table>
  </div>

  <h3 class="text-xl font-semibold text-gray-800 mb-4">New Interaction Props</h3>
  <div class="bg-gray-50 rounded-xl p-6 mb-6">
    <table class="w-full text-sm">
      <thead><tr><th class="text-left py-2">Prop</th><th class="text-left py-2">Type</th><th class="text-left py-2">Default</th><th class="text-left py-2">Description</th></tr></thead>
      <tbody>
        <tr><td class="py-2 font-mono text-purple-700">dragMomentum</td><td>boolean</td><td>false</td><td>Thumb follows drag; snaps by velocity</td></tr>
        <tr><td class="py-2 font-mono text-purple-700">longPress</td><td>boolean</td><td>false</td><td>Hold to toggle with progress ring</td></tr>
        <tr><td class="py-2 font-mono text-purple-700">longPressDuration</td><td>number</td><td>600</td><td>Hold time in ms</td></tr>
        <tr><td class="py-2 font-mono text-purple-700">confirmToggle</td><td>boolean</td><td>false</td><td>Requires confirmation before toggling</td></tr>
        <tr><td class="py-2 font-mono text-purple-700">confirmMessage</td><td>string</td><td>'Are you sure?'</td><td>Inline prompt text</td></tr>
        <tr><td class="py-2 font-mono text-purple-700">onconfirm</td><td>() => Promise&lt;boolean&gt;</td><td>—</td><td>Async confirmation callback</td></tr>
      </tbody>
    </table>
  </div>

  <h3 class="text-xl font-semibold text-gray-800 mb-4">Breaking Changes (v2 → v3)</h3>
  <div class="bg-red-50 border border-red-200 rounded-xl p-6 mb-6">
    <p class="text-red-800 font-medium mb-3">Event API changed — update your event handlers:</p>
    <pre class="text-sm"><code>{"<!-- v2.x -->\n<Switch on:change={handler} on:focus={handler} on:blur={handler} />\n\n<!-- v3.0 -->\n<Switch onchange={handler} onfocus={handler} onblur={handler} />"}</code></pre>
    <p class="text-red-700 text-sm mt-3">All prop values remain unchanged. Only the event syntax is different.</p>
  </div>
</section>
```

- [ ] **Step 2: Add v3.0.0 entry to CHANGELOG.md**

Insert at the top of `CHANGELOG.md` (after the header):

```markdown
## [3.0.0] - 2026-06-29

### Breaking Changes
- **Svelte 5 Runes**: Component migrated to `$props()`, `$state()`, `$derived()`, `$effect()` — internal upgrade, no prop API changes
- **Event API**: `on:change`, `on:focus`, `on:blur` replaced with `onchange`, `onfocus`, `onblur` callback props
  - Before: `<Switch on:change={handler} />`
  - After: `<Switch onchange={handler} />`

### Added
- **Neon design variant** (`design="neon"`): Cyberpunk-style glow toggle with color-matched neon bloom, works with all 9 color schemes
- **Flip design variant** (`design="flip"`): 3D card that rotates 180° on Y-axis; `flipFrontContent` and `flipBackContent` props for face content
- **Pill design variant** (`design="pill"`): Binary segmented control with two connected pill buttons; uses existing `onText`/`offText` labels
- **Drag momentum** (`dragMomentum: boolean`): Thumb follows pointer during drag; snaps to ON/OFF based on velocity > 0.3px/ms or distance > 20px
- **Long press** (`longPress: boolean`, `longPressDuration: number`): Animated SVG progress ring around thumb; fires toggle after hold duration, cancels on early release
- **Confirm toggle** (`confirmToggle: boolean`, `confirmMessage: string`, `onconfirm: () => Promise<boolean>`): Pending state with amber pulse while awaiting confirmation; inline Yes/No prompt when no async callback provided
- **TypeScript barrel export**: `src/lib/index.ts` exports `Switch` and `SwitchProps` type interface
- **Neon color map**: Per-scheme saturated neon glow colors for cyberpunk aesthetic

### Changed
- All DOM event handlers in template updated to Svelte 5 syntax (`on:click` → `onclick`, etc.)
- `handleFocus`/`handleBlur` renamed to `handleFocusEvent`/`handleBlurEvent` (internal only)
- `commitToggle()` extracted as shared helper used by all interaction modes

### Technical
- Removed `createEventDispatcher` import — replaced with typed callback props
- `$:` reactive statements replaced with `$derived()` and `$effect()`
- `let` mutable state replaced with `$state()`
- Added `neonColorSchemes` color map for neon design
- Added `longPressProgress`, `isPending`, `showConfirmPrompt`, `dragThumbOffset` internal state
- Pointer events used for drag momentum (supports both mouse and touch via unified API)
```

- [ ] **Step 3: Update README.md**

Update the version badge line near the top:
```markdown
[![npm version](https://badge.fury.io/js/svelte-toggle-switch.svg)](https://www.npmjs.com/package/svelte-toggle-switch)
**v3.0.0** — 8 design variants · Svelte 5 runes · typed callback events
```

Update the Design Variants section to include the three new ones:
```markdown
## Design Variants

| Value | Description |
|-------|-------------|
| `slider` / `ios` | iOS-style slider (default) |
| `inner` | Button with ON/OFF text |
| `modern` | Enhanced slider with track icons |
| `material` | Google Material Design style |
| `multi` | N-option segmented control |
| `neon` ✨ | Cyberpunk glow with color bloom (v3.0.0) |
| `flip` ✨ | 3D card that rotates 180° on toggle (v3.0.0) |
| `pill` ✨ | Binary segmented pill control (v3.0.0) |
```

Add migration guide section:
```markdown
## Migrating from v2

Only one breaking change: **event syntax**. Update all event handlers:

```svelte
<!-- v2.x -->
<Switch on:change={handler} on:focus={onFocus} on:blur={onBlur} />

<!-- v3.0 -->
<Switch onchange={handler} onfocus={onFocus} onblur={onBlur} />
```

All prop values, names, and defaults are unchanged.
```

- [ ] **Step 4: Bump version in package.json**

Update line 4 in `package.json`:
```json
"version": "3.0.0",
```

- [ ] **Step 5: Update CLAUDE.md version references**

In `CLAUDE.md`, update:
- Section heading `### Version 2.3.0 Features` → `### Version 3.0.0 Features`
- Add under the features heading:
  - `- **Neon Design**: Cyberpunk glow toggle with color-matched neon bloom`
  - `- **Flip Design**: 3D card flip with customizable face content (flipFrontContent/flipBackContent)`
  - `- **Pill Design**: Binary segmented control using onText/offText labels`
  - `- **Drag Momentum**: Thumb tracks pointer, snaps by velocity on release`
  - `- **Long Press**: SVG progress ring, fires after longPressDuration ms`
  - `- **Confirm Toggle**: Async onconfirm callback or inline Yes/No prompt`
  - `- **Svelte 5 Runes**: Fully migrated from Options API; onchange/onfocus/onblur callback props`
- Update `Package Configuration` section: `Version 2.0.0` → `Version 3.0.0`

- [ ] **Step 6: Verify everything**

```bash
npm run check && npm run build
```

Expected: 0 errors.

- [ ] **Step 7: Commit all documentation and version bump**

```bash
git add demo/src/routes/docs/+page.svelte CHANGELOG.md README.md package.json CLAUDE.md
git commit -m "chore: Bump version to 3.0.0 and update docs, changelog, README"
```

---

## Self-Review

**Spec coverage check:**
- ✅ Svelte 5 runes migration — Task 1
- ✅ TypeScript barrel export with SwitchProps — Task 1
- ✅ Event API breaking change (onchange/onfocus/onblur) — Task 1
- ✅ Neon design variant — Task 2
- ✅ Flip design variant — Task 3
- ✅ Pill design variant — Task 4
- ✅ dragMomentum interaction — Tasks 1 + 5
- ✅ longPress interaction — Tasks 1 + 5
- ✅ confirmToggle interaction — Tasks 1 + 5
- ✅ Demo homepage v3.0.0 section — Task 6
- ✅ Playground metadata — Task 7
- ✅ Docs page update — Task 8
- ✅ CHANGELOG — Task 8
- ✅ README — Task 8
- ✅ Version bump — Task 8
- ✅ CLAUDE.md update — Task 8

**Type consistency check:**
- `onchange` used consistently (Task 1 defines it, all usages call `onchange?.({...})`)
- `handleFocusEvent`/`handleBlurEvent` used in all template blocks (renamed from v2's `handleFocus`/`handleBlur`)
- `commitToggle()` is the single place that mutates `checked` + `value` + fires `onchange`
- `neonColor` ($derived) used in neon template `--neon-color` CSS var
- `longPressProgress`, `isPending`, `showConfirmPrompt` state vars defined in Task 1 and consumed in Tasks 2-5
- `dragThumbOffset`, `isDragActive` defined in Task 1, used in Task 5

**No placeholders found.**
