# Motion Components

> Generated reference (regen-owned; see [index.md](index.md)). Binds only under `primitives: nurix`.

The effect tier (`effect/*`; the streaming cursor is a conversation surface, in [collections.md](collections.md)) plus the `element/animated-number` counter and the motion-aware `lib/slot`. Compose sparingly — decorative and low-frequency surfaces only; never on data or on actions users repeat dozens of times a day. Import each from its own subpath, e.g. `import { AnimatedList } from "@nurix/components/effect/animated-list"`.

Every effect times itself from the personality's motion ladder: durations follow the `pace` trait, and the caller periods below (seconds at neutral pace unless marked) scale with it. No effect takes a `transition`, `variants` or `initial` prop. Under `prefers-reduced-motion`, a zeroed ladder, or inside a `data-mode="inline"` collection, an effect goes still: a continuous effect renders its static frame and publishes `data-playing="false"` (it also pauses off screen and in a hidden tab), and a one-shot effect lands in its end state and publishes `data-reduced-motion`. Where an effect takes `as`, the value is a tag from the tier's closed motion tag table.

## Animated numbers

- `element/animated-number` — `AnimatedNumber`: rolling digits over static `Intl.NumberFormat` text on a `<data>` root (plain text through SSR, hydration and reduced motion — no layout shift, no flash of `0`); `value` (`null` or non-finite renders `fallback`), `format` (`Intl.NumberFormatOptions` minus two notations; default `{ notation: "standard", maximumFractionDigits: 0 }`), `locale`, `prefix`, `suffix`, `fallback`, `trend`, `digits`, `plugins` (`continuous`). The roll is timed by `pace`; there is no timing prop. `AnimatedNumber.Group` links counters that change together; `useIsSupported` is re-exported. **Optional peer:** `@number-flow/react` — install it or the subpath fails to resolve.

```tsx
<AnimatedNumber value={total} format={{ notation: "compact", maximumFractionDigits: 1 }} />
```

## Stagger entrance

- `effect/animated-list` — `AnimatedList`: animates keyed children entering, leaving and reflowing; `animation` [slide|fade|scale] (default `slide`), `animate` (play the entrance on first mount), `as`, `itemAs`, `layout` [true|position]. Every child needs a stable `key`; a leaver pops out of the flow against the `relative` root. Above 50 children, items render as plain elements with no motion. `block/list` already wraps it.

```tsx
<AnimatedList className="space-y-2">
  {items.map((item) => <Row key={item.id} item={item} />)}
</AnimatedList>
```

## Text effects

Per-character/word/line text animation — low-frequency surfaces only (empty states, onboarding, marketing-adjacent copy). Each member renders a `span` by default and takes a string child. `trigger` means one thing across the family: a `false → true` edge replays the effect.

- `effect/text-effects` — `TextEffect`: staggered reveal; `per` [word|char|line] (default `word`), `animation` [blur|fade-in-blur|scale|fade|slide] (default `fade`), `delay`, `speedReveal`, `speedSegment`, `trigger`, `onAnimationComplete`, `segmentWrapperClassName`. Prefer `per="word"` or `"line"` for long strings (above 50 segments, per-segment motion drops)
- `effect/text-effects` — `TextMorph`: cross-fades one short string into the next in place; change `children` in place, never force a new `key`
- `effect/text-effects` — `TextScramble`: decodes a string in from noise; `duration` (default `0.8`), `speed` (default `0.04`), `characterSet`, `trigger`, `onAnimationComplete`
- `effect/text-effects` — `TextShimmer`: continuous "still working" gradient sweep; `duration` (default `2`), `spread` (default `2`)
- `effect/text-effects` — `TypingAnimation`: types a string, or cycles `words`; `typeInterval` (default `0.1`), `deleteInterval` (default half `typeInterval`), `startDelay`, `holdDelay` (default `1`), `loop`, `start` [mount|view] (default `view`), `hasCursor` (default `true`), `isCursorBlinking` (default `true`), `cursorStyle` [line|block|underscore]
- `effect/dia-text-reveal` — `DiaTextReveal`: sweeps a colour band across a line, revealing it; `words` + `loop` rotate several strings, `colors` (band stops only — the text inherits its colour), `duration` (default `1.5`), `delay`, `holdDelay` (default `0.5`), `start` [mount|view|each-view] (default `view`), `hasFixedWidth`. The line is blank until the sweep passes

## Animated icons

- `effect/icons` — `AnimateIcon` plus 259 lucide-derived animated icons, each exported under its plain name and an `…Icon` alias (`Activity` / `ActivityIcon`, `ArrowDown` / `ArrowDownIcon`, …). Triggers sit on an icon or on an `AnimateIcon` wrapper that drives every icon inside it: `animateOnHover`, `animateOnTap`, `animateOnView` (`animateOnViewMargin`, `animateOnViewOnce`), or the boolean `animate` for an external trigger; `animation` names the variant (`default`, `default-loop`, … per icon), `loop`, `startDelay`, `holdDelay`, `persistOnAnimateEnd`, `completeOnStop`. Icon props: `size`, `label` (an unlabelled icon is `aria-hidden`). `AnimateIcon` drives only icons from this set — wrapping a plain `lucide-react` icon triggers nothing. A static icon is `lucide-react` directly; an icon from a stored name is `element/icon`.

## Morphing overlays

The trigger morphs its own bounds into the popup (shared layout on the slow motion step, not a fade); under reduced motion the popup opens on the plain transition. Both are parts of the overlay families in [overlays.md](overlays.md):

- `compound/dialog` — `Dialog.MorphTrigger`, in place of `Dialog.Trigger`; takes no `render` (style it with `className`)
- `element/popover` — `Popover.MorphTrigger`, in place of `Popover.Trigger`; takes no `render`

## Panel transitions

Swap between sibling panels, or a height-animated reveal.

- `effect/transition-panel` — `TransitionPanel` + `TransitionPanel.Panel` (each with a `value`): shows the Panel matching `value` (controlled only — no `defaultValue` or `onValueChange`; an unmatched `value` renders nothing); `animation` [fade|slide|scale] (default `fade`). Slide direction follows Panel order; panels of different heights change the root's height at the swap
- `compound/tabs` — `Tabs.Contents` settles the panel box to the active panel's height and fades the entering panel in; reach for Tabs rather than pairing a hand-rolled tab strip with `TransitionPanel` ([navigation.md](navigation.md))
- `compound/accordion` / `element/collapsible` — `Accordion.Panel` and `Collapsible.Panel` open on their own measured height ([layout.md](layout.md))

## Decorative effects

Pointer-tracked washes, sliding highlights and ambient loops — decorative chrome only, never load-bearing content.

- `effect/spotlight` — `Spotlight`: cursor-following radial glow, first child of a `relative overflow-hidden` parent; `diameter` (px, default `200`), `as` (`span` inside phrasing content). No spring prop — the trail follows `pace`
- `effect/shine-border` — `ShineBorder`: absolutely-positioned animated gradient border, placed as the direct first child of a `relative overflow-hidden` rounded card; `borderWidth` (px, defaults to `--edge-hairline`), `duration` (seconds, default 14), `colors` (gradient stops, default `["var(--primary)"]`).
- `effect/glow-effect` — `GlowEffect`: animated multi-colour halo, first child of a `relative` parent with the content in a later `relative` sibling; `animation` [rotate|pulse|breathe|colorShift|flowHorizontal|static] (default `rotate`), `colors`, `blur` [softest|soft|medium|strong|stronger|strongest|none] or a px number (default `medium`), `duration` (default `5`), `scale` (default `1`; raise it to bleed past the parent)
- `effect/grainient` — `Grainient`: WebGL2 animated grain gradient for a hero or empty-state background; `colors` (three stops, default `GRAINIENT_DEFAULT_COLORS`; token stops follow `.dark`), `timeSpeed` (default `0.25`), `warpSpeed`, `isGrainAnimated`, `maxFps` (default `30`), plus shader tuning (`colorBalance`, `warpStrength`, `warpFrequency`, `warpAmplitude`, `blendAngle`, `blendSoftness`, `rotationAmount`, `noiseScale`, `grainAmount`, `grainScale`, `contrast`, `gamma`, `saturation`, `centerX`, `centerY`, `zoom`). Override its root to `absolute inset-0` inside a `relative` parent; one instance per screen
- `effect/infinite-slider` — `InfiniteSlider`: seamless looping marquee that pauses on hover and focus within; `gap` (px, default `16`), `speed` (default `100`), `orientation` [horizontal|vertical], `isReversed`, `isPaused`, `as`. Children render twice — give each a stable `key` and keep `id`s and form controls out
- `effect/highlight` — `Highlight` + `Highlight.Item` (each with a string `value`): one box slides behind the item the host's `value` names (controlled only; `value={null}` shows none); `mode` [children|parent] (`parent` for mixed-size items), `exitDelay` (ms, default `200`), `boundsOffset`, `containerClassName`, `as`; root `className` and `style` paint the box (add `inset-0` in `children` mode). `useHighlightHover(restingValue)` returns `value` plus `getItemProps(id)` for a box that follows the pointer — paint it neutral (`bg-muted`), never the accent

```tsx
const hover = useHighlightHover(currentPage);

<Highlight value={hover.value} className="inset-0 rounded-md bg-muted">
  {pages.map((page) => (
    <Highlight.Item key={page.id} value={page.id} {...hover.getItemProps(page.id)}>
      <a href={page.href}>{page.label}</a>
    </Highlight.Item>
  ))}
</Highlight>
```

## Motion-aware slot

`Slot` (from `lib/slot`) — the motion-aware `asChild` slot: renders its single child element with the slot's props merged on (classes through `cn`, `style` merged with the slot's values winning, both refs kept), lifting the child onto a motion component so motion props reach a plain element. A non-element child renders nothing. `WithAsChild<Props>` is the matching props shape. Base UI parts compose through their `render` prop instead; `Slot` is for consumers building their own motion children.

```tsx
<Slot whileHover={{ scale: 1.02 }} className="rounded-md">
    <a href="/reports">Reports</a>
</Slot>
```

## Dynamic toolbar

No primitive (`dynamic-toolbar`) — a toolbar that expands into a contextual input follows the plain-spec fallback in [index.md](index.md).
