Communitygithub.com

motion-advanced

Patrones de animación avanzados para React / Next.js — arrastrar y soltar, gestos, animaciones de texto, dibujo de rutas SVG, hooks personalizados, secuencias imperativas (useAnimate), cargadores y el árbol de decisión completo de la API. Requiere motion-foundations. Úsalo al construir arrastrar y soltar, gestos, animaciones de texto o SVG, o secuencias de animación imperativas en React o Next.js.

¿Qué es motion-advanced?

Este skill permite implementar patrones de animación complejos e interactivos en React y Next.js, basados en física y gestos. Incluye interacciones de arrastre (tarjetas, paneles que se descartan deslizando, listas reordenables), detección de gestos (deslizamiento, pulsación larga), animaciones de texto (palabra por palabra, carácter por carácter, contador numérico), animaciones SVG (dibujo de rutas, morphing de iconos, anillos de progreso) y hooks personalizados como useScrollReveal o seguidor de cursor. Proporciona secuencias imperativas con useAnimate, que son seguras ante interrupciones, y componentes de loader como spinners, shimmers y estados de carga en botones. El skill enfatiza el uso de valores de movimiento (useMotionValue, useTransform) para cálculos reactivos sin re-renderizados, y sigue reglas estrictas como pausar animaciones infinitas cuando la pestaña está oculta, limpiar event listeners en hooks personalizados y asegurar que los valores de movimiento no se recrean en cada render. Soporta tanto APIs declarativas (transiciones con spring) como imperativas (animate()), y requiere que motion-foundations esté configurado primero.

Compatible con~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/motion-advanced

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Motion Avanzado

Patrones de animación complejos, interactivos y basados en física. Requiere que motion-foundations esté configurado primero. Úsalos cuando motion-patterns no sea suficiente.

Cuándo Activar

  • Construir paneles que se descartan deslizando hacia abajo (drag-to-dismiss), gestos de deslizamiento o listas reordenables
  • Animar texto palabra por palabra, carácter por carácter, o como un contador en vivo
  • Dibujar rutas SVG, hacer morphing de iconos o animar progreso circular
  • Escribir un hook de animación personalizado (useScrollReveal, botón magnético, seguidor de cursor)
  • Secuenciar animaciones de varios pasos imperativamente con useAnimate
  • Construir spinners, esqueletos shimmer, indicadores de pulso o estados de carga en botones

Salidas

Este skill produce:

  • Interacciones de arrastre: tarjetas arrastrables, paneles que se descartan deslizando, listas Reorder.Group
  • Hooks de gestos: detección de deslizamiento, pulsación larga, contorno de pellizco
  • Componentes de animación de texto: revelación de palabras, máquina de escribir, contador numérico
  • Animación SVG: dibujo de rutas, morphing de iconos, anillo de progreso de trazo
  • Hooks personalizados: useScrollReveal, useHoverScale, useNavigationDirection, useInViewOnce
  • Secuencias imperativas mediante useAnimate con async/await seguro ante interrupciones
  • Componentes de loader: spinner, shimmer, punto de pulso, barra de progreso, estado de carga de botón

Principios

  • El movimiento basado en física (useSpring, springs.*) siempre se siente más natural que el basado en duración para manipulación directa.
  • useMotionValue + useTransform calcula valores derivados sin provocar re-renderizados.
  • Las secuencias de useAnimate son imperativas y seguras ante interrupciones — llamar a animate() a mitad de camino cancela la animación anterior automáticamente.
  • Los valores de movimiento (useMotionValue, useSpring) son seguros para SSR y no causan errores de hidratación.

Reglas

  1. Las interacciones de arrastre deben probarse en dispositivos táctiles, no solo con el ratón. La prop drag funciona en ambos, pero la sensación y el umbral difieren.
  2. Las animaciones infinitas deben pausarse cuando document.visibilityState === "hidden". Las pestañas en segundo plano no deben consumir GPU/CPU.
  3. El umbral de deslizamiento debe ser explícito. Nunca inferir la intención solo de la velocidad; combinar comprobaciones de offset + velocity.
  4. El ref de ámbito de useAnimate debe estar adjunto a un elemento DOM montado. Llamar a animate() antes del montaje falla silenciosamente.
  5. Los valores de movimiento no deben recrearse en cada render. useMotionValue(0) dentro del cuerpo de un componente es correcto; new MotionValue(0) en un render no lo es.
  6. Todos los valores de token se importan de motion-foundations. Sin números inline.
  7. Los hooks personalizados deben manejar la limpieza. Cada window.addEventListener necesita un removeEventListener correspondiente en el retorno del useEffect.
  8. El morphing SVG requiere recuentos iguales de comandos de ruta. Las rutas con diferentes estructuras de comando saltan en lugar de interpolar.

Guía de Decisión

Elegir la API avanzada correcta

EscenarioAPI
Arrastre con física al soltardrag + dragTransition: springs.release
Lista ordenada arrastrando para reordenarReorder.Group + Reorder.Item
Descartar al arrastrardrag="y" + comprobación de offset en onDragEnd
Deslizar izquierda/derechadrag="x" + comprobación de offset en onDragEnd
Pulsación largaHook useLongPress
Valor suavizado en el tiempouseSpring
Valor derivado de otrouseTransform
Secuencia de varios pasosuseAnimate con async/await
Animación imperativa de un solo usoanimate() de motion
Texto que entra palabra por palabraEscalonamiento en spans inline-block
Dibujado de SVGpathLength 0 → 1
Morphing SVGInterpolación del atributo d (comandos iguales)
Progreso circularInterpolación de strokeDashoffset

Cuándo usar useSpring vs una transición spring

useSpringtransition: springs.*
Úsalo paraSeguidor de cursor, valores seguidos por el punteroCambios de estado discretos
ActualizacionesContinuas, en cada frameDesencadenadas por cambio de estado
InterrupciónSuave — la física retoma desde la velocidadSe reinicia desde el valor actual

Conceptos Clave

useMotionValue + useTransform

Cómputo reactivo sin re-renderizados:

const x = useMotionValue(0)
const opacity = useTransform(x, [-200, 0, 200], [0, 1, 0])
// la opacidad se actualiza en cada frame a medida que x cambia — sin setState, sin re-render

useAnimate

Devuelve [scope, animate]. El ref de ámbito debe estar adjunto a un elemento DOM. Las llamadas a animate() son seguras ante interrupciones — llamar a mitad de camino cancela la ejecución anterior.

const [scope, animate] = useAnimate()

async function play() {
  await animate(".step-1", { opacity: 1 }, { duration: 0.3 })
  await animate(".step-2", { x: 0 },       { duration: 0.4 })
        animate(".step-3", { scale: 1 },    { duration: 0.25 })  // dispara y olvida
}

return <div ref={scope}>...</div>

Ejemplos de Código

Tarjeta arrastrable

"use client"
import { motion } from "motion/react"
import { springs, motionTokens } from "@/lib/motion-tokens"

<motion.div
  drag
  dragConstraints={{ left: -100, right: 100, top: -100, bottom: 100 }}
  dragElastic={0.1}
  whileDrag={{
    scale: motionTokens.scale.pop,
    boxShadow: "0 16px 40px rgba(0,0,0,0.2)",
  }}
  dragTransition={springs.release}
/>

Panel que se descarta deslizando

"use client"
import { motion, useMotionValue, useTransform } from "motion/react"

export function BottomSheet({ onClose }: { onClose: () => void }) {
  const y = useMotionValue(0)
  const opacity = useTransform(y, [0, 200], [1, 0])

  return (
    <motion.div
      drag="y"
      dragConstraints={{ top: 0 }}
      style={{ y, opacity }}
      onDragEnd={(_, info) => {
        // Regla 3: combinar offset + velocity
        if (info.offset.y > 120 || info.velocity.y > 500) onClose()
      }}
    />
  )
}

Lista reordenable

"use client"
import { Reorder } from "motion/react"

export function SortableList() {
  const [items, setItems] = useState(initialItems)
  return (
    <Reorder.Group axis="y" values={items} onReorder={setItems}>
      {items.map((item) => (
        <Reorder.Item key={item.id} value={item}>
          {item.label}
        </Reorder.Item>
      ))}
    </Reorder.Group>
  )
}

Detección de deslizamiento

"use client"
import { motion } from "motion/react"

const OFFSET_THRESHOLD  = 50
const VELOCITY_THRESHOLD = 300

<motion.div
  drag="x"
  dragConstraints={{ left: 0, right: 0 }}
  onDragEnd={(_, info) => {
    const swipedRight = info.offset.x > OFFSET_THRESHOLD  || info.velocity.x > VELOCITY_THRESHOLD
    const swipedLeft  = info.offset.x < -OFFSET_THRESHOLD || info.velocity.x < -VELOCITY_THRESHOLD
    if (swipedRight) onSwipeRight()
    if (swipedLeft)  onSwipeLeft()
  }}
/>

Hook de pulsación larga

import { useRef } from "react"

export function useLongPress(callback: () => void, ms = 600) {
  const timerRef = useRef<ReturnType<typeof setTimeout>>()
  return {
    onPointerDown:  () => { timerRef.current = setTimeout(callback, ms) },
    onPointerUp:    () => clearTimeout(timerRef.current),
    onPointerLeave: () => clearTimeout(timerRef.current),
  }
}

Revelación palabra por palabra

"use client"
import { motion } from "motion/react"
import { springs } from "@/lib/motion-tokens"

export function AnimatedText({ text }: { text: string }) {
  return (
    <motion.p
      variants={{ visible: { transition: { staggerChildren: 0.05 } } }}
      initial="hidden"
      animate="visible"
    >
      {text.split(" ").map((word, i) => (
        <motion.span
          key={i}
          className="inline-block mr-1"
          variants={{
            hidden:  { opacity: 0, y: 12 },
            visible: { opacity: 1, y: 0, transition: springs.gentle },
          }}
        >
          {word}
        </motion.span>
      ))}
    </motion.p>
  )
}

Contador numérico

"use client"
import { useRef, useEffect } from "react"
import { animate } from "motion"
import { motionTokens } from "@/lib/motion-tokens"

export function Counter({ to }: { to: number }) {
  const nodeRef = useRef<HTMLSpanElement>(null)

  useEffect(() => {
    const controls = animate(0, to, {
      duration: motionTokens.duration.crawl,
      ease: motionTokens.easing.smooth,
      onUpdate: (v) => {
        if (nodeRef.current) nodeRef.current.textContent = Math.round(v).toString()
      },
    })
    return controls.stop   // Regla 7: limpieza
  }, [to])

  return <span ref={nodeRef} />
}

Dibujado de ruta SVG

"use client"
import { motion } from "motion/react"
import { motionTokens } from "@/lib/motion-tokens"

<motion.path
  d="M 0 100 Q 50 0 100 100"
  initial={{ pathLength: 0, opacity: 0 }}
  animate={{ pathLength: 1, opacity: 1 }}
  transition={{ duration: motionTokens.duration.slow, ease: motionTokens.easing.smooth }}
/>

Anillo de progreso de trazo

"use client"
import { motion } from "motion/react"
import { motionTokens } from "@/lib/motion-tokens"

const CIRCUMFERENCE = 2 * Math.PI * 40   // r=40

export function ProgressRing({ progress }: { progress: number }) {
  return (
    <svg width="100" height="100" viewBox="0 0 100 100">
      <circle cx="50" cy="50" r="40" fill="none" stroke="#e5e7eb" strokeWidth="8" />
      <motion.circle
        cx="50" cy="50" r="40"
        fill="none" stroke="#6366f1" strokeWidth="8"
        strokeLinecap="round"
        strokeDasharray={CIRCUMFERENCE}
        animate={{ strokeDashoffset: CIRCUMFERENCE - (progress / 100) * CIRCUMFERENCE }}
        transition={{ duration: motionTokens.duration.normal, ease: motionTokens.easing.smooth }}
        style={{ rotate: -90, transformOrigin: "center" }}
      />
    </svg>
  )
}

Hook useScrollReveal

"use client"
import { useRef } from "react"
import { useScroll, useTransform } from "motion/react"
import { motionTokens } from "@/lib/motion-tokens"

export function useScrollReveal() {
  const ref = useRef(null)
  const { scrollYProgress } = useScroll({ target: ref, offset: ["start end", "end start"] })
  const opacity = useTransform(scrollYProgress, [0, 0.3], [0, 1])
  const y       = useTransform(scrollYProgress, [0, 0.3], [motionTokens.distance.lg, 0])
  return { ref, style: { opacity, y } }
}

// Uso
const { ref, style } = useScrollReveal()
<motion.section ref={ref} style={style} />

Seguidor de cursor

"use client"
import { useEffect } from "react"
import { motion, useMotionValue, useSpring } from "motion/react"
import { springs } from "@/lib/motion-tokens"

export function CursorFollower() {
  const x = useMotionValue(-100)
  const y = useMotionValue(-100)
  const sx = useSpring(x, springs.gentle)
  const sy = useSpring(y, springs.gentle)

  useEffect(() => {
    const move = (e: MouseEvent) => { x.set(e.clientX); y.set(e.clientY) }
    window.addEventListener("mousemove", move)
    return () => window.removeEventListener("mousemove", move)   // Regla 7
  }, [])

  return (
    <motion.div
      className="fixed top-0 left-0 w-6 h-6 rounded-full bg-indigo-500
                 pointer-events-none -translate-x-1/2 -translate-y-1/2 z-50"
      style={{ x: sx, y: sy }}
    />
  )
}

Esqueleto shimmer

"use client"
import { useEffect } from "react"
import { motion, useAnimation } from "motion/react"
import { motionTokens } from "@/lib/motion-tokens"

export function ShimmerSkeleton({ className = "" }: { className?: string }) {
  const controls = useAnimation()

  useEffect(() => {
    const play = () =>
      controls.start({
        x: ["-100%", "100%"],
        transition: {
          repeat: Infinity,
          duration: motionTokens.duration.crawl,
          ease: motionTokens.easing.linear,
        },
      })

    const handleVisibility = () => {
      if (document.visibilityState === "hidden") controls.stop()
      else void play()
    }

    void play()
    document.addEventListener("visibilitychange", handleVisibility)
    return () => {
      controls.stop()
      document.removeEventListener("visibilitychange", handleVisibility)
    }
  }, [controls])

  return (
    <div className={`relative overflow-hidden bg-gray-200 rounded ${className}`}>
      <motion.div
        className="absolute inset-0 bg-gradient-to-r from-transparent via-white/60 to-transparent"
        initial={{ x: "-100%" }}
        animate={controls}
      />
    </div>
  )
}

Estado de carga de botón

"use client"
import { motion, AnimatePresence } from "motion/react"
import { motionTokens, springs } from "@/lib/motion-tokens"

export function LoadingButton({
  loading,
  label,
  onClick,
}: {
  loading: boolean
  label: string
  onClick: () => void
}) {
  return (
    <motion.button
      onClick={onClick}
      animate={{ opacity: loading ? 0.7 : 1 }}
      whileTap={loading ? {} : { scale: motionTokens.scale.press }}
      transition={springs.snappy}
      disabled={loading}
    >
      <AnimatePresence mode="wait">
        {loading ? (
          <motion.span
            key="loading"
            initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
            transition={{ duration: motionTokens.duration.fast }}
          >
            …
          </motion.span>
        ) : (
          <motion.span
            key="label"
            initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
            transition={{ duration: motionTokens.duration.fast }}
          >
            {label}
          </motion.span>
        )}
      </AnimatePresence>
    </motion.button>
  )
}

Animación infinita con pausa por visibilidad

"use client"
import { useEffect } from "react"
import { motion, useAnimation } from "motion/react"
import { motionTokens } from "@/lib/motion-tokens"

export function PulseDot() {
  const controls = useAnimation()

  useEffect(() => {
    const pulse = () =>
      controls.start({
        scale: [1, 1.4, 1],
        opacity: [1, 0.6, 1],
        transition: { repeat: Infinity, duration: motionTokens.duration.crawl },
      })

    // Regla 2: pausar cuando la pestaña está oculta
    const handleVisibility = () => {
      if (document.visibilityState === "hidden") controls.stop()
      else void pulse()
    }

    void pulse()
    document.addEventListener("visibilitychange", handleVisibility)
    return () => {
      controls.stop()
      document.removeEventListener("visibilitychange", handleVisibility)
    }
  }, [controls])

  return (
    <motion.span
      animate={controls}
      className="inline-block w-2 h-2 bg-indigo-500 rounded-full"
    />
  )
}

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Skills relacionados