Skip to content

feat(react): add the Checkbox primitive#11

Open
ivanbanov wants to merge 2 commits into
mainfrom
feat/checkbox
Open

feat(react): add the Checkbox primitive#11
ivanbanov wants to merge 2 commits into
mainfrom
feat/checkbox

Conversation

@ivanbanov

Copy link
Copy Markdown
Member

Summary

Adds the Checkbox primitive as two packages:

  • @dunky.dev/checkbox — the framework-free core: a tri-state state machine (checked / unchecked / indeterminate) following the WAI-ARIA APG Checkbox pattern, built on @dunky.dev/state-machine.
  • @dunky.dev/react-checkbox — the thin React binding exposing the compound Checkbox component.

checked / defaultChecked accept boolean | 'indeterminate', so a bulk-selection parent is driven by a single controlled value. Value changes report through onCheckedChange, but re-syncing the controlled checked prop is never echoed back.

Anatomy

<Checkbox>          - root; owns the checked state, renders nothing of its own
  |_ <Control>      - the interactive element carrying the checkbox role
  |_ <Indicator>    - the visual mark; exists only while checked or indeterminate
  |_ <Label>        - names the control; pressing it toggles too

Behavior

  • Toggle: press (pointer or Space) toggles; indeterminate always resolves to checked — the user can never reach indeterminate by interaction, only the consumer sets it.
  • Keyboard: Space toggles; Enter is suppressed by the core so a button-backed control does not activate on Enter, per APG.
  • ARIA: the control carries role="checkbox" with aria-checked (true / false / mixed), and is named by the Label via aria-labelledby minted from the root id.
  • Disabled: conveyed as aria-disabled (control stays focusable and discoverable); the machine blocks user intent while programmatic and controlled updates still apply. Every part carries data-state and data-disabled styling hooks.
  • Label: pressing the label toggles, matching the native label affordance, wired through the machine (not native <label for>) so the disabled guard holds everywhere.

Test plan

  • pnpm test:ci — 114 tests, 9 files, all passing
  • pnpm typecheck
  • pnpm lint
  • pnpm format:check
  • pnpm build — publint clean for both new packages

🤖 Generated with Claude Code

ivanbanov and others added 2 commits July 18, 2026 11:00
Ships @dunky.dev/checkbox (core machine) and @dunky.dev/react-checkbox
(React binding). Anatomy: Checkbox root with Control, Indicator, and
Label parts. A tri-state (checked / unchecked / indeterminate) APG
checkbox: press or Space toggles, indeterminate resolves to checked,
disabled gates the user but not the consumer, and changes report via
onCheckedChange without echoing controlled writes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ivanbanov ivanbanov changed the title feat: add the Checkbox primitive feat(react): add the Checkbox primitive Jul 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant