animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "animation-designer" with this command: npx skills add daffy0208/ai-dev-standards/daffy0208-ai-dev-standards-animation-designer

Animation Designer Skill

I help you create smooth, professional animations for web applications using Framer Motion and CSS.

What I Do

UI Animations:

  • Page transitions
  • Component enter/exit animations
  • Hover effects, button interactions
  • Loading animations

Scroll Animations:

  • Parallax effects
  • Scroll-triggered animations
  • Progress indicators

Micro-interactions:

  • Button press feedback
  • Form field focus states
  • Success/error animations
  • Drag and drop feedback

Framer Motion Basics

Installation

npm install framer-motion

Basic Animation

import { motion } from 'framer-motion'

export function FadeIn({ children }: { children: React.ReactNode }) {
  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      transition={{ duration: 0.5 }}
    >
      {children}
    </motion.div>
  )
}

Common Animation Patterns

Pattern 1: Fade In on Mount

import { motion } from 'framer-motion'

export function Card({ children }: { children: React.ReactNode }) {
  return (
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.4, ease: 'easeOut' }}
      className="p-6 bg-white rounded-lg shadow"
    >
      {children}
    </motion.div>
  )
}

Pattern 2: Staggered List Animation

import { motion } from 'framer-motion'

const container = {
  hidden: { opacity: 0 },
  show: {
    opacity: 1,
    transition: {
      staggerChildren: 0.1
    }
  }
}

const item = {
  hidden: { opacity: 0, y: 20 },
  show: { opacity: 1, y: 0 }
}

export function List({ items }: { items: string[] }) {
  return (
    <motion.ul
      variants={container}
      initial="hidden"
      animate="show"
    >
      {items.map((text, i) => (
        <motion.li key={i} variants={item}>
          {text}
        </motion.li>
      ))}
    </motion.ul>
  )
}

Pattern 3: Button Hover Animation

import { motion } from 'framer-motion'

export function AnimatedButton({ children, onClick }: {
  children: React.ReactNode
  onClick: () => void
}) {
  return (
    <motion.button
      onClick={onClick}
      whileHover={{ scale: 1.05 }}
      whileTap={{ scale: 0.95 }}
      transition={{ type: 'spring', stiffness: 400, damping: 17 }}
      className="px-6 py-3 bg-blue-600 text-white rounded-lg"
    >
      {children}
    </motion.button>
  )
}

Pattern 4: Modal / Dialog Animation

import { motion, AnimatePresence } from 'framer-motion'

export function Modal({ isOpen, onClose, children }: {
  isOpen: boolean
  onClose: () => void
  children: React.ReactNode
}) {
  return (
    <AnimatePresence>
      {isOpen && (
        <>
          {/* Backdrop */}
          <motion.div
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            onClick={onClose}
            className="fixed inset-0 bg-black/50 z-40"
          />

          {/* Modal */}
          <motion.div
            initial={{ opacity: 0, scale: 0.9, y: 20 }}
            animate={{ opacity: 1, scale: 1, y: 0 }}
            exit={{ opacity: 0, scale: 0.9, y: 20 }}
            transition={{ type: 'spring', damping: 25, stiffness: 300 }}
            className="fixed inset-0 flex items-center justify-center z-50 p-4"
          >
            <div className="bg-white rounded-lg p-6 max-w-md w-full">
              {children}
            </div>
          </motion.div>
        </>
      )}
    </AnimatePresence>
  )
}

Pattern 5: Page Transition

'use client'
import { motion } from 'framer-motion'
import { usePathname } from 'next/navigation'

export function PageTransition({ children }: { children: React.ReactNode }) {
  const pathname = usePathname()

  return (
    <motion.div
      key={pathname}
      initial={{ opacity: 0, x: 20 }}
      animate={{ opacity: 1, x: 0 }}
      exit={{ opacity: 0, x: -20 }}
      transition={{ duration: 0.3 }}
    >
      {children}
    </motion.div>
  )
}

// Usage in layout
export default function Layout({ children }) {
  return (
    <PageTransition>
      {children}
    </PageTransition>
  )
}

Scroll Animations

Scroll-Triggered Animation

import { motion, useScroll, useTransform } from 'framer-motion'
import { useRef } from 'react'

export function ScrollReveal({ children }: { children: React.ReactNode }) {
  const ref = useRef(null)
  const { scrollYProgress } = useScroll({
    target: ref,
    offset: ['start end', 'end start']
  })

  const opacity = useTransform(scrollYProgress, [0, 0.3, 0.7, 1], [0, 1, 1, 0])
  const y = useTransform(scrollYProgress, [0, 0.3, 0.7, 1], [100, 0, 0, -100])

  return (
    <motion.div
      ref={ref}
      style={{ opacity, y }}
    >
      {children}
    </motion.div>
  )
}

Parallax Effect

import { motion, useScroll, useTransform } from 'framer-motion'

export function ParallaxSection() {
  const { scrollY } = useScroll()
  const y = useTransform(scrollY, [0, 500], [0, 150])

  return (
    <div className="relative h-screen overflow-hidden">
      <motion.div
        style={{ y }}
        className="absolute inset-0"
      >
        <img src="/background.jpg" alt="" className="w-full h-full object-cover" />
      </motion.div>

      <div className="relative z-10 flex items-center justify-center h-full">
        <h1 className="text-6xl font-bold text-white">
          Parallax Effect
        </h1>
      </div>
    </div>
  )
}

Scroll Progress Indicator

import { motion, useScroll } from 'framer-motion'

export function ScrollProgress() {
  const { scrollYProgress } = useScroll()

  return (
    <motion.div
      style={{ scaleX: scrollYProgress }}
      className="fixed top-0 left-0 right-0 h-1 bg-blue-600 origin-left z-50"
    />
  )
}

Loading Animations

Spinner

import { motion } from 'framer-motion'

export function Spinner() {
  return (
    <motion.div
      animate={{ rotate: 360 }}
      transition={{
        duration: 1,
        repeat: Infinity,
        ease: 'linear'
      }}
      className="w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full"
    />
  )
}

Skeleton Loader

import { motion } from 'framer-motion'

export function SkeletonLoader() {
  return (
    <motion.div
      animate={{ opacity: [0.5, 1, 0.5] }}
      transition={{
        duration: 1.5,
        repeat: Infinity,
        ease: 'easeInOut'
      }}
      className="bg-gray-200 rounded h-4 w-full"
    />
  )
}

Pulsing Dots

import { motion } from 'framer-motion'

const dotVariants = {
  start: { scale: 0.8, opacity: 0.5 },
  end: { scale: 1.2, opacity: 1 }
}

export function PulsingDots() {
  return (
    <div className="flex gap-2">
      {[0, 1, 2].map((i) => (
        <motion.div
          key={i}
          variants={dotVariants}
          animate="end"
          initial="start"
          transition={{
            duration: 0.6,
            repeat: Infinity,
            repeatType: 'reverse',
            delay: i * 0.2
          }}
          className="w-3 h-3 bg-blue-600 rounded-full"
        />
      ))}
    </div>
  )
}

CSS Animations

Keyframe Animations

/* Fade in animation */
@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.fade-in {
  animation: fadeIn 0.5s ease-out;
}

/* Slide in from right */
@keyframes slideInRight {
  from {
    transform: translateX(100%);
  }
  to {
    transform: translateX(0);
  }
}

.slide-in-right {
  animation: slideInRight 0.3s ease-out;
}

/* Bounce */
@keyframes bounce {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-10px);
  }
}

.bounce {
  animation: bounce 0.5s ease-in-out infinite;
}

Tailwind Animations

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      keyframes: {
        'fade-in': {
          '0%': { opacity: '0', transform: 'translateY(10px)' },
          '100%': { opacity: '1', transform: 'translateY(0)' }
        },
        'slide-in': {
          '0%': { transform: 'translateX(-100%)' },
          '100%': { transform: 'translateX(0)' }
        }
      },
      animation: {
        'fade-in': 'fade-in 0.5s ease-out',
        'slide-in': 'slide-in 0.3s ease-out'
      }
    }
  }
}

Usage:

<div className="animate-fade-in">Fades in</div>
<div className="animate-slide-in">Slides in</div>

Micro-Interactions

Success Checkmark Animation

import { motion } from 'framer-motion'

export function SuccessCheckmark() {
  return (
    <motion.svg
      width="48"
      height="48"
      viewBox="0 0 48 48"
      initial={{ scale: 0 }}
      animate={{ scale: 1 }}
      transition={{ type: 'spring', stiffness: 300, damping: 20 }}
    >
      <motion.circle
        cx="24"
        cy="24"
        r="22"
        fill="none"
        stroke="#10B981"
        strokeWidth="4"
        initial={{ pathLength: 0 }}
        animate={{ pathLength: 1 }}
        transition={{ duration: 0.5 }}
      />
      <motion.path
        d="M12 24 L20 32 L36 16"
        fill="none"
        stroke="#10B981"
        strokeWidth="4"
        strokeLinecap="round"
        strokeLinejoin="round"
        initial={{ pathLength: 0 }}
        animate={{ pathLength: 1 }}
        transition={{ duration: 0.3, delay: 0.3 }}
      />
    </motion.svg>
  )
}

Notification Badge

import { motion } from 'framer-motion'

export function NotificationBadge({ count }: { count: number }) {
  return (
    <div className="relative">
      <button className="p-2">
        <BellIcon />
      </button>

      {count > 0 && (
        <motion.div
          initial={{ scale: 0 }}
          animate={{ scale: 1 }}
          transition={{ type: 'spring', stiffness: 500, damping: 15 }}
          className="absolute -top-1 -right-1 bg-red-600 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center"
        >
          {count}
        </motion.div>
      )}
    </div>
  )
}

Animation Best Practices

1. Performance

// ✅ Good: Animate transform and opacity (GPU accelerated)
<motion.div
  animate={{ x: 100, opacity: 0.5 }}
/>

// ❌ Bad: Animate width, height (triggers layout)
<motion.div
  animate={{ width: '100%', height: '200px' }}
/>

2. Duration

// Too fast: < 100ms (feels abrupt)
// Too slow: > 500ms (feels sluggish)

// ✅ Sweet spot: 200-400ms for most UI animations
<motion.div
  animate={{ opacity: 1 }}
  transition={{ duration: 0.3 }}
/>

3. Easing

// Natural motion: easeOut (starts fast, ends slow)
<motion.div
  animate={{ y: 0 }}
  transition={{ ease: 'easeOut' }}
/>

// Bouncy: spring
<motion.button
  whileTap={{ scale: 0.95 }}
  transition={{ type: 'spring', stiffness: 400 }}
/>

4. Reduce Motion (Accessibility)

import { useReducedMotion } from 'framer-motion'

export function AccessibleAnimation({ children }: { children: React.ReactNode }) {
  const shouldReduceMotion = useReducedMotion()

  return (
    <motion.div
      initial={{ opacity: 0, y: shouldReduceMotion ? 0 : 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{
        duration: shouldReduceMotion ? 0 : 0.4
      }}
    >
      {children}
    </motion.div>
  )
}

Complex Animations

Drag and Drop

import { motion } from 'framer-motion'
import { useState } from 'react'

export function Draggable() {
  const [position, setPosition] = useState({ x: 0, y: 0 })

  return (
    <motion.div
      drag
      dragConstraints={{ left: 0, right: 300, top: 0, bottom: 300 }}
      dragElastic={0.1}
      onDragEnd={(e, info) => {
        setPosition({ x: info.point.x, y: info.point.y })
      }}
      className="w-24 h-24 bg-blue-600 rounded-lg cursor-grab active:cursor-grabbing"
    />
  )
}

Animated Number Counter

import { motion, useSpring, useTransform } from 'framer-motion'
import { useEffect } from 'react'

export function AnimatedNumber({ value }: { value: number }) {
  const spring = useSpring(0, { stiffness: 100, damping: 30 })
  const display = useTransform(spring, (current) =>
    Math.round(current).toLocaleString()
  )

  useEffect(() => {
    spring.set(value)
  }, [spring, value])

  return <motion.span>{display}</motion.span>
}

// Usage
<AnimatedNumber value={1250} />

When to Use Me

Perfect for:

  • Creating polished UI animations
  • Building interactive components
  • Adding scroll effects
  • Designing loading states
  • Improving user feedback

I'll help you:

  • Choose the right animation type
  • Implement smooth transitions
  • Optimize animation performance
  • Ensure accessibility
  • Create delightful micro-interactions

What I'll Create

✨ Page Transitions
🎯 Micro-Interactions
📜 Scroll Animations
⏳ Loading States
🎨 Hover Effects
🎪 Complex Animations

Let's make your interfaces feel alive!

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

brand-designer

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

data-visualizer

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

3d-visualizer

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

mobile-developer

No summary provided by upstream source.

Repository SourceNeeds Review