Claude Skill

css-animation-creator

Create professional CSS animations, transitions, micro-interactions, and complex motion design. Use when adding animations, hover effects, loading states, page transitions, scroll animations, or any motion design work.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download onewave-ai-claude-skills-css-animation-creator-97b5147.zip · 11 KB
Part of onewave-ai/claude-skills — 67 skills

Install

skills CLI npx skills add https://github.com/OneWave-AI/claude-skills/tree/main/css-animation-creator
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install onewave-ai-claude-skills@llmmart
Git git clone https://github.com/OneWave-AI/claude-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole onewave-ai/claude-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

CSS Animation Creator

Build production-grade, accessible, GPU-efficient motion for web UIs using CSS, Tailwind, and Framer Motion.

Workflow

  1. Identify the purpose of the motion: feedback, delight, guidance, or storytelling.
  2. Choose the technique: CSS transitions for state changes, keyframes for looping or multi-step motion, Framer Motion or JS for orchestration and scroll-linked effects.
  3. Set timing and easing to match the interaction. Apply the timing and easing tables in references/principles-and-timing.md.
  4. Author the animation. Pull ready-made keyframes and patterns from the reference files below rather than writing from scratch.
  5. Constrain animated properties to transform and opacity for GPU acceleration. Apply references/performance.md.
  6. Honor reduced-motion preferences for every animation. Apply references/accessibility.md.
  7. Verify on low-end devices and confirm no layout thrash before shipping.

Contents

  • references/principles-and-timing.md — Disney 12 principles mapped to UI, duration and easing tables.
  • references/transitions.md — Transition syntax, easing functions, cubic-bezier presets, Tailwind transitions.
  • references/keyframe-library.md — Keyframe syntax plus fade, scale, bounce, rotate, slide, and attention-seeker keyframes.
  • references/loading-animations.md — Spinners, dots, skeleton loaders, shimmer, progress bars.
  • references/micro-interactions.md — Button, hover, icon, form, and success/error interactions.
  • references/page-transitions.md — View Transitions API, Framer Motion page and shared-layout transitions, staggered lists.
  • references/scroll-animations.md — Intersection Observer, Framer Motion scroll hooks, native CSS scroll-driven animations.
  • references/accessibility.md — prefers-reduced-motion CSS, Tailwind motion-safe/reduce, React hook.
  • references/tailwind-config.md — Drop-in tailwind.config.js animation and keyframe definitions.
  • references/performance.md — GPU-accelerated properties, will-change, contain, performance checklist.
Files (claude-skills)
  • references
    • accessibility.md 1.4 KB
      # Accessibility: Reduced Motion
      
      ```css
      /* Global reduced motion */
      @media (prefers-reduced-motion: reduce) {
        *,
        *::before,
        *::after {
          animation-duration: 0.01ms !important;
          animation-iteration-count: 1 !important;
          transition-duration: 0.01ms !important;
          scroll-behavior: auto !important;
        }
      }
      
      /* Per-element control */
      .animated-element {
        animation: bounce 1s infinite;
      }
      
      @media (prefers-reduced-motion: reduce) {
        .animated-element {
          animation: none;
        }
      }
      ```
      
      ```tsx
      // Tailwind motion-safe/motion-reduce
      <div className="motion-safe:animate-bounce motion-reduce:animate-none">
        Respects preferences
      </div>
      
      // React hook
      function usePrefersReducedMotion() {
        const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
      
        useEffect(() => {
          const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
          setPrefersReducedMotion(mediaQuery.matches);
      
          const handler = (e) => setPrefersReducedMotion(e.matches);
          mediaQuery.addEventListener('change', handler);
          return () => mediaQuery.removeEventListener('change', handler);
        }, []);
      
        return prefersReducedMotion;
      }
      
      // Usage
      function AnimatedComponent() {
        const prefersReducedMotion = usePrefersReducedMotion();
      
        return (
          <motion.div
            animate={{ x: 100 }}
            transition={{
              duration: prefersReducedMotion ? 0 : 0.3
            }}
          />
        );
      }
      ```
      
    • keyframe-library.md 6.6 KB
      # Keyframe Animations
      
      ## Basic Syntax
      
      ```css
      @keyframes animationName {
        0% { /* starting state */ }
        50% { /* midpoint state */ }
        100% { /* ending state */ }
      }
      
      .element {
        animation: animationName 1s ease-in-out infinite;
        /* name | duration | timing | iteration-count */
      
        /* Full syntax */
        animation-name: animationName;
        animation-duration: 1s;
        animation-timing-function: ease-in-out;
        animation-delay: 0s;
        animation-iteration-count: infinite; /* or number */
        animation-direction: normal; /* reverse, alternate, alternate-reverse */
        animation-fill-mode: forwards; /* none, forwards, backwards, both */
        animation-play-state: running; /* paused */
      }
      ```
      
      ## Fade Animations
      
      ```css
      /* Fade In */
      @keyframes fadeIn {
        from { opacity: 0; }
        to { opacity: 1; }
      }
      
      /* Fade In Up */
      @keyframes fadeInUp {
        from {
          opacity: 0;
          transform: translateY(20px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }
      
      /* Fade In Down */
      @keyframes fadeInDown {
        from {
          opacity: 0;
          transform: translateY(-20px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }
      
      /* Fade In Left */
      @keyframes fadeInLeft {
        from {
          opacity: 0;
          transform: translateX(-20px);
        }
        to {
          opacity: 1;
          transform: translateX(0);
        }
      }
      
      /* Fade In Right */
      @keyframes fadeInRight {
        from {
          opacity: 0;
          transform: translateX(20px);
        }
        to {
          opacity: 1;
          transform: translateX(0);
        }
      }
      
      /* Fade In Scale */
      @keyframes fadeInScale {
        from {
          opacity: 0;
          transform: scale(0.9);
        }
        to {
          opacity: 1;
          transform: scale(1);
        }
      }
      
      /* Fade Out */
      @keyframes fadeOut {
        from { opacity: 1; }
        to { opacity: 0; }
      }
      ```
      
      ## Scale Animations
      
      ```css
      /* Scale In */
      @keyframes scaleIn {
        from { transform: scale(0); }
        to { transform: scale(1); }
      }
      
      /* Scale In Bounce */
      @keyframes scaleInBounce {
        0% { transform: scale(0); }
        50% { transform: scale(1.1); }
        100% { transform: scale(1); }
      }
      
      /* Pop */
      @keyframes pop {
        0% { transform: scale(1); }
        50% { transform: scale(1.1); }
        100% { transform: scale(1); }
      }
      
      /* Pulse */
      @keyframes pulse {
        0%, 100% { transform: scale(1); }
        50% { transform: scale(1.05); }
      }
      
      /* Heartbeat */
      @keyframes heartbeat {
        0%, 100% { transform: scale(1); }
        14% { transform: scale(1.3); }
        28% { transform: scale(1); }
        42% { transform: scale(1.3); }
        70% { transform: scale(1); }
      }
      ```
      
      ## Bounce Animations
      
      ```css
      /* Bounce */
      @keyframes bounce {
        0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
        40% { transform: translateY(-20px); }
        60% { transform: translateY(-10px); }
      }
      
      /* Bounce In */
      @keyframes bounceIn {
        0% {
          opacity: 0;
          transform: scale(0.3);
        }
        50% {
          opacity: 1;
          transform: scale(1.05);
        }
        70% { transform: scale(0.9); }
        100% { transform: scale(1); }
      }
      
      /* Bounce In Down */
      @keyframes bounceInDown {
        0% {
          opacity: 0;
          transform: translateY(-100px);
        }
        60% {
          opacity: 1;
          transform: translateY(20px);
        }
        80% { transform: translateY(-10px); }
        100% { transform: translateY(0); }
      }
      
      /* Rubber Band */
      @keyframes rubberBand {
        0% { transform: scaleX(1); }
        30% { transform: scaleX(1.25) scaleY(0.75); }
        40% { transform: scaleX(0.75) scaleY(1.25); }
        50% { transform: scaleX(1.15) scaleY(0.85); }
        65% { transform: scaleX(0.95) scaleY(1.05); }
        75% { transform: scaleX(1.05) scaleY(0.95); }
        100% { transform: scaleX(1) scaleY(1); }
      }
      ```
      
      ## Rotate Animations
      
      ```css
      /* Spin */
      @keyframes spin {
        from { transform: rotate(0deg); }
        to { transform: rotate(360deg); }
      }
      
      /* Spin Reverse */
      @keyframes spinReverse {
        from { transform: rotate(360deg); }
        to { transform: rotate(0deg); }
      }
      
      /* Swing */
      @keyframes swing {
        20% { transform: rotate(15deg); }
        40% { transform: rotate(-10deg); }
        60% { transform: rotate(5deg); }
        80% { transform: rotate(-5deg); }
        100% { transform: rotate(0deg); }
      }
      
      /* Wobble */
      @keyframes wobble {
        0% { transform: translateX(0); }
        15% { transform: translateX(-15px) rotate(-5deg); }
        30% { transform: translateX(12px) rotate(3deg); }
        45% { transform: translateX(-9px) rotate(-3deg); }
        60% { transform: translateX(6px) rotate(2deg); }
        75% { transform: translateX(-3px) rotate(-1deg); }
        100% { transform: translateX(0); }
      }
      
      /* Flip */
      @keyframes flipX {
        0% { transform: perspective(400px) rotateX(90deg); opacity: 0; }
        40% { transform: perspective(400px) rotateX(-20deg); }
        60% { transform: perspective(400px) rotateX(10deg); opacity: 1; }
        80% { transform: perspective(400px) rotateX(-5deg); }
        100% { transform: perspective(400px) rotateX(0deg); }
      }
      ```
      
      ## Slide Animations
      
      ```css
      /* Slide In Up */
      @keyframes slideInUp {
        from {
          transform: translateY(100%);
          visibility: visible;
        }
        to { transform: translateY(0); }
      }
      
      /* Slide In Down */
      @keyframes slideInDown {
        from {
          transform: translateY(-100%);
          visibility: visible;
        }
        to { transform: translateY(0); }
      }
      
      /* Slide In Left */
      @keyframes slideInLeft {
        from {
          transform: translateX(-100%);
          visibility: visible;
        }
        to { transform: translateX(0); }
      }
      
      /* Slide In Right */
      @keyframes slideInRight {
        from {
          transform: translateX(100%);
          visibility: visible;
        }
        to { transform: translateX(0); }
      }
      
      /* Slide Out */
      @keyframes slideOutUp {
        from { transform: translateY(0); }
        to {
          transform: translateY(-100%);
          visibility: hidden;
        }
      }
      ```
      
      ## Attention Seekers
      
      ```css
      /* Shake */
      @keyframes shake {
        0%, 100% { transform: translateX(0); }
        10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
        20%, 40%, 60%, 80% { transform: translateX(5px); }
      }
      
      /* Shake Horizontal (stronger) */
      @keyframes shakeX {
        0%, 100% { transform: translateX(0); }
        10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); }
        20%, 40%, 60%, 80% { transform: translateX(10px); }
      }
      
      /* Jello */
      @keyframes jello {
        0%, 11.1%, 100% { transform: none; }
        22.2% { transform: skewX(-12.5deg) skewY(-12.5deg); }
        33.3% { transform: skewX(6.25deg) skewY(6.25deg); }
        44.4% { transform: skewX(-3.125deg) skewY(-3.125deg); }
        55.5% { transform: skewX(1.5625deg) skewY(1.5625deg); }
        66.6% { transform: skewX(-0.78125deg) skewY(-0.78125deg); }
        77.7% { transform: skewX(0.390625deg) skewY(0.390625deg); }
        88.8% { transform: skewX(-0.1953125deg) skewY(-0.1953125deg); }
      }
      
      /* Flash */
      @keyframes flash {
        0%, 50%, 100% { opacity: 1; }
        25%, 75% { opacity: 0; }
      }
      
      /* Tada */
      @keyframes tada {
        0% { transform: scale(1) rotate(0); }
        10%, 20% { transform: scale(0.9) rotate(-3deg); }
        30%, 50%, 70%, 90% { transform: scale(1.1) rotate(3deg); }
        40%, 60%, 80% { transform: scale(1.1) rotate(-3deg); }
        100% { transform: scale(1) rotate(0); }
      }
      ```
      
    • loading-animations.md 3.8 KB
      # Loading Animations
      
      ## Spinners
      
      ```tsx
      // Simple spinner
      <div className="w-8 h-8 border-4 border-gray-200 border-t-blue-600 rounded-full animate-spin" />
      
      // Dual ring
      <div className="relative w-12 h-12">
        <div className="absolute inset-0 border-4 border-blue-200 rounded-full" />
        <div className="absolute inset-0 border-4 border-transparent border-t-blue-600 rounded-full animate-spin" />
      </div>
      
      // Gradient spinner
      <div className="w-10 h-10 rounded-full animate-spin"
        style={{
          background: 'conic-gradient(from 0deg, transparent, #3b82f6)',
          mask: 'radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px))'
        }}
      />
      ```
      
      ```css
      /* Pulsing ring */
      @keyframes pingRing {
        0% {
          transform: scale(1);
          opacity: 1;
        }
        75%, 100% {
          transform: scale(2);
          opacity: 0;
        }
      }
      
      .ping-ring {
        position: relative;
      }
      
      .ping-ring::before {
        content: '';
        position: absolute;
        inset: 0;
        border: 2px solid currentColor;
        border-radius: 50%;
        animation: pingRing 1.5s cubic-bezier(0, 0, 0.2, 1) infinite;
      }
      ```
      
      ## Dots Loading
      
      ```tsx
      // Bouncing dots
      <div className="flex gap-1">
        <div className="w-2 h-2 bg-blue-600 rounded-full animate-bounce [animation-delay:-0.3s]" />
        <div className="w-2 h-2 bg-blue-600 rounded-full animate-bounce [animation-delay:-0.15s]" />
        <div className="w-2 h-2 bg-blue-600 rounded-full animate-bounce" />
      </div>
      
      // Pulsing dots
      <div className="flex gap-1">
        {[0, 1, 2].map((i) => (
          <div
            key={i}
            className="w-2 h-2 bg-blue-600 rounded-full animate-pulse"
            style={{ animationDelay: `${i * 0.15}s` }}
          />
        ))}
      </div>
      ```
      
      ```css
      /* Scaling dots */
      @keyframes dotScale {
        0%, 80%, 100% { transform: scale(0); }
        40% { transform: scale(1); }
      }
      
      .dot-loader {
        display: flex;
        gap: 4px;
      }
      
      .dot-loader span {
        width: 8px;
        height: 8px;
        background: currentColor;
        border-radius: 50%;
        animation: dotScale 1.4s ease-in-out infinite;
      }
      
      .dot-loader span:nth-child(1) { animation-delay: -0.32s; }
      .dot-loader span:nth-child(2) { animation-delay: -0.16s; }
      .dot-loader span:nth-child(3) { animation-delay: 0s; }
      ```
      
      ## Skeleton Loaders
      
      ```tsx
      // Basic skeleton
      <div className="animate-pulse space-y-4">
        <div className="h-4 bg-gray-200 rounded w-3/4" />
        <div className="h-4 bg-gray-200 rounded w-1/2" />
        <div className="h-4 bg-gray-200 rounded w-5/6" />
      </div>
      
      // Card skeleton
      <div className="animate-pulse">
        <div className="bg-gray-200 h-48 rounded-t-lg" />
        <div className="p-4 space-y-3">
          <div className="h-4 bg-gray-200 rounded w-3/4" />
          <div className="h-4 bg-gray-200 rounded w-1/2" />
        </div>
      </div>
      
      // Shimmer effect
      <div className="relative overflow-hidden bg-gray-200 rounded">
        <div className="absolute inset-0 -translate-x-full animate-[shimmer_2s_infinite] bg-gradient-to-r from-transparent via-white/60 to-transparent" />
      </div>
      ```
      
      ```css
      /* Shimmer keyframe */
      @keyframes shimmer {
        100% { transform: translateX(100%); }
      }
      ```
      
      ## Progress Bars
      
      ```tsx
      // Indeterminate progress
      <div className="h-1 w-full bg-gray-200 rounded overflow-hidden">
        <div className="h-full bg-blue-600 w-1/3 animate-[progress_1s_ease-in-out_infinite]" />
      </div>
      
      // Striped progress
      <div className="h-2 w-full bg-gray-200 rounded overflow-hidden">
        <div
          className="h-full bg-blue-600 transition-all duration-300"
          style={{
            width: `${progress}%`,
            backgroundImage: 'linear-gradient(45deg, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%)',
            backgroundSize: '1rem 1rem',
            animation: 'progress-stripes 1s linear infinite'
          }}
        />
      </div>
      ```
      
      ```css
      @keyframes progress {
        0% { transform: translateX(-100%); }
        100% { transform: translateX(400%); }
      }
      
      @keyframes progress-stripes {
        from { background-position: 1rem 0; }
        to { background-position: 0 0; }
      }
      ```
      
    • micro-interactions.md 4.7 KB
      # Micro-interactions
      
      ## Button Effects
      
      ```tsx
      // Press effect
      <button className="transition-transform duration-100 active:scale-95">
        Click me
      </button>
      
      // Ripple effect (React)
      function RippleButton({ children, ...props }) {
        const [ripples, setRipples] = useState([]);
      
        const handleClick = (e) => {
          const rect = e.currentTarget.getBoundingClientRect();
          const x = e.clientX - rect.left;
          const y = e.clientY - rect.top;
      
          setRipples([...ripples, { x, y, id: Date.now() }]);
          setTimeout(() => setRipples(r => r.slice(1)), 600);
        };
      
        return (
          <button className="relative overflow-hidden" onClick={handleClick} {...props}>
            {ripples.map(ripple => (
              <span
                key={ripple.id}
                className="absolute bg-white/30 rounded-full animate-[ripple_0.6s_ease-out]"
                style={{
                  left: ripple.x,
                  top: ripple.y,
                  transform: 'translate(-50%, -50%)'
                }}
              />
            ))}
            {children}
          </button>
        );
      }
      ```
      
      ```css
      @keyframes ripple {
        from {
          width: 0;
          height: 0;
          opacity: 0.5;
        }
        to {
          width: 200px;
          height: 200px;
          opacity: 0;
        }
      }
      ```
      
      ## Hover Effects
      
      ```tsx
      // Lift effect
      <div className="transition-all duration-300 hover:-translate-y-1 hover:shadow-lg">
        Card content
      </div>
      
      // Glow effect
      <button className="transition-shadow duration-300 hover:shadow-[0_0_20px_rgba(59,130,246,0.5)]">
        Glow button
      </button>
      
      // Border animation
      <div className="relative group">
        <div className="absolute -inset-0.5 bg-gradient-to-r from-blue-600 to-cyan-600 rounded-lg blur opacity-0 group-hover:opacity-75 transition duration-300" />
        <div className="relative bg-white rounded-lg p-6">Content</div>
      </div>
      
      // Underline animation
      <a className="relative inline-block after:absolute after:bottom-0 after:left-0 after:h-0.5 after:w-0 after:bg-current after:transition-all after:duration-300 hover:after:w-full">
        Animated link
      </a>
      
      // Fill animation
      <a className="relative overflow-hidden group">
        <span className="relative z-10 transition-colors duration-300 group-hover:text-white">
          Hover me
        </span>
        <span className="absolute inset-0 bg-blue-600 transform -translate-x-full group-hover:translate-x-0 transition-transform duration-300" />
      </a>
      ```
      
      ## Icon Animations
      
      ```tsx
      // Rotate on hover
      <button className="group">
        <SettingsIcon className="transition-transform duration-500 group-hover:rotate-180" />
      </button>
      
      // Bounce on hover
      <button className="group">
        <ArrowIcon className="transition-transform group-hover:translate-x-1 group-hover:animate-bounce" />
      </button>
      
      // Scale + rotate
      <button className="group">
        <PlusIcon className="transition-all duration-300 group-hover:scale-110 group-hover:rotate-90" />
      </button>
      ```
      
      ## Form Interactions
      
      ```tsx
      // Input focus effect
      <div className="relative">
        <input
          className="peer w-full border-b-2 border-gray-300 focus:border-blue-600 outline-none py-2 transition-colors"
          placeholder=" "
        />
        <label className="absolute left-0 top-2 text-gray-500 transition-all peer-focus:-top-4 peer-focus:text-sm peer-focus:text-blue-600 peer-[:not(:placeholder-shown)]:-top-4 peer-[:not(:placeholder-shown)]:text-sm">
          Email
        </label>
      </div>
      
      // Checkbox animation
      <label className="flex items-center gap-2 cursor-pointer">
        <div className="relative">
          <input type="checkbox" className="peer sr-only" />
          <div className="w-5 h-5 border-2 rounded transition-colors peer-checked:bg-blue-600 peer-checked:border-blue-600" />
          <CheckIcon className="absolute inset-0 m-auto w-3 h-3 text-white opacity-0 scale-0 transition-all peer-checked:opacity-100 peer-checked:scale-100" />
        </div>
        Label text
      </label>
      
      // Toggle switch
      <button
        role="switch"
        aria-checked={enabled}
        onClick={() => setEnabled(!enabled)}
        className={cn(
          "relative w-11 h-6 rounded-full transition-colors",
          enabled ? "bg-blue-600" : "bg-gray-200"
        )}
      >
        <span className={cn(
          "absolute top-0.5 left-0.5 w-5 h-5 bg-white rounded-full shadow transition-transform",
          enabled && "translate-x-5"
        )} />
      </button>
      ```
      
      ## Success/Error States
      
      ```tsx
      // Success checkmark
      <div className="w-16 h-16 rounded-full bg-green-100 flex items-center justify-center">
        <svg className="w-8 h-8 text-green-600" viewBox="0 0 24 24">
          <path
            className="animate-[draw_0.5s_ease-out_forwards]"
            fill="none"
            stroke="currentColor"
            strokeWidth="3"
            strokeLinecap="round"
            strokeLinejoin="round"
            strokeDasharray="24"
            strokeDashoffset="24"
            d="M5 13l4 4L19 7"
          />
        </svg>
      </div>
      
      // Error shake
      <input className="animate-[shake_0.5s_ease-in-out] border-red-500" />
      ```
      
      ```css
      @keyframes draw {
        to { stroke-dashoffset: 0; }
      }
      ```
      
    • page-transitions.md 2.7 KB
      # Page Transitions
      
      ## CSS-only Transitions
      
      ```css
      /* View Transitions API (Chrome 111+) */
      @view-transition {
        navigation: auto;
      }
      
      ::view-transition-old(root) {
        animation: fadeOut 0.3s ease-out;
      }
      
      ::view-transition-new(root) {
        animation: fadeIn 0.3s ease-in;
      }
      
      /* Specific element transitions */
      .hero-image {
        view-transition-name: hero;
      }
      
      ::view-transition-old(hero),
      ::view-transition-new(hero) {
        animation-duration: 0.5s;
      }
      ```
      
      ## Framer Motion
      
      ```tsx
      import { motion, AnimatePresence } from 'framer-motion';
      
      // Fade transition
      const pageVariants = {
        initial: { opacity: 0 },
        animate: { opacity: 1 },
        exit: { opacity: 0 },
      };
      
      function PageWrapper({ children }) {
        return (
          <AnimatePresence mode="wait">
            <motion.div
              key={pathname}
              variants={pageVariants}
              initial="initial"
              animate="animate"
              exit="exit"
              transition={{ duration: 0.3 }}
            >
              {children}
            </motion.div>
          </AnimatePresence>
        );
      }
      
      // Slide transition
      const slideVariants = {
        initial: { opacity: 0, x: 20 },
        animate: { opacity: 1, x: 0 },
        exit: { opacity: 0, x: -20 },
      };
      
      // Scale + fade
      const scaleVariants = {
        initial: { opacity: 0, scale: 0.95 },
        animate: { opacity: 1, scale: 1 },
        exit: { opacity: 0, scale: 1.05 },
      };
      
      // Shared layout animations
      function Gallery({ items, selectedId }) {
        return (
          <>
            {items.map(item => (
              <motion.div key={item.id} layoutId={item.id}>
                <img src={item.src} />
              </motion.div>
            ))}
      
            <AnimatePresence>
              {selectedId && (
                <motion.div layoutId={selectedId} className="modal">
                  <img src={items.find(i => i.id === selectedId).src} />
                </motion.div>
              )}
            </AnimatePresence>
          </>
        );
      }
      ```
      
      ## Staggered Animations
      
      ```tsx
      // Framer Motion stagger
      const containerVariants = {
        hidden: { opacity: 0 },
        visible: {
          opacity: 1,
          transition: {
            staggerChildren: 0.1,
          },
        },
      };
      
      const itemVariants = {
        hidden: { opacity: 0, y: 20 },
        visible: { opacity: 1, y: 0 },
      };
      
      function StaggeredList({ items }) {
        return (
          <motion.ul
            variants={containerVariants}
            initial="hidden"
            animate="visible"
          >
            {items.map(item => (
              <motion.li key={item.id} variants={itemVariants}>
                {item.name}
              </motion.li>
            ))}
          </motion.ul>
        );
      }
      
      // CSS stagger with custom properties
      <ul className="stagger-list">
        {items.map((item, i) => (
          <li
            key={item.id}
            style={{ '--i': i } as React.CSSProperties}
            className="animate-fadeInUp opacity-0"
          >
            {item.name}
          </li>
        ))}
      </ul>
      ```
      
      ```css
      .stagger-list li {
        animation: fadeInUp 0.5s ease forwards;
        animation-delay: calc(var(--i) * 0.1s);
      }
      ```
      
    • performance.md 984 B
      # Performance Best Practices
      
      ## GPU-Accelerated Properties
      
      ```css
      /* GOOD - GPU accelerated */
      transform: translateX(100px);
      transform: scale(1.1);
      transform: rotate(45deg);
      opacity: 0.5;
      
      /* BAD - Triggers layout/paint */
      left: 100px;
      top: 50px;
      width: 200px;
      height: 100px;
      margin: 20px;
      padding: 10px;
      border-width: 2px;
      font-size: 16px;
      ```
      
      ## will-change (Use Sparingly)
      
      ```css
      /* Only when needed for complex animations */
      .complex-animation {
        will-change: transform, opacity;
      }
      
      /* Remove after animation */
      .complex-animation.done {
        will-change: auto;
      }
      ```
      
      ## Contain for Isolation
      
      ```css
      .animated-section {
        contain: layout style paint;
      }
      ```
      
      ## Animation Performance Checklist
      
      - [ ] Only animate `transform` and `opacity`
      - [ ] Use `will-change` only when necessary
      - [ ] Keep animations under 300ms for UI feedback
      - [ ] Test on low-end devices
      - [ ] Use `contain` for isolated sections
      - [ ] Reduce animation during scroll
      - [ ] Pause off-screen animations
      
    • principles-and-timing.md 989 B
      # Animation Principles and Timing
      
      ## The 12 Principles (Disney) Applied to UI
      
      | Principle | UI Application |
      |-----------|----------------|
      | Squash & Stretch | Button press, elastic effects |
      | Anticipation | Hover states before action |
      | Staging | Focus attention on important elements |
      | Follow Through | Overshoot then settle |
      | Ease In/Out | Natural acceleration/deceleration |
      | Arcs | Curved motion paths |
      | Secondary Action | Supporting animations |
      | Timing | Duration conveys weight/importance |
      | Exaggeration | Emphasis for clarity |
      | Appeal | Pleasing, polished motion |
      
      ## Timing Guidelines
      
      | Animation Type | Duration | Easing |
      |----------------|----------|--------|
      | Micro-interaction | 100-200ms | ease-out |
      | Button/hover | 150-250ms | ease |
      | Modal open | 200-300ms | ease-out |
      | Modal close | 150-200ms | ease-in |
      | Page transition | 300-500ms | ease-in-out |
      | Loading loop | 1000-2000ms | linear/ease-in-out |
      | Attention grab | 500-1000ms | elastic |
      
    • scroll-animations.md 2.2 KB
      # Scroll Animations
      
      ## Intersection Observer
      
      ```tsx
      function useInView(options = {}) {
        const ref = useRef(null);
        const [isInView, setIsInView] = useState(false);
      
        useEffect(() => {
          const observer = new IntersectionObserver(([entry]) => {
            if (entry.isIntersecting) {
              setIsInView(true);
              if (options.once) observer.disconnect();
            }
          }, { threshold: 0.1, ...options });
      
          if (ref.current) observer.observe(ref.current);
          return () => observer.disconnect();
        }, []);
      
        return [ref, isInView];
      }
      
      // Usage
      function AnimatedSection() {
        const [ref, isInView] = useInView({ once: true });
      
        return (
          <div
            ref={ref}
            className={cn(
              "transition-all duration-700",
              isInView ? "opacity-100 translate-y-0" : "opacity-0 translate-y-10"
            )}
          >
            Content
          </div>
        );
      }
      ```
      
      ## Scroll-triggered with Framer Motion
      
      ```tsx
      import { motion, useScroll, useTransform } from 'framer-motion';
      
      function ParallaxSection() {
        const ref = useRef(null);
        const { scrollYProgress } = useScroll({
          target: ref,
          offset: ["start end", "end start"]
        });
      
        const y = useTransform(scrollYProgress, [0, 1], [100, -100]);
        const opacity = useTransform(scrollYProgress, [0, 0.5, 1], [0, 1, 0]);
      
        return (
          <motion.div ref={ref} style={{ y, opacity }}>
            Parallax content
          </motion.div>
        );
      }
      
      // Scroll-linked progress
      function ScrollProgress() {
        const { scrollYProgress } = useScroll();
      
        return (
          <motion.div
            className="fixed top-0 left-0 right-0 h-1 bg-blue-600 origin-left"
            style={{ scaleX: scrollYProgress }}
          />
        );
      }
      ```
      
      ## CSS Scroll-driven Animations
      
      ```css
      /* Native scroll-driven animations (Chrome 115+) */
      @keyframes reveal {
        from {
          opacity: 0;
          transform: translateY(50px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }
      
      .scroll-reveal {
        animation: reveal linear both;
        animation-timeline: view();
        animation-range: entry 0% cover 40%;
      }
      
      /* Scroll progress indicator */
      .progress-bar {
        transform-origin: left;
        animation: grow linear;
        animation-timeline: scroll();
      }
      
      @keyframes grow {
        from { transform: scaleX(0); }
        to { transform: scaleX(1); }
      }
      ```
      
    • tailwind-config.md 3.1 KB
      # Tailwind Animation Config
      
      ```javascript
      // tailwind.config.js
      module.exports = {
        theme: {
          extend: {
            animation: {
              // Fade
              'fade-in': 'fadeIn 0.5s ease forwards',
              'fade-in-up': 'fadeInUp 0.5s ease forwards',
              'fade-in-down': 'fadeInDown 0.5s ease forwards',
              'fade-out': 'fadeOut 0.3s ease forwards',
      
              // Slide
              'slide-in-left': 'slideInLeft 0.3s ease-out',
              'slide-in-right': 'slideInRight 0.3s ease-out',
              'slide-in-up': 'slideInUp 0.3s ease-out',
              'slide-in-down': 'slideInDown 0.3s ease-out',
      
              // Scale
              'scale-in': 'scaleIn 0.2s ease-out',
              'pop': 'pop 0.3s ease-out',
      
              // Attention
              'shake': 'shake 0.5s ease-in-out',
              'wiggle': 'wiggle 1s ease-in-out infinite',
              'heartbeat': 'heartbeat 1.5s ease-in-out infinite',
      
              // Loading
              'shimmer': 'shimmer 2s infinite',
              'progress': 'progress 1s ease-in-out infinite',
            },
            keyframes: {
              fadeIn: {
                '0%': { opacity: '0' },
                '100%': { opacity: '1' },
              },
              fadeInUp: {
                '0%': { opacity: '0', transform: 'translateY(20px)' },
                '100%': { opacity: '1', transform: 'translateY(0)' },
              },
              fadeInDown: {
                '0%': { opacity: '0', transform: 'translateY(-20px)' },
                '100%': { opacity: '1', transform: 'translateY(0)' },
              },
              fadeOut: {
                '0%': { opacity: '1' },
                '100%': { opacity: '0' },
              },
              slideInLeft: {
                '0%': { transform: 'translateX(-100%)' },
                '100%': { transform: 'translateX(0)' },
              },
              slideInRight: {
                '0%': { transform: 'translateX(100%)' },
                '100%': { transform: 'translateX(0)' },
              },
              slideInUp: {
                '0%': { transform: 'translateY(100%)' },
                '100%': { transform: 'translateY(0)' },
              },
              slideInDown: {
                '0%': { transform: 'translateY(-100%)' },
                '100%': { transform: 'translateY(0)' },
              },
              scaleIn: {
                '0%': { transform: 'scale(0)' },
                '100%': { transform: 'scale(1)' },
              },
              pop: {
                '0%': { transform: 'scale(1)' },
                '50%': { transform: 'scale(1.1)' },
                '100%': { transform: 'scale(1)' },
              },
              shake: {
                '0%, 100%': { transform: 'translateX(0)' },
                '10%, 30%, 50%, 70%, 90%': { transform: 'translateX(-5px)' },
                '20%, 40%, 60%, 80%': { transform: 'translateX(5px)' },
              },
              wiggle: {
                '0%, 100%': { transform: 'rotate(-3deg)' },
                '50%': { transform: 'rotate(3deg)' },
              },
              heartbeat: {
                '0%, 100%': { transform: 'scale(1)' },
                '14%': { transform: 'scale(1.3)' },
                '28%': { transform: 'scale(1)' },
                '42%': { transform: 'scale(1.3)' },
                '70%': { transform: 'scale(1)' },
              },
              shimmer: {
                '100%': { transform: 'translateX(100%)' },
              },
              progress: {
                '0%': { transform: 'translateX(-100%)' },
                '100%': { transform: 'translateX(400%)' },
              },
            },
          },
        },
      };
      ```
      
    • transitions.md 1.9 KB
      # CSS Transitions
      
      ## Basic Syntax
      
      ```css
      .element {
        /* Single property */
        transition: opacity 0.3s ease;
      
        /* Multiple properties */
        transition:
          transform 0.3s ease,
          opacity 0.3s ease,
          background-color 0.2s ease;
      
        /* Shorthand: property duration timing-function delay */
        transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) 0s;
      }
      ```
      
      ## Easing Functions
      
      ```css
      /* Built-in */
      transition-timing-function: linear;
      transition-timing-function: ease;        /* Default - slow start, fast middle, slow end */
      transition-timing-function: ease-in;     /* Slow start */
      transition-timing-function: ease-out;    /* Slow end */
      transition-timing-function: ease-in-out; /* Slow start and end */
      
      /* Custom cubic-bezier */
      /* Material Design standard */
      transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
      
      /* Decelerate (entering) */
      transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
      
      /* Accelerate (exiting) */
      transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
      
      /* Bounce effect */
      transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
      
      /* Elastic */
      transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
      ```
      
      ## Tailwind Transitions
      
      ```tsx
      // Duration
      <div className="transition duration-150" />  // 150ms
      <div className="transition duration-300" />  // 300ms
      <div className="transition duration-500" />  // 500ms
      
      // Timing function
      <div className="transition ease-linear" />
      <div className="transition ease-in" />
      <div className="transition ease-out" />
      <div className="transition ease-in-out" />
      
      // Specific properties (better performance)
      <div className="transition-opacity" />
      <div className="transition-transform" />
      <div className="transition-colors" />
      <div className="transition-shadow" />
      <div className="transition-all" />
      
      // Combined
      <button className="transition-all duration-200 ease-out hover:scale-105 hover:shadow-lg">
        Hover me
      </button>
      ```
      
  • SKILL.md 2.3 KB
    ---
    name: css-animation-creator
    description: Create professional CSS animations, transitions, micro-interactions, and complex motion design. Use when adding animations, hover effects, loading states, page transitions, scroll animations, or any motion design work.
    ---
    
    # CSS Animation Creator
    
    Build production-grade, accessible, GPU-efficient motion for web UIs using CSS, Tailwind, and Framer Motion.
    
    ## Workflow
    
    1. Identify the purpose of the motion: feedback, delight, guidance, or storytelling.
    2. Choose the technique: CSS transitions for state changes, keyframes for looping or multi-step motion, Framer Motion or JS for orchestration and scroll-linked effects.
    3. Set timing and easing to match the interaction. Apply the timing and easing tables in `references/principles-and-timing.md`.
    4. Author the animation. Pull ready-made keyframes and patterns from the reference files below rather than writing from scratch.
    5. Constrain animated properties to `transform` and `opacity` for GPU acceleration. Apply `references/performance.md`.
    6. Honor reduced-motion preferences for every animation. Apply `references/accessibility.md`.
    7. Verify on low-end devices and confirm no layout thrash before shipping.
    
    ## Contents
    
    - `references/principles-and-timing.md` — Disney 12 principles mapped to UI, duration and easing tables.
    - `references/transitions.md` — Transition syntax, easing functions, cubic-bezier presets, Tailwind transitions.
    - `references/keyframe-library.md` — Keyframe syntax plus fade, scale, bounce, rotate, slide, and attention-seeker keyframes.
    - `references/loading-animations.md` — Spinners, dots, skeleton loaders, shimmer, progress bars.
    - `references/micro-interactions.md` — Button, hover, icon, form, and success/error interactions.
    - `references/page-transitions.md` — View Transitions API, Framer Motion page and shared-layout transitions, staggered lists.
    - `references/scroll-animations.md` — Intersection Observer, Framer Motion scroll hooks, native CSS scroll-driven animations.
    - `references/accessibility.md` — `prefers-reduced-motion` CSS, Tailwind motion-safe/reduce, React hook.
    - `references/tailwind-config.md` — Drop-in `tailwind.config.js` animation and keyframe definitions.
    - `references/performance.md` — GPU-accelerated properties, `will-change`, `contain`, performance checklist.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related