# Motion

Builds a moment's chain. Call it once at module level and spread the result into the element's `style`, beside the family utility that the component adds at the honest moment:

```tsx const tick = motion("copy-field", "tick", "pop") <Check style={tick} className={cn(copied && "cd-pop")} /> ```

The brand reaches the moment with `--cd-motion-copy-field-tick` (its level), `-dur`, `-ease`, the family's amplitude suffix and `-variant`; anything it doesn't set falls back to the family (`--cd-motion-pop-*`), then to `defaults`, then to Candor's. Two families on one element compose: `style={{ ...press, ...tint }}`.

- Registry name: `motion` (`@candor/motion`)
- Tier: Free (MIT). No licence key needed.
- Docs: https://candor.themethodic.com/docs/motion

## Install

```bash
npx candor-ui add motion
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/motion`.
It brings in: status.

## Functions

### motion

```ts
motion(item: string, moment: string, family: F, defaults: MomentDefaults<F> = {})
```

Builds a moment's chain. Call it once at module level and spread the result into the element's `style`, beside the family utility that the component adds at the honest moment:

```tsx const tick = motion("copy-field", "tick", "pop") <Check style={tick} className={cn(copied && "cd-pop")} /> ```

The brand reaches the moment with `--cd-motion-copy-field-tick` (its level), `-dur`, `-ease`, the family's amplitude suffix and `-variant`; anything it doesn't set falls back to the family (`--cd-motion-pop-*`), then to `defaults`, then to Candor's. Two families on one element compose: `style={{ ...press, ...tint }}`.

### useSettled

```ts
useSettled(value: T, test?: SettledTest<T>)
```

### useArrived

```ts
useArrived(keys: readonly string[])
```

Which keys were added after the component first drew. Keys present at the first render answer false, so a list that loads with fifty entries draws them still; a key added later answers true until it leaves (and true again if it comes back).

### useLeaving

```ts
useLeaving(items: readonly T[], keyOf: (item: T) => string, fallbackMs = 400)
```

Items that just left, kept as inert ghosts for the length of their out moment. Each ghost goes when `left(item)` is called (from its `onAnimationEnd`), or after `fallbackMs` when nothing animated. An item that comes back while its ghost is still leaving takes the ghost's place.

### motionMode

```ts
motionMode(el: Element | null | undefined)
```

The person's setting where `el` is: `none` under `data-motion="none"`, `calm` under the OS's reduced-motion setting or `data-motion="calm"`, else `full`. `none` or `calm` anywhere above wins (the same reach as the CSS variants), the OS setting can't be raised by the app, and values that aren't Candor's (Radix writes its own `data-motion` on menu panels) are ignored.

### motionLevel

```ts
motionLevel(el: Element | null | undefined, moment: MotionMoment | MotionFamily)
```

The level a moment resolves to on `el`, read from its computed style: the moment's token, its family's, the preset, then capped by `data-motion`. A page without Candor's CSS reads as subtle.

### useMotionAllowed

```ts
useMotionAllowed(ref: React.RefObject<Element | null>, moment: MotionStyle | MotionFamily)
```

For motion that CSS can't stop (smooth scrolling, the Web Animations API, a bar painted from the clock): a stable function that answers, at the moment of the motion, whether it may play on `ref`'s element. False under `data-motion="none"`, under the calm level (reduced motion), and when the moment's resolved level is 0. Pass the moment's chain from `motion()`, or a family name for the family's level.

## Types

### MotionFamily

The fourteen families: one per kind of change a person can see.

```ts
| "reveal" | "arrive" | "pop" | "mark" | "fill" | "value" | "move" | "overlay" | "press" | "tint" | "refuse" | "attention" | "live" | "clock"
```

### MotionLevel

A motion level: 0 off (the end state appears, events still fire), 1 subtle, 2 expressive.

```ts
0 | 1 | 2
```

### MotionEase

The easings a family may allow. Spring overshoots, so only spatial families accept it.

```ts
"standard" | "inout" | "spring" | "linear"
```

### MotionAmplitude

A family's amplitude: what its expressive level moves by.

```ts
{ /** The token suffix: `--cd-motion-<family>-<name>`. */ readonly name: "distance" | "scale" | "count" /** The value at the expressive level, as CSS. */ readonly default: string /** The lowest value `theme check` accepts. */ readonly min: number /** The highest value `theme check` accepts. */ readonly max: number /** The unit `min` and `max` are in ("" for a plain number). */ readonly unit: "px" 
```

### MotionDuration

A duration with the range `theme check` accepts, in milliseconds.

```ts
{ readonly default: number; readonly min: number; readonly max: number }
```

### MotionFamilySpec

Everything Candor knows about one family. `check:motion` checks `candor.css` against these.

```ts
{ /** What the family reports, in words, for the docs and the theme builder. */ readonly reports: string /** Moves things in space (scale, translate, size). Spring easing is accepted only here. */ readonly spatial: boolean /** The information itself (a hold, a deadline): a brand can't turn it below subtle. */ readonly essential: boolean /** How long one play takes; `null` when the clock decides. *
```

### MotionVariant

A family's variants, by name: `MotionVariant<"mark">` is `"draw" | "pop" | "drop"`.

```ts
(typeof motionFamilies)[F]["variants"][number]
```

### MomentDefaults

What a moment may set as its own default, where it differs from its family's.

```ts
{ /** The moment's duration, in milliseconds, within its family's range. */ dur?: number | undefined /** How long leaving takes, for families with an out moment. */ outDur?: number | undefined /** The moment's easing, from its family's allowed set. */ ease?: (typeof motionFamilies)[F]["eases"][number] | undefined /** The amplitude at the expressive level, as CSS (`"0.9"`, `"0.25rem"`, `"3"`). */ a
```

### MotionMoment

The moment a chain belongs to, kept on the style object (not enumerable, so it never reaches the DOM).

```ts
{ readonly item: string; readonly moment: string; readonly family: MotionFamily }
```

### MotionStyle

A moment's chain: CSS variables to spread into the moving element's `style`. Each resolves the moment's own token, then its family's, then the default, on that element, so a `data-motion` container anywhere above it still applies.

```ts
React.CSSProperties & { readonly [momentKey]?: MotionMoment | undefined }
```

### SettledTest

How `useSettled` tells a finished value from an unfinished one; Candor's run statuses when left out.

```ts
{ /** Reached an outcome worth showing settling (done or failed). */ settled: (value: T) => boolean /** Over in any way, including ones that don't animate (cancelled, skipped); `settled` when left out. */ over?: ((value: T) => boolean) | undefined }
```

### MotionMode

What the person allows here: everything, the calm level (reduced motion), or nothing.

```ts
"full" | "calm" | "none"
```

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider` (fixed strings) and `TermsProvider` (nouns); entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
