}>
  **Lesson 6 of 10**: Motion isn't decoration. It must respect lifecycle, accessibility, and user settings.

## Why Presence exists

A common bug in animated overlays: component unmounts before exit animation finishes.

`Presence` solves this by delaying unmount until animation completion.

```tsx
import { Presence } from '@gentleduck/primitives/presence'

<Presence present={open}>
  <div className={open ? 'animate-in' : 'animate-out'} />
</Presence>
```

***

## State machine mental model

`Presence` transitions through:

1. `mounted`
2. `unmountSuspended`
3. `unmounted`

When `present` becomes false, it checks animation state and waits for completion.

***

## CSS contract for primitives

Most primitives expose `data-state="open|closed"`.

```css
.dialog-content[data-state='open'] { animation: zoomIn 160ms ease; }
.dialog-content[data-state='closed'] { animation: zoomOut 120ms ease; }
```

Always define both states when using animated exit.

***

## Using JS animation libraries

Use `forceMount` when a motion library controls render lifecycle.

```tsx
<Dialog.Portal forceMount>
  <Dialog.Content forceMount asChild>
    <motion.div />
  </Dialog.Content>
</Dialog.Portal>
```

This avoids double lifecycle ownership.

***

## Reduced motion policy

Respect system preference:

```css
@media (prefers-reduced-motion: reduce) {
  .dialog-overlay,
  .dialog-content {
    animation: none !important;
  }
}
```

Motion should never block usability.

***

## Debugging animation issues

If exit motion is not visible:

1. Verify closed-state animation exists.
2. Verify selector matches rendered element.
3. Verify no immediate `display: none` style is applied.
4. Verify `forceMount` strategy if using JS animation library.

***

## Lab

1. Add side-aware popover motion.
2. Add dialog overlay/content enter and exit motion.
3. Validate behavior with reduced-motion enabled.

}>
  Next: [Lesson 7 - Accessibility Deep Dive](/duck-primitives/course/07-accessibility)