Motion design rules for the Agenta mobile app (web/mobile) — the shared presets in src/lib/motion, when to animate, and reduced-motion requirements. Use when adding any animation or transition under web/mobile, animating navigation, sheets, skeletons, or list/chat surfaces.
JS-driven animation in web/mobile uses the motion package through the shared
presets module src/lib/motion/presets.ts. Components never define their own
durations, easings, or springs.
Consume via the hook (reduced-motion aware — this is mandatory):
import {AnimatePresence, motion} from "motion/react"
import {useMotionPresets} from "@/lib/motion/presets"
const presets = useMotionPresets()
<AnimatePresence initial={false}>
{open ? (
<motion.div key="panel" variants={presets.crossfade} initial="initial" animate="animate" exit="exit" />
) : null}
</AnimatePresence>In use today:
crossfade — the only preset components consume. Used for the reply
reveal (features/chat/AnswerReveal.tsx) and the composer's picker and
recording overlays (features/chat/Composer.tsx). For skeleton → content
swaps, skeleton and content must occupy identical geometry so the fade causes
zero layout shift.Defined but not used by any screen yet:
sharedAxisPush — horizontal parent → child push. custom={1} forward,
custom={-1} back, inside <AnimatePresence custom={direction} initial={false}>.sheetSlideUp — spring-based slide-up for a custom bottom sheet.pushTransition / sheetTransition / crossfadeTransition values
and the reduced flag.Reach for these before adding a new preset. Screen navigation has no transition
today, and sheets and drawers (Sheet from @agenta/ui/ui or the local
src/components/ui/sheet.tsx) animate with their own Tailwind
animate-in/slide-in-* classes, not with motion.
transform/opacity only).useMotionPresets() returns instant
variants and zero-duration transitions when prefers-reduced-motion is set.
Any animation built outside the presets module must justify itself in review
AND handle reduced motion itself. CSS keyframe animations need a
@media (prefers-reduced-motion: reduce) override in globals.css (see
.animate-composer-ring). Prefer extending the presets module.crossfade;
text streaming is never per-character animated.presets.ts (one exported preset + doc comment,
plus its reduced variant in useMotionPresets), not into a component file.7a975fb
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.