introducing duck motion
Enter/exit animations, mount/unmount transitions, and staggered sequences — built to pair with duck-variants and duck-primitives.
Why Another Animation Library?
General-purpose animation libraries handle springs, keyframes, and timeline orchestration. That is more machinery than you need for a fade-in dialog or a dropdown transition.
@gentleduck/motion targets UI component animations. It coordinates mount/unmount with CSS or JS transitions and stays small.
What It Solves
You want a dialog to fade in on open and fade out on close. React unmounts the component immediately when open={false}, so the exit animation never plays.
// The problem: component unmounts before animation finishes
{open && <Dialog>...</Dialog>}// The problem: component unmounts before animation finishes
{open && <Dialog>...</Dialog>}@gentleduck/motion provides a Presence primitive that holds the component in the tree until the exit animation finishes.
How It Works
Three phases:
- Mount — component enters the DOM, enter animation triggers
- Present — component is visible and interactive
- Exit — exit animation plays, component unmounts after it ends
Used Across the Ecosystem
Every animated component in @gentleduck/ui uses motion primitives:
| Component | Animation |
|---|---|
| Dialog | Fade + scale on open/close |
| Sheet | Slide from edge with backdrop fade |
| Drawer | Slide up with drag-to-dismiss |
| Dropdown Menu | Scale + fade from trigger |
| Tooltip | Fade with slight offset |
| Popover | Scale from anchor point |
| Collapsible | Height animation |
| Accordion | Height animation per section |
Pairing with Variants
Motion pairs with @gentleduck/variants for conditional animation classes:
import { cva } from '@gentleduck/variants'
const overlay = cva('fixed inset-0 bg-black/50', {
variants: {
state: {
open: 'animate-in fade-in',
closed: 'animate-out fade-out',
},
},
})import { cva } from '@gentleduck/variants'
const overlay = cva('fixed inset-0 bg-black/50', {
variants: {
state: {
open: 'animate-in fade-in',
closed: 'animate-out fade-out',
},
},
})Variants pick the classes. Motion picks when to unmount.
Getting Started
bun add @gentleduck/motionbun add @gentleduck/motionMotion is a peer dependency of @gentleduck/primitives and ships automatically with the CLI installer. Install it directly if you are building custom components on top of primitives.