motion-and-animation
Catalogs each animation as a Reanimated 4 moment.
Skill by zakariaf
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.
About
Use cases: spreadsheets, design
Reviews
Write a reviewNo 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
- 0.606score
debug-menu-and-test-hooks
zakariaf
Gates test-only code behind one literal TEST_ONLY switch: debug menu, debug link, simulated clock and offline.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
adaptive-layout
zakariaf
Lays screens out by window, not device: breakpoints, master-detail panes, readable widths, grids.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
admob-ads-and-consent
zakariaf
Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
analytics-and-consent
zakariaf
Gates analytics on opt-in consent: no SDK before a yes, typed events without personal data, ATT.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
android-release-play
zakariaf
Ships Android App Bundles to Google Play: upload key, Play App Signing, AAB gate, versionCode, tracks, rollout.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
architecture-and-boundaries
zakariaf
Places app code in the src/ layout and checks import boundaries: one-way zones, pure core, ports with fakes, one composition root.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
async-safety
zakariaf
Prevents silent async bugs: effect races, stale results, lost promises, leaked listeners and timers, AbortSignal gaps.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
audio-and-haptics-feedback
zakariaf
Builds SFX and haptics behind ports.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
auth-and-secure-storage
zakariaf
Builds sign-in - tokens in expo-secure-store, shared refresh, protected routes, OAuth PKCE, Apple sign-in.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
background-tasks
zakariaf
Builds expo-background-task jobs: idempotent, budgeted runs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
canvas-gestures-and-hit-testing
zakariaf
Turns canvas taps, swipes, drags and aim into typed intents: one gesture module, hit-testing on the drawn layout.
SkillAFreshness: A★ 11Claude Code +9
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
- Repository
- github.com/zakariaf/ReactNative-Skills
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from zakariaf
- 0.606score
admob-ads-and-consent
zakariaf
Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
canvas-gestures-and-hit-testing
zakariaf
Turns canvas taps, swipes, drags and aim into typed intents: one gesture module, hit-testing on the drawn layout.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
adaptive-layout
zakariaf
Lays screens out by window, not device: breakpoints, master-detail panes, readable widths, grids.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
debug-menu-and-test-hooks
zakariaf
Gates test-only code behind one literal TEST_ONLY switch: debug menu, debug link, simulated clock and offline.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
analytics-and-consent
zakariaf
Gates analytics on opt-in consent: no SDK before a yes, typed events without personal data, ATT.
SkillAFreshness: A★ 11Claude Code +9
Cite this page
motion-and-animation skill. Agent Mart. https://agentmart.dev/skills/zakariaf-motion-and-animation (last updated 2026-10-24)