Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Implement smooth, accessible animations with motion/react (Framer Motion) in React. Use when adding animations, gestures, or layout transitions with motion, AnimatePresence, useScroll, and related APIs. Requires a 'use client' boundary; not for strictly server-rendered components or when no animation is needed.
.claude/skills/asymmetric-al-motion/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-20 | ✗→✓ | ▲ Improved | 36% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 10% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 15% | 0% |
| case-06 | ✗→✓ | ▲ Improved | -14% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 14% | 0% |
Name: motion Purpose: Implement smooth, accessible animations with motion/react in React. Use this skill whenever adding animations, gestures, or layout transitions.
Applies when: Using motion/react (motion, AnimatePresence, useScroll, etc.). Do not use when: No animations are needed or the component is strictly server-rendered.
'use client'; keep client islands small.AnimatePresence for enter/exit, layout for layout, whileHover/whileTap for gestures.useReducedMotion().LazyMotion for larger motion surfaces.'use client' boundary.'use client' only where neededAnimatePresence wraps conditional UIlayouttsx"use client"; import { AnimatePresence, motion } from "motion/react"; export function Toast({ open, children, }: { open: boolean; children: React.ReactNode; }) { return ( <AnimatePresence> {open ? ( <motion.div initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: 8 }} transition={{ duration: 0.18 }} > {children} </motion.div> ) : null} </AnimatePresence> ); }
tsx"use client"; import { motion } from "motion/react"; export function Expander({ open }: { open: boolean }) { return ( <motion.div layout className="overflow-hidden rounded-md border p-3"> <div className="font-medium">Title</div> {open ? <div className="mt-2 text-sm">More content...</div> : null} </motion.div> ); }
tsx"use client"; import { motion, useReducedMotion } from "motion/react"; export function FadeIn({ children }: { children: React.ReactNode }) { const reduce = useReducedMotion(); return ( <motion.div initial={reduce ? false : { opacity: 0, y: 6 }} animate={reduce ? { opacity: 1 } : { opacity: 1, y: 0 }} transition={{ duration: 0.2 }} > {children} </motion.div> ); }
AnimatePresence (exit animations never run)layout| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→pass | 20,659 | 7,700 | -63% | 1 | 1 | 0% | 4,345 | 2,413 | -44% | 0 | 0 | — |
case-02 | pass→pass | 16,338 | 9,066 | -45% | 1 | 1 | 0% | 3,490 | 2,697 | -23% | 0 | 0 | — |
case-20 | fail→pass | 11,481 | 11,667 | +2% | 1 | 1 | 0% | 2,330 | 3,180 | +36% | 0 | 0 | — |
case-03 | pass→pass | 11,255 | 9,521 | -15% | 1 | 1 | 0% | 2,161 | 2,713 | +26% | 0 | 0 | — |
case-04 | fail→pass | 17,256 | 13,669 | -21% | 1 | 1 | 0% | 3,525 | 3,868 | +10% | 0 | 0 | — |
case-05 | fail→pass | 15,267 | 11,713 | -23% | 1 | 1 | 0% | 2,924 | 3,369 | +15% | 0 | 0 | — |
case-06 | fail→pass | 22,733 | 14,632 | -36% | 1 | 1 | 0% | 4,247 | 3,641 | -14% | 0 | 0 | — |
case-07 | fail→pass | 15,011 | 11,821 | -21% | 1 | 1 | 0% | 2,935 | 3,335 | +14% | 0 | 0 | — |
case-13 | fail→pass | 17,605 | 13,366 | -24% | 1 | 1 | 0% | 3,447 | 3,740 | +9% | 0 | 0 | — |
case-08 | fail→pass | 11,374 | 7,018 | -38% | 1 | 1 | 0% | 2,185 | 2,190 | +0% | 0 | 0 | — |
case-09 | fail→pass | 17,556 | 9,181 | -48% | 1 | 1 | 0% | 3,107 | 2,601 | -16% | 0 | 0 | — |
case-10 | fail→pass | 16,152 | 10,087 | -38% | 1 | 1 | 0% | 3,185 | 2,703 | -15% | 0 | 0 | — |
case-11 | fail→pass | 19,515 | 14,242 | -27% | 1 | 1 | 0% | 3,667 | 4,035 | +10% | 0 | 0 | — |
case-12 | fail→pass | 16,901 | 5,943 | -65% | 1 | 1 | 0% | 3,255 | 1,917 | -41% | 0 | 0 | — |
case-14 | fail→pass | 13,787 | 11,198 | -19% | 1 | 1 | 0% | 2,671 | 3,084 | +15% | 0 | 0 | — |
case-15 | fail→pass | 16,219 | 11,861 | -27% | 1 | 1 | 0% | 3,481 | 3,573 | +3% | 0 | 0 | — |
case-16 | fail→pass | 16,107 | 13,622 | -15% | 1 | 1 | 0% | 3,177 | 3,773 | +19% | 0 | 0 | — |
case-17 | fail→pass | 14,788 | 11,523 | -22% | 1 | 1 | 0% | 2,830 | 3,124 | +10% | 0 | 0 | — |
case-18 | fail→pass | 14,968 | 6,746 | -55% | 1 | 1 | 0% | 2,884 | 2,167 | -25% | 0 | 0 | — |
case-19 | fail→pass | 16,477 | 10,039 | -39% | 1 | 1 | 0% | 3,347 | 2,908 | -13% | 0 | 0 | — |
case-21 | fail→pass | 13,942 | 11,493 | -18% | 1 | 1 | 0% | 2,790 | 3,275 | +17% | 0 | 0 | — |
case-22 | fail→pass | 12,587 | 11,652 | -7% | 1 | 1 | 0% | 2,489 | 3,206 | +29% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +86 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.