Keyboard
No component-specific keymap. Inherits standard browser focus / activation behaviour.
/ button
@kumiki/atelier/button Layer 5 preview — styled Button (Tailwind v4 + vanilla CSS variants). Owns the data-variant / data-size styling vocabulary that Layer 4 no longer ships.
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 { createButton } from '@kumiki/headless/button';
let busy = $state(false);
const ctl = createButton({ loading: false, disabled: false });
$effect(() => ctl.setLoading(busy));
async function save() {
busy = true;
await fetch('/save', { method: 'POST' });
busy = false;
}
</script>
<button
type="button"
class="my-button"
onclick={save}
{@attach ctl.root}
>
Save
</button>// You want full control over markup (e.g. an <a> styled like a button,
// a custom wrapper, or your own design system's button shell) but still
// want Kumiki to wire ARIA, click/Enter/Space gating, and aria-busy.
//
// At Layer 3 you mount the controller on a node you own — no <Button.Root>
// wrapper, no fixed markup, no styles.Compound components (<Root> / <Trigger> / …). Markup is fixed; styling is not. Same trade-off as a typical headless UI library.
pnpm add @kumiki/components<script lang="ts">
import { Button } from '@kumiki/components/button';
let busy = $state(false);
async function save() {
busy = true;
await fetch('/save', { method: 'POST' });
busy = false;
}
</script>
<Button.Root loading={busy} onclick={save}>
Save
</Button.Root>
<!--
Variants are not part of the headless contract.
Drive them via your own `class` / `data-*`, or use `@kumiki/atelier/button`:
-->
<Button.Root data-variant="primary" loading={busy} onclick={save}>
Save
</Button.Root><Button.Root aria-label="Add item">
{#snippet icon()}<PlusIcon />{/snippet}
</Button.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 Button } from '@kumiki/atelier/button';
let busy = $state(false);
</script>
<Button.Root variant="primary" size="md" loading={busy} onclick={() => (busy = !busy)}>
Save
</Button.Root>
<Button.Root variant="danger">Delete</Button.Root>
<Button.Root variant="ghost" size="sm">Cancel</Button.Root><script lang="ts">
import { Vanilla as Button } from '@kumiki/atelier/button';
</script>
<Button.Root variant="primary">Save</Button.Root>
<!-- Override CSS custom properties via global stylesheet:
.kumiki-button { --kumiki-button-bg: hsl(170 60% 40%); } -->pnpm kumiki add button
# Drops a vanilla-CSS or Tailwind button preset into your repo,
# under `src/lib/components/button` (path is configurable).
# Edit the variants/sizes to match your design system./ accessibility
axe-core — run on every PR (LTR + RTL × every documented state).
No component-specific keymap. Inherits standard browser focus / activation behaviour.