Skip to main content

Duck Primitives

Headless, accessibility-first React primitives. Shared Slot, Presence, Popper, and focus-scope load once, so Alert Dialog ships at 1.6 KB and Popover at 2.4 KB.

Headless + a11y-first

Unstyled primitives, shared internals

A headless, accessibility-first component library. Slot, Presence, Popper, and focus-scope are shared across every primitive, so each one stays small.

Shared internals

Alert Dialog: 1.6 KB. Popover: 2.4 KB. Shared Slot, Presence, Popper, and focus-scope load once across every primitive.

A11y built in

Every primitive ships with the right roles, states, and keyboard interactions. Compose them and ARIA is already wired.

Compound components

Compose `Root`, `Trigger`, `Content`, and `Portal` with scoped context. Each part owns its concern, no prop drilling.

Keyboard everything

Roving focus, type-ahead search, arrow-key navigation, and focus trapping. Same semantics across menus, dialogs, and selects.

Familiar API

Compound parts, `asChild`, `data-state` attributes. If you have used a headless primitive library before, the vocabulary is the same.

40+ primitives

Dialog, Popover, Select, Menu, Command, Calendar, Input OTP, Navigation Menu. All headless and unstyled.

Install

Import each primitive from its own subpath.

# Install
bun add @gentleduck/primitives

# Import any primitive
import { Root, Trigger, Portal, Content } from '@gentleduck/primitives/dialog'

Free & open source

gentleduck is MIT licensed and will always be free and open source. Every package ships with full source access — fork it, modify it, own it.

Become a Sponsor