Keyboard
| Key | Effect |
|---|---|
| Space, Enter | Toggles checked state. |
| Tab | Move focus. |
Pure state machines (FSMs). No DOM. Run in server, Vitest, Worker. Reach for these when you only want the behavior.
On/off switch (aria-checked).
pnpm add @kumiki/machinesimport { createSwitchMachine } from '@kumiki/machines/switch';
const m = createSwitchMachine({ initial: false });
console.log(m.state); // 'off'
m.send({ type: 'TOGGLE' });
console.log(m.state); // 'on'
console.log(m.context.checked); // trueSvelte 5 {@attach} factories. Glue ARIA / keyboard / focus onto any DOM node you choose. **Best when you want full control over markup and styling.**
pnpm add @kumiki/headless<script lang="ts">
import { createSwitch } from '@kumiki/headless/switch';
const s = createSwitch({ initial: false });
</script>
<button {@attach s.root}>{s.checked ? 'On' : 'Off'}</button>Compound components (<Root> / <Trigger> / …). Markup is fixed; styling is not. Same trade-off as a typical headless UI library.
pnpm add @kumiki/components/switch<script lang="ts">
import { Switch } from '@kumiki/components/switch';
let checked = $state(false);
</script>
<Switch.Root bind:checked>
{checked ? 'On' : 'Off'}
</Switch.Root><Switch.Root
onCheckedChange={(c) =>
fetch('/api/dark-mode', {
method: 'POST',
body: JSON.stringify({ on: c }),
})}>
Dark mode
</Switch.Root><Switch.Root disabled>Off</Switch.Root>Styled, copy-paste presets (preview). Run pnpm kumiki add to drop the source into your project, then edit freely.
Live preview…
pnpm add @kumiki/atelier<script lang="ts">
import { Tailwind as Switch } from '@kumiki/atelier/switch';
let notify = $state<boolean | undefined>(true);
</script>
<label>
<Switch.Root bind:checked={notify} aria-label="Notifications" />
<span>Email notifications</span>
</label>/ accessibility
axe-core — run on every PR (LTR + RTL × every documented state).
| Key | Effect |
|---|---|
| Space, Enter | Toggles checked state. |
| Tab | Move focus. |