# 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 = 600)
```

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. The default, 600 ms, is longer than the longest out moment a brand can set (pop's 300 ms at the slowest speed, 1.5×, is 450 ms). 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.

### useMotionMode

```ts
useMotionMode(ref: React.RefObject<Element | null>)
```

`motionMode` for a component that has to react to it: the person's setting where `ref`'s element is, read after mount and again whenever the app changes `data-motion` or the OS setting changes. It starts as `full` for the first render, since there is nothing to read until the element exists. Use it for behaviour (a timer that must stop), not for styling, which the CSS variants already answer.

### 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`.
