# Motion primitives

> Animation building blocks: FacadeMotionProvider, FadeIn, Reveal, Stagger and Collapse. They animate only opacity and transform, and follow the reduced-motion setting.

Facade UI item `motion-primitives` (component, motion).
Docs page: https://facadeui.dev/components/motion-primitives · Registry JSON: https://facadeui.dev/r/motion-primitives.json

## Install

```bash
npx shadcn@latest add @facade/motion-primitives
```

Or by URL: `npx shadcn@latest add https://facadeui.dev/r/motion-primitives.json`. The CLI also installs what it needs: motion@^13.4.0, @facade/utils.

Files: `lib/motion.ts`, `components/motion/facade-motion-provider.tsx`, `components/motion/fade-in.tsx`, `components/motion/reveal.tsx`, `components/motion/stagger.tsx`, `components/motion/collapse.tsx`, `components/motion/slots.tsx`

Usage example as an installable item: `npx shadcn@latest add @facade/motion-primitives-demo` (lands in components/examples/).

## Usage

The source of the preview on the docs page. `./content` holds sample data.

```tsx
"use client"

import { FadeIn } from "@/components/motion/fade-in"
import { Reveal } from "@/components/motion/reveal"
import { Stagger, StaggerItem } from "@/components/motion/stagger"

const items = ["Composable", "Accessible", "Themeable", "Framework-neutral"]

export function Demo() {
  return (
    <div className="flex flex-col gap-10">
      <FadeIn className="bg-accent text-accent-foreground rounded-lg px-4 py-3 text-sm font-medium">
        FadeIn — plays on mount
      </FadeIn>

      <Stagger as="ul" trigger="mount" className="grid gap-3 sm:grid-cols-2">
        {items.map((item) => (
          <StaggerItem
            key={item}
            as="li"
            className="bg-card rounded-lg border px-4 py-3 text-sm font-medium"
          >
            {item}
          </StaggerItem>
        ))}
      </Stagger>

      <Reveal
        direction="left"
        className="bg-accent text-accent-foreground rounded-lg px-4 py-3 text-sm font-medium"
      >
        Reveal — plays once, when scrolled into view
      </Reveal>
    </div>
  )
}
```

## Props

### FacadeMotionProviderProps

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` (required) | `ReactNode` |  |  |
| `reducedMotion` | `"user" \| "always" \| "never"` | `"user"` | `"user"` (default) follows the OS setting. Use `"always"` for reduced-motion screenshots, and `"never"` only in tests. |
| `nonce` | `string` |  | Disables `nonce`-less inline style injection in strict CSP setups. |

### FadeInProps

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` (required) | `ReactNode` |  |  |
| `direction` | `FadeDirection` | `"up"` | The direction the element moves in *from*. `"none"` fades without moving. |
| `duration` | `number` | `facadeDuration.base` | Seconds. Defaults to `--facade-duration-base`. |
| `delay` | `number` | `0` | Seconds. |
| `distance` | `number` |  | Distance moved, in px. Defaults to `--facade-motion-distance` (16px). |
| `as` | `MotionTag` | `"div"` |  |
| `className` | `string` |  |  |

### RevealProps

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` (required) | `ReactNode` |  |  |
| `direction` | `FadeDirection` | `"up"` |  |
| `duration` | `number` | `facadeDuration.base` |  |
| `delay` | `number` | `0` |  |
| `distance` | `number` |  |  |
| `as` | `MotionTag` | `"div"` |  |
| `className` | `string` |  |  |
| `repeat` | `boolean` | `false` | Replays each time the element comes back into view. Default `false`. |
| `amount` | `number` | `FACADE_VIEWPORT.amount` | How much of the element must be visible to start, from 0 to 1. Default `0.25`. |

### StaggerProps

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` (required) | `ReactNode` |  |  |
| `stagger` | `number` | `0.08` | Seconds between each child. Default `0.08`. |
| `delayChildren` | `number` | `0` | Seconds before the first child starts. Default `0`. |
| `as` | `MotionTag` | `"div"` |  |
| `className` | `string` |  |  |
| `trigger` | `"view" \| "mount"` | `"view"` | `"view"` (default) waits until it is scrolled into view. `"mount"` plays at once. |
| `repeat` | `boolean` | `false` |  |
| `amount` | `number` | `FACADE_VIEWPORT.amount` |  |

### StaggerItemProps

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` (required) | `ReactNode` |  |  |
| `direction` | `FadeDirection` | `"up"` |  |
| `duration` | `number` | `facadeDuration.base` |  |
| `distance` | `number` |  |  |
| `as` | `MotionTag` | `"div"` |  |
| `className` | `string` |  |  |

### CollapseProps

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `open` (required) | `boolean` |  |  |
| `children` (required) | `ReactNode` |  |  |
| `duration` | `number` | `facadeDuration.fast` | Seconds. Defaults to `--facade-duration-fast`, which suits short panels. |
| `className` | `string` |  |  |
| `keepMounted` | `boolean` | `false` | Keeps the panel in the DOM and hides it with `hidden`. This lets browser find-in-page reach the content. |
| `id` | `string` |  | Set on the panel element. Use the same value as the trigger's `aria-controls`. |

### MotionSlotProps

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` |  |  |
| `children` | `ReactNode` |  |  |
