Styling gentleduck/primitives
How to style unstyled primitives with CSS, Tailwind, CSS modules, or CSS-in-JS.
gentleduck/primitives ship with zero CSS. Every component renders semantic HTML with data attributes that describe its state. You provide all visual styles.
The approach
gentleduck/primitives ship with zero CSS. Every component renders semantic HTML with data attributes that describe its state. You provide all visual styles using whatever method you prefer.
Styling options
The simplest approach. Apply utility classes directly:
<Dialog.Overlay className="fixed inset-0 bg-black/50 backdrop-blur-sm" />
<Dialog.Content className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white rounded-xl shadow-2xl p-6 w-full max-w-lg"><Dialog.Overlay className="fixed inset-0 bg-black/50 backdrop-blur-sm" />
<Dialog.Content className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white rounded-xl shadow-2xl p-6 w-full max-w-lg">For state-based styling, use Tailwind's data-* variant:
<Dialog.Overlay className="data-[state=open]:animate-fadeIn data-[state=closed]:animate-fadeOut" /><Dialog.Overlay className="data-[state=open]:animate-fadeIn data-[state=closed]:animate-fadeOut" />Data attributes reference
Primitives expose their state via data-* attributes on rendered elements. Use these for CSS-only styling based on state — no JavaScript needed.
| Attribute | Values | Used by |
|---|---|---|
data-state | open, closed | Dialog, Popover, Tooltip, HoverCard |
data-state | checked, unchecked, indeterminate | Menu CheckboxItem, RadioItem |
data-state | complete, loading, indeterminate | Progress |
data-state | delayed-open, instant-open, closed | Tooltip |
data-disabled | (present/absent) | Items, triggers |
data-highlighted | (present/absent) | Menu items |
data-side | top, right, bottom, left | Popper content |
data-align | start, center, end | Popper content |