Skip to main content
ReactZero · 2026engineering

3 accessible React primitives shipped to npm: combobox (ARIA 1.2), datepicker (WCAG 2.1 AA), animation orchestration. All under 8KB.

Open SourceDesign SystemAccessibilityTypeScript

The problem

Bloated, inaccessible, inflexible

I got tired of bloated React libraries, so I built three tiny ones. That is not a subtweet — it is a real frustration that turned into three published npm packages.

Three specific pains drove this:

  • Animation — most libraries tie animation to React state, so every frame triggers a re-render. Framer Motion is powerful but heavy. CSS transitions cannot orchestrate sequences. The Web Animations API exists but has no React layer.
  • Date pickers — popular choices (react-datepicker, MUI) drag in date-fns or moment.js for functionality the native Intl API already provides. Most are not accessible out of the box.
  • Comboboxes — Downshift is old API patterns, Radix is opinionated styling, Floating UI is another dependency. Very few libraries implement ARIA 1.2; most are still on 1.1.

ReactZero emerged from a single philosophy: zero dependencies, accessibility-first, headless + styled, small bundles. Three libraries, all under 8 kB, all on npm.

Design philosophy — the zero principles

Zero runtime dependencies

Native browser APIs only — Web Animations API, Intl, DOM positioning. No date-fns, Floating UI, or lodash.

Accessibility is default

WCAG baked into the architecture, not bolted on. Keyboard nav, ARIA roles, focus management, live regions.

Headless + styled dual API

Use the headless hook for full control, or the pre-built component for quick wins. Developers pick their abstraction.

CSS custom properties for theming

No CSS-in-JS runtime. Pure CSS variables cascading naturally, overridable at any scope.

Tree-shakeable entry points

Import only what you use. Combo ships 7 separate entry points. Dead code elimination built in.

AI-readable

Each library ships an `ai-reference.md` so AI coding assistants can work with the components accurately.

@reactzero/combo — the accessible combobox

Comboboxes are one of the most complex ARIA patterns — text input, listbox, keyboard nav, focus management, and live region announcements all in one component. ARIA 1.2 introduced significant changes most libraries still have not implemented.

Architecture: state machine + prop getters

The core of combo is a pure reducer in core/stateMachine.ts — no side effects, deterministic transitions, testable in isolation. On top of that, useCombo returns prop getters (getInputProps, getMenuProps, getItemProps) that spread ARIA attributes automatically. Developers cannot forget aria-expanded — it is physically impossible to drop it.

Accessibility deep-dive

  • ARIA 1.2: correct role="combobox" on the input, role="listbox" on the menu, automatic aria-expanded, aria-activedescendant, aria-selected, aria-disabled
  • Live region: announces selection changes and filtered result counts to screen readers
  • 12+ keybindings: ArrowDown/Up, Enter, Escape, Home/End, PageDown/PageUp, Tab, Backspace for chip removal, Space for the select variant
  • Focus management: focus returns to the input on menu close; active descendant highlighting without moving DOM focus
  • High-contrast theme: passes WCAG AAA contrast, not just AA

Theming system

Combo exposes 40+ CSS custom properties organized by scope — input, popover, items, labels, errors, icons, chips, checkboxes, radios, groups, footer, tabs, custom items. Three built-in themes (default, dark, high-contrast) plus theme="system" for OS dark-mode auto-detection. The CSS variables are the design tokens flowing from theme → component → element.

@reactzero/datepicker — the design system showcase

The datepicker is a mini design system: 7 components (DatePicker, TimePicker, DateRangePicker, DateTimePicker, CalendarGrid, FieldWrapper, TimeInput) and 4 headless hooks. Components build on each other, share tokens, and expose both styled and headless APIs.

Tokens and theming in practice

  • 10 built-in themes: light, dark, minimal, ocean, rose, purple, amber, slate, glass, and high-contrast
  • 3 density modes: compact, comfortable, default
  • 5 trigger styles: default, icon, minimal, pill, ghost
  • CSS custom properties: --dp-accent, --dp-bg, --dp-text, --dp-cell-size

This is the most tangible design tokens in practice artifact in the portfolio.

Internationalization via native APIs

The datepicker uses Intl.DateTimeFormat and Date — no date-fns, no moment.js. Locale-aware formatting, RTL support, configurable first day of week. Why add 30 kB of date library when the browser already speaks every language?

Developer experience pipeline

  • Storybook: full component documentation with interactive controls
  • Chromatic: visual regression testing — catches UI drift across themes and states
  • size-limit: budget enforcement (10 kB JS, 5 kB CSS) — prevents accidental bloat

@reactzero/flow — motion as a system

Flow is an animation library with a specific constraint: zero React re-renders. All animations run on the browser's compositor thread via the Web Animations API. The React layer is a thin hook surface that never touches state during playback.

Composition API

  • Primitives: animate(), delay(), sequence(), parallel(), stagger(), timeline()
  • Operators: race(), repeat(), timeout() — composition over configuration
  • True cancellation: finished promises always resolve, never reject. No try/catch needed. Every animation is interruptible.

Reduced motion as first-class

Most animation libraries give you a boolean toggle for reduced motion. Flow gives you four policies via <ReducedMotionProvider>:

  • skip — instant jump to end state
  • reduce — faster, simpler version of the animation
  • crossfade — opacity-only transitions
  • respect — honor the OS prefers-reduced-motion setting

Accessibility is not just about screen readers. Motion sensitivity affects real users — they deserve more than an on/off switch.

Adaptive performance

enableAdaptivePerformance() does runtime device tier detection and FPS monitoring. Animations are tagged by priority — critical, normal, decorative — and under frame pressure the system degrades gracefully: decorative animations skip entirely, normal animations speed up, critical animations stay at full fidelity. No other animation library I know of does runtime adaptive degradation.

Engineering decisions — why zero dependencies?

  1. Why zero dependencies?

    Smaller installs, no transitive vulnerability surface, no dependency conflicts, no abandoned upstream packages breaking my library.

  2. Why Web Animations API over Framer Motion?

    Compositor thread = no React re-renders = no jank. WAAPI is a web standard, not a library API that can break.

  3. Why build positioning instead of using Floating UI?

    One less dependency. Flip and shift logic is simple — it does not need a 15 kB library.

  4. Why a state machine for combo?

    Complex interaction patterns are error-prone with ad-hoc state. A state machine makes transitions deterministic and testable.

  5. Why ARIA 1.2, not 1.1?

    ARIA 1.2 changed the combobox pattern significantly. Implementing the old pattern means shipping incorrect accessibility. Better to be correct from the start.

  6. Why `ai-reference.md`?

    AI coding assistants are part of the workflow now. Making a library machine-readable is an adoption accelerator — AI-native developer experience.

Links and artifacts

Bundle sizes: combo 4.8–6.7 kB · datepicker 6–8 kB · flow ~8 kB

@reactzero/combo

npm — @reactzero/comboGitHubLive demo

@reactzero/datepicker

npm — @reactzero/datepickerGitHubLive demoStorybook (Chromatic)

@reactzero/flow

npm — @reactzero/flowGitHubLive demo
Other work

Explore more case studies