GitHub

Headless · Svelte 5 · Layered

Pieces that fit with millimetric precision.

Kumiki — 組木 — is a deeply accessible UI primitive system for Svelte 5. Five composable layers, twenty components, ten locales. No CSS opinions, no runtime decisions you can’t re-make.

pnpm add @kumiki/components

/ 02 — Architecture

Five layers, one mental model.

Each component lives at exactly one layer. Pick the layer that matches your control needs — and ship the bytes for that layer only.

L0

Types

Shared TypeScript surface — every layer above speaks this contract.

L1

Primitives

Framework-agnostic helpers — focus trap, dismissable, IDs, locale.

L2

Machines

Pure-TS finite state machines. Inspectable JSON. ~1 KB runtime.

L3

Attachments

Svelte 5 {@attach} factories. ARIA + data-state on real DOM.

L4

Components

Compound primitives. <Toggle.Root>, <Combobox.Input>, the lot.

L5

Atelier

Layer 5 preview — copy-pasteable styled variants. Ships at 0.x preview.

/ 03 — Discipline

Built for the long version of accessibility.

1.5–4.5 KB

Bundle budgets in CI

Every component subpath has a brotli budget. Going over fails CI. Toggle ships in 1.5 KB; Combobox in 4.5 KB.

WCAG 2.2 AA

Real screen-reader nightlies

macOS-VoiceOver and Windows-NVDA via Guidepup, on a schedule. Axe + APG keyboard tests on every PR.

10 locales

Locales as subpath imports

No mega-bundle. @kumiki/locale/<lang> at ≤ 1 KB each. RTL inversion lives in the machines.

toJSON()

XState-compatible JSON

Each machine.toJSON() exports a Stately-viewable config. Inspect any FSM in stately.ai/viz.

/ 04 — In code

Three lines, fully accessible.

<script lang="ts">
  import Toggle from '@kumiki/components/toggle';
  let pressed = $state(false);
</script>

<Toggle.Root bind:pressed aria-label="Mute">
  {pressed ? 'Muted' : 'On'}
</Toggle.Root>
  • APG-aligned aria-pressed & data-state.
  • Keyboard: Space, Enter, Disabled.
  • SSR-safe; dehydrate / hydrate with no flicker.
  • 1.5 KB brotli, enforced by CI.
  • Localised to 10 languages out of the box.