Skip to main content

gentleduck/primitives

Unstyled, accessible React primitives for building serious component libraries and product-grade interaction systems.

What this package is

gentleduck/primitives is a set of low-level React building blocks for teams that need:

  • Correct keyboard and screen reader behavior.
  • Flexible composition (asChild, slotting, scoped contexts).
  • Controlled and uncontrolled state strategies.
  • Animation-aware mount/unmount semantics.
  • A clear path from raw primitive to polished design-system component.

This package is not a CSS framework. It is an interaction and accessibility layer.


When to use primitives

Use primitives when you are building:

  • A reusable design system across many apps.
  • Product surfaces with complex overlays (dialogs, popovers, menus).
  • UI with strict accessibility requirements.
  • Highly customized visuals where pre-styled libraries get in the way.

Do not use primitives if you only need quick, fixed styling with minimal customization. In that case, use pre-styled components instead.


Capability model

LayerResponsibilityTypical primitives
FoundationRendering abstraction and compositionPrimitive Elements, Slot, Direction
Interaction infraFocus, dismissal, mounting, positioningFocus Scope, Dismissable Layer, Presence, Portal, Popper
Product patternsOverlays, menus, inputs, selectionDialog, Popover, Menu, Select, Slider

This layering is the core reason primitives scale well in large codebases.


How teams usually adopt

  1. Start with one high-value wrapper (Dialog, Popover, Menu).
  2. Standardize wrapper rules: forwardRef, className merge, sensible defaults, no hidden primitive props.
  3. Add design tokens and motion classes via data-state.
  4. Document wrapper contracts in your design-system package.
  5. Expand to additional primitives after keyboard + screen reader tests pass.

Architecture snapshot

Loading diagram...

Higher-level components are built by composition, not inheritance. You can always drop one level lower when you need custom behavior.


Production checklist

Before shipping primitive-based wrappers:

  • Verify title/description semantics for dialogs.
  • Verify keyboard loops and escape behavior.
  • Verify focus restoration after close.
  • Verify click-outside handling for nested overlays.
  • Verify reduced-motion behavior.
  • Verify RTL behavior if your app supports Arabic/Hebrew/Persian.

Internal examples

These are production-style primitive patterns from the internal registry.

1) Guarded async dialog

2) Side-aware popover

3) Dropdown menu selection semantics

4) Tooltip delay behavior

5) Controlled select

6) Alert dialog confirmation


Learn next