# Motion

> Continuous transitions for changing labels, icons, and local layouts. Keeps the live controls and focus in place.

- Name: `motion`
- Status: beta
- Categories: layout
- File: the install writes `motion.tsx` to your `ui` alias, `@/components/ui` by default.
- Other registry items: `@arbokui/motion-timing`. The install adds them.
- Gallery: https://arbokui.com/components/motion
- Registry item: https://arbokui.com/r/motion.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@arbokui=https://arbokui.com/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@arbokui": "https://arbokui.com/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@arbokui` in a public list that Arbokui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @arbokui/theme
   ```

3. Add the component:

   ```bash
   npx shadcn@latest add @arbokui/motion
   ```

## Use it for

- Use MotionSwap with a meaningful value when a label or icon changes state.
- Use Motion around a local group whose items are added, removed, or rearranged. Keep keys stable. Nested lists use the none axis so their parent alone owns the occupied size.
- Use useMotionLayout(ref) on an existing positioned element when an extra wrapper would change its semantics.
- The latest state is immediately usable; only the visual transition takes time.

## Avoid it for

- Wrapping the whole page or nesting boundaries that animate the same geometry.
- Animating each keystroke, pointer drag, or slider scrub. These track the input directly.
- Remounting controls to replay an entrance. Initial content renders at rest.

## Props

- `useMotionLayout`: `(ref: RefObject<HTMLElement | null>, options?: { axis?: "height" | "both" | "none"; items?: boolean }) => void`. Apply local layout motion to an existing positioned element.
- `MotionControl`: `({ children: ReactElement }) => ReactElement`. Add size continuity to one existing control without a wrapper.
- `motionSettings`: `(node: Element) => { duration: number; fade: number; easing: string; travel: number }`. Read the motion tokens in scope at node, for script animation. duration is the layout time and fade the fast time, in ms. easing is the ease-out curve. travel is the distance in px. Motion off makes the numbers 0; reduced motion makes duration and travel 0.
- `axis`: `"height" | "both" | "none"`. Default `"height"`. Resize height, both dimensions, or use none for an items-only boundary inside a resizing parent.
- `items`: `boolean`. Default `true`. Motion also moves its direct children and fades additions and removals.
- `value`: `string | number | boolean`. MotionSwap's meaningful state identifier; a change fades or rolls its contents, as effect sets.
- `effect`: `"fade" | "roll"`. Default `"fade"`. MotionSwap: roll compact chart readouts vertically; keep the default fade for other labels. Reduced motion keeps only the fade.
- `children`: `ReactNode`. The current content, immediately available to keyboard and assistive technology.
- `className`: `string`. Layout classes on the boundary. Later classes win.

## Accessibility

- Live controls stay mounted, preserving focus and drafts. Outgoing visuals are inert, unnamed, and hidden from screen readers.
- Motion off is immediate. Reduced motion removes spatial transitions and keeps short fades.
- Without browser animation support, all content updates immediately.

## Examples

### A local boundary

```tsx
<Motion>Content</Motion>
```

### An existing control

```tsx
<MotionControl><button type="button">Save</button></MotionControl>
```

### An existing element

```tsx
function LocalList() { const ref = useRef<HTMLUListElement>(null); useMotionLayout(ref); return <ul ref={ref} className="relative"><li>Design</li></ul>; }
```

### A result label

```tsx
<MotionSwap value={saved}>{saved ? "Saved" : "Save changes"}</MotionSwap>
```

### A rolling chart readout

```tsx
<MotionSwap value={day} effect="roll">{day}</MotionSwap>
```

### A script fade on the theme's times

```tsx
const { fade, easing } = motionSettings(node); node.animate([{ opacity: 0 }, { opacity: 1 }], { duration: fade, easing });
```

### A changing list

```tsx
<Motion className="flex flex-col gap-2">{items.map(item => <div key={item.id}>{item.name}</div>)}</Motion>
```
