Keyboard
| Key | Effect |
|---|---|
| Space, Enter | Toggles pressed state. |
| Tab | Move focus into / out. |
Pure state machines (FSMs). No DOM. Run in server, Vitest, Worker. Reach for these when you only want the behavior.
aria-pressed two-state toggle.
pnpm add @kumiki/machinesimport { createToggleMachine } from '@kumiki/machines/toggle';
const m = createToggleMachine({ initial: false });
console.log(m.state); // 'unpressed'
m.send({ type: 'TOGGLE' });
console.log(m.state); // 'pressed'
console.log(m.context.toggles); // 1import { createToggleMachine } from '@kumiki/machines/toggle';
// Drop the JSON into https://stately.ai/viz to see the statechart.
console.log(JSON.stringify(createToggleMachine().toJSON(), null, 2));Svelte 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 { createToggle } from '@kumiki/headless/toggle';
const t = createToggle({ initial: false });
</script>
<button {@attach t.root}>
{t.pressed ? 'On' : 'Off'}
</button><script lang="ts">
import { createToggle } from '@kumiki/headless/toggle';
const t = createToggle({ initial: false });
let pressed = $state(t.pressed);
t.subscribe(({ context }) => (pressed = context.pressed));
</script>
<button {@attach t.root}>{pressed ? '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/toggle<script lang="ts">
import { Toggle } from '@kumiki/components/toggle';
let pressed = $state(false);
</script>
<Toggle.Root bind:pressed>
{pressed ? 'On' : 'Off'}
</Toggle.Root><script lang="ts">
import { Toggle } from '@kumiki/components/toggle';
</script>
<Toggle.Root onPressedChange={(p) => console.log('changed:', p)}>
Bold
</Toggle.Root><Toggle.Root>
{#snippet child({ props, state })}
<button {...props} class="my-styled-toggle" data-state={state.pressed ? 'on' : 'off'}>
{state.pressed ? '🌙' : '☀️'}
</button>
{/snippet}
</Toggle.Root><Toggle.Root disabled>Off</Toggle.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 Toggle } from '@kumiki/atelier/toggle';
let bold = $state(false);
</script>
<Toggle.Root bind:pressed={bold} variant="outline" size="md">
Bold
</Toggle.Root><script lang="ts">
import { Vanilla as Toggle } from '@kumiki/atelier/toggle';
</script>
<Toggle.Root size="lg">Italic</Toggle.Root>
<!-- Theme via CSS custom properties:
.kumiki-toggle { --kumiki-toggle-bg-on: oklch(0.85 0.15 252); } -->/ accessibility
axe-core — run on every PR (LTR + RTL × every documented state).
| Key | Effect |
|---|---|
| Space, Enter | Toggles pressed state. |
| Tab | Move focus into / out. |