Skip to content
Agentmart
Submit a tool

motion-and-animation

Catalogs each animation as a Reanimated 4 moment.

Skill by zakariaf

IndexedMIT★ 11
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/zakariaf-motion-and-animation

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Catalogs each animation as a Reanimated 4 moment. Use when animating a transition, sheet, loop, drag, spring or celebration, or one ignores Reduce Motion or blocks taps. Not for canvases (skia-canvas-rendering).

Use cases: spreadsheets, design

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 26, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found
Runs as
Node.js package

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/motion-and-animation

What's inside

Skill name
motion-and-animation
Description
Catalogs each animation as a Reanimated 4 moment. Use when animating a transition, sheet, loop, drag, spring or celebration, or one ignores Reduce Motion or blocks taps. Not for canvases (skia-canvas-rendering).
License
MIT
Instructions
229 lines in SKILL.md · view on GitHub

Before installing

  • Ships executable scripts

Files (73)

  • SKILL.md
  • shared.json · assets/
  • template-placeholders.json · assets/
  • app-stack.tsx · examples/
  • due-chip.tsx · examples/
  • hold-to-delete.tsx · examples/
  • progress-bar.tsx · examples/
  • root-layout.tsx · examples/
  • root-stack.tsx · examples/
  • sync-badge.tsx · examples/
  • task-check-cell.tsx · examples/
  • task-check.tsx · examples/
  • task-list-row.tsx · examples/
  • task-sheet.tsx · examples/
  • tasks-motion-moments.ts · examples/
  • moment-catalog.md · references/
  • reanimated-4-in-practice.md · references/
  • reduce-motion-and-interruption.md · references/
  • testing-motion.md · references/
  • check-lib.mjs · scripts/
  • check-motion.mjs · scripts/
  • app-modules.mjs · scripts/lib/
  • motion-catalog.mjs · scripts/lib/
  • motion-code.mjs · scripts/lib/
  • source-scan.mjs · scripts/lib/
  • selftest.mjs · scripts/
  • use-is-app-active.test.ts · templates/src/hooks/
  • use-is-app-active.ts · templates/src/hooks/
  • motion-config.test.tsx · templates/src/ui/a11y/
  • motion-config.tsx · templates/src/ui/a11y/
  • system-a11y-store.test.ts · templates/src/ui/a11y/
  • system-a11y-store.ts · templates/src/ui/a11y/
  • use-reduce-motion.test.tsx · templates/src/ui/a11y/
  • use-reduce-motion.ts · templates/src/ui/a11y/
  • burst-piece.tsx · templates/src/ui/motion/
  • celebration-burst.test.tsx · templates/src/ui/motion/
  • celebration-burst.tsx · templates/src/ui/motion/
  • moment-config.test.ts · templates/src/ui/motion/
  • moment-config.ts · templates/src/ui/motion/
  • motion-moments.test.ts · templates/src/ui/motion/
  • motion-moments.ts · templates/src/ui/motion/
  • use-celebration.test.tsx · templates/src/ui/motion/
  • use-celebration.ts · templates/src/ui/motion/
  • use-motion-loop.test.tsx · templates/src/ui/motion/
  • use-motion-loop.ts · templates/src/ui/motion/
  • use-screen-animation.test.tsx · templates/src/ui/motion/
  • use-screen-animation.ts · templates/src/ui/motion/
  • wobble.tsx · tests/bases/app/.agents/skills/kit/templates/src/ui/
  • package.json · tests/bases/app/
  • fade.tsx · tests/bases/app/skills/brand/src/ui/
  • _layout.tsx · tests/bases/app/src/app/
  • board-glow.ts · tests/bases/app/src/canvas/
  • hold-to-delete.tsx · tests/bases/app/src/features/lists/components/
  • sync-badge.tsx · tests/bases/app/src/features/sync/components/
  • due-chip.tsx · tests/bases/app/src/features/tasks/components/
  • progress-bar.tsx · tests/bases/app/src/features/tasks/components/
  • task-check-cell.tsx · tests/bases/app/src/features/tasks/components/
  • task-check.test.tsx · tests/bases/app/src/features/tasks/components/
  • task-check.tsx · tests/bases/app/src/features/tasks/components/
  • task-list-row.tsx · tests/bases/app/src/features/tasks/components/
  • …and 13 more

SKILL.md (first lines)

# Motion and animation

Makes every moving thing in an Expo app a declared moment: named once in a catalog with what starts
it, the motion tokens it spends and what it shows when motion is reduced; built with Reanimated 4 on
the UI thread from wherever it is now; never in the way of the next touch; looping only while someone
can see it; celebrating within a budget, once. One checker proves the code and the catalog agree.

## Rules that must hold

1. **Declare every moment once, in the moment catalog** (`src/ui/motion/motion-moments.ts`): its
   kind, a trigger a reviewer can find in the app, its timing as token names (`{ duration, easing,
   delay?, stagger? }`, `{ spring }` or `{ navigator: 'stack' }`) and its reduced alternative; plain
   data with type-only imports. Why: a moment nobody declared is a library default chosen by nobody,
   and it never meets the reduce-motion review (`moment-catalog`, `moment-tokens`).
2. **Every file that animates names its moment.** It takes timing through `timingOf` / `springOf` /
   `delayOf` / `staggerOf(MOTION_MOMENTS.<id>)` (`moment-config.ts`), or, when it cannot import the
   catalog (the shared press surface, a file rendering a native `<Stack>`), the moment lists it in
   `files` and the file spends the token the moment names; nothing declared goes unused. Why: the
   catalog stays a true list only if code cannot move without it (`moment-declared`,
   `moment-files`, `moment-unused`).
3. **Every moment declares what reduced motion shows, as its kind allows:** feedback `instant`,
   `fade` or `none`; state `instant` or `fade`; transition `fade`, `instant` or `none`; attention
   `static` or `none`; celebration `static`, `fade` or `none` (`none` when drawn with
   `<CelebrationBurst>`); functional `unchanged` only; a `fade` uses `REDUCED_FADE` or
   `reducedEntering()` in code. Why: the end state must be identical with motion off (WCAG 2.3.3),
   and "it is small" is not an alternative (`moment-reduced`).
4. **Reduce motion reaches every animation from one root.** The root layout, or a file it renders,
   mounts accessibility-as-code's `<MotionConfig preference>` and starts `watchSystemA11y()`;
   anything that reads `useReduceMotion()` or `useScreenAnimation()` renders below `<MotionConfig>`,
   never in the component that mounts it; only a functional moment's file (and the reduced fade in
   `moment-config.ts`) passes `ReduceMotion.Never`. Why: without the config no `withTiming`,
   `withSpring` or layout animation sees the in-app setting or a live switch; context flows down
   only, so the mounting component reads the default `'system'`; without the watcher
   `useReduceMotion()` stays false (`reduce-motion-root`, `reduce-motion-opt-out`).
5. **What the root cannot reach reads the answer itself.** Reanimated CSS animations and
   transitions carry the condition on the key (`transitionProperty: isReducedMotion ? 'none' : ...`;
   a bare `transitionDuration` transitions `'all'`);
   every native `<Stack>` takes `animation: useScreenAnimation()`; React Native's `LayoutAnimation`
   is not used. Why: Reanimated 4.5.1 applies no reduce-motion setting to CSS, and
   react-native-screens 4.26.2 reads none (both verified in source) (`css-motion-reduced`,
   `screen-transition-reduced`, `reanimated-only`).
6. **Loop only through `useMotionLoop(moment, { isFocused })`.** It runs while the screen is
   focused, the app is active and motion is not reduced, and cancels a running loop when that
   changes. Why: Reanimated decides reduce motion once, when an animation starts, so a running loop
   ignores the switch; a pushed screen keeps the one below mounted, loop and battery drain included
   (`loop-through-hook`).
7. **Motion decorates state; it never is the state.** Commit in the handler first, then animate; no
   completion callback hands work back to JS outside a functional moment; nothing switches input off
   while something moves. Why: an interrupted animation calls back with `finished: false`, so the
   commit never happens, and a locked control reads as a frozen app (`commit-before-motion`,
   `no-input-lock`).
8. **Every animation is interruptible.** Retarget from the current value (never snap to the start
   first), let the finger catch the motion, and give a spring started when a drag, pinch or rotation
   ends the gesture's velocity (tap, long-press and fling events carry none). Why: Reanimated starts a new animation where the old one is and a spring keeps its
   momentum; a reset snaps, and a flick without velocity feels dead (`no-reset-before-animate`,
   `gesture-velocity`).
9. **Sequence on the UI thread.** `withDelay` and `withSequence`, never `setTimeout`, `setInterval`
   or `requestAnimationFrame` to start or stop an animation. Why: `withDelay` skips its delay under
   reduced motion, while a JS timer waits it out, drifts from the frames and fires after unmount
   (`no-timer-sequencing`).
10. **Frames stay on the UI thread.** No `scheduleOnRN` or React setter inside `useAnimatedStyle`,
    `useDerivedValue`, `useAnimatedProps`, `useFrameCallback`, a scroll handler or a gesture's
    `onUpdate` / `onChange`; cross once in `onEnd` or a `useAnimatedReaction`; `scheduleOnRN`, never
    the deprecated `runOnJS` / `runOnUI`. Why: one React render per frame is the jank Reanimated
    exists to remove (`no-js-per-frame`, `worklets-api`).
11. **Animate transform and opacity.** A layout key in `useAnimatedStyle` (`width`, `top`,
    `margin`...) needs `// motion-layout: <reason>` on the line above (an absolutely positioned,
    childless fill); laid-out size changes use layout animations. Why: a layout key re-runs Yoga for
    the node and its siblings on every frame (`compositor-props`).
12. **Celebrations are bounded, played once and skippable.** A celebration counts its `pieces` and
    ends within `CELEBRATION_LIMITS` (library ceilings 2000 ms and 40 pieces, computed from the
    tokens), plays through `useCelebration` from the handler that committed the result (never a
    `useEffect` or focus effect), starts at the point of action and takes no touch. Why: an unbounded flourish is a
    loading screen nobody asked for, and a mount-effect trigger replays it on every remount
    (`celebration-budget`, `celebration-through-hook`).

Owned elsewhere and only cited here: motion token values, complete springs and `no-raw-duration`
(design-system-structure); `useReduceMotion`, `MotionConfig` and the ban on Reanimated's
`useReducedMotion` (accessibility-as-code); `.get()` / `.set()` (react-components-and-hooks); when
sounds and haptics play (audio-and-haptics-feedback); canvas timelines (skia-canvas-rendering).

## Workflow

1. **Detect.** `package.json` must depend on `react-native-reanimated` (`npx expo install
   react-native-reanimated react-native-worklets`; `babel-preset-expo` adds the Worklets plugin);
   without it check-motion prints NOT APPLICABLE. The catalog reads design-system-structure's
   `src/theme/motion.ts`, and the root wiring uses accessibility-as-code's `src/ui/a11y/`.
2. **Set up once.** Copy `templates/src/ui/motion/` to `src/ui/motion/`; copy
   `templates/src/ui/a11y/` and `templates/src/hooks/` only where the app lacks them (synced,
   identical canonical copies). Replace `__ROOT_STACK_FILE__` in the catalog with the file that
   renders the root `<Stack>` and list every other `<Stack>` file there. Start `watchSystemA11y` in
   the root layout and mount `<MotionConfig>` around the navigator, every `<Stack>` taking
   `useScreenAnimation()` in a component rendered below `<MotionConfig>`, as
   [examples/root-layout.tsx](examples/root-layout.tsx),
   [examples/root-stack.tsx](examples/root-stack.tsx) and
   [examples/app-stack.tsx](examples/app-stack.tsx) show. Read
   [references/reduce-motion-and-interruption.md](references/reduce-motion-and-interruption.md).
3. **Decide whether it should move.** Read [references/moment-catalog.md](references/moment-catalog.md)
   ("Should it move at all", kinds, tokens, choreography). If the answer is no, there is no moment.
4. **Declare the moment.** Add the entry: kind, trigger, token names (a missing token goes into the
   token file first, design-system-structure), the reduced alternative its kind allows, `pieces` for
   a celebration, `cue` when the commit plays a sound or haptic. Imitate
   [examples/tasks-motion-moments.ts](examples/tasks-motion-moments.ts). Review with
   `node ${CLAUDE_SKILL_DIR}/scripts/check-motion.mjs . --table`.
5. **Build it with the right tool** ([references/reanimated-4-in-practice.md](references/reanimated-4-in-practice.md),
   "Choosing the tool"): a state change follows its value
   ([examples/task-check.tsx](examples/task-check.tsx)); enter and exit are layout animations with
   `reducedEntering()` ([examples/task-list-row.tsx](examples/task-list-row.tsx)); a drag springs
   with the finger's velocity ([examples/task-sheet.tsx](examples/task-sheet.tsx)); a loop uses
   `useMotionLoop` ([examples/sync-badge.tsx](examples/sync-badge.tsx)); a celebration uses
   `useCelebration` and `<CelebrationBurst>` from the committing handler
   ([examples/task-check-cell.tsx](examples/task-check-cell.tsx)); a CSS transition carries the
   condition on its key ([examples/due-chip.tsx](examples/due-chip.tsx)); a fill that must animate
   its width carries the marker ([examples/progress-bar.tsx](examples/progress-bar.tsx)); a safety
   timer is a functional moment ([examples/hold-to-delete.tsx](examples/hold-to-delete.tsx)).
6. **Test it.** Read [references/testing-motion.md](references/testing-motion.md): fake timers,
   the `<MotionConfig preference>` wrapper, the end state with motion reduced, an interruption, a
   loop's stop conditions, a celebration's once-per-event rule.
7. **Run the checks** from the repo root and loop until each passes: `npm run typecheck`,

From zakariaf/ReactNative-Skills, MIT.

More from zakariaf/ReactNative-Skills

Signals

Stars
11
Forks
2
Open issues
0
Last commit (this folder)
Oct 17, 2026 (12 days ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from zakariaf

Cite this page

motion-and-animation skill. Agent Mart. https://agentmart.dev/skills/zakariaf-motion-and-animation (last updated 2026-10-24)