Skip to content

Instantly share code, notes, and snippets.

@homanp
Last active January 5, 2026 20:39
Show Gist options
  • Select an option

  • Save homanp/a56ebddbd9321715d87480a3a71a9c8b to your computer and use it in GitHub Desktop.

Select an option

Save homanp/a56ebddbd9321715d87480a3a71a9c8b to your computer and use it in GitHub Desktop.
framer-motion-skill

name: framer-motion

description: Create polished, cinematic product demo animations using Framer Motion. Use this skill when building animated product showcases, feature reveal sequences, UI walkthroughs, marketing videos, or any React-based demo that needs professional motion design. Covers orchestration, scroll-triggered reveals, gesture animations, layout transitions, and cinematic timing patterns.

Framer Motion for Product Demos

Create cinematic, professional product demo animations that showcase features with impact.

Core Setup

import { motion, AnimatePresence, useScroll, useTransform } from "motion/react"

Cinematic Timing Presets

Use these easing curves for professional feel:

const easing = {
  smooth: [0.4, 0, 0.2, 1],        // General UI
  snappy: [0.4, 0, 0, 1],          // Quick interactions
  dramatic: [0.16, 1, 0.3, 1],     // Hero reveals
  elastic: [0.68, -0.6, 0.32, 1.6] // Playful bounce
}

Demo Animation Patterns

1. Staggered Feature Reveal

const container = {
  hidden: { opacity: 0 },
  show: {
    opacity: 1,
    transition: { staggerChildren: 0.15, delayChildren: 0.3 }
  }
}

const item = {
  hidden: { opacity: 0, y: 40 },
  show: { opacity: 1, y: 0, transition: { duration: 0.6, ease: easing.dramatic } }
}

<motion.div variants={container} initial="hidden" animate="show">
  {features.map(f => <motion.div key={f.id} variants={item}>{f.content}</motion.div>)}
</motion.div>

2. Hero Product Entrance

<motion.div
  initial={{ scale: 0.8, opacity: 0, y: 60 }}
  animate={{ scale: 1, opacity: 1, y: 0 }}
  transition={{ duration: 1.2, ease: easing.dramatic }}
/>

3. Scroll-Triggered Sections

function ScrollReveal({ children }) {
  return (
    <motion.div
      initial={{ opacity: 0, y: 80 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true, margin: "-100px" }}
      transition={{ duration: 0.8, ease: easing.smooth }}
    >
      {children}
    </motion.div>
  )
}

4. Sequenced Demo Flow

For timed demo sequences (auto-playing walkthroughs):

function DemoSequence() {
  const [step, setStep] = useState(0)
  
  useEffect(() => {
    const timings = [2000, 3000, 2500, 4000] // ms per step
    const timer = setTimeout(() => {
      setStep(s => (s + 1) % timings.length)
    }, timings[step])
    return () => clearTimeout(timer)
  }, [step])

  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={step}
        initial={{ opacity: 0, x: 40 }}
        animate={{ opacity: 1, x: 0 }}
        exit={{ opacity: 0, x: -40 }}
        transition={{ duration: 0.5 }}
      >
        {steps[step]}
      </motion.div>
    </AnimatePresence>
  )
}

5. UI Interaction Highlights

Simulate clicks, hovers, typing for demos:

// Animated cursor
<motion.div
  className="cursor"
  animate={{ x: [0, 200, 200], y: [0, 0, 150] }}
  transition={{ duration: 2, times: [0, 0.4, 1], ease: "easeInOut" }}
/>

// Simulated typing
function TypeWriter({ text, delay = 0 }) {
  const [displayed, setDisplayed] = useState("")
  useEffect(() => {
    const timeout = setTimeout(() => {
      let i = 0
      const interval = setInterval(() => {
        setDisplayed(text.slice(0, i + 1))
        i++
        if (i >= text.length) clearInterval(interval)
      }, 50)
    }, delay)
    return () => clearTimeout(timeout)
  }, [text, delay])
  return <span>{displayed}<motion.span animate={{ opacity: [1, 0] }} transition={{ repeat: Infinity, duration: 0.8 }}>|</motion.span></span>
}

6. Layout Transitions

Smooth morphing between states:

<motion.div layout layoutId="feature-card" transition={{ type: "spring", stiffness: 300, damping: 30 }}>
  {expanded ? <ExpandedView /> : <CompactView />}
</motion.div>

7. Parallax Depth

function ParallaxLayer({ speed = 0.5, children }) {
  const { scrollY } = useScroll()
  const y = useTransform(scrollY, [0, 1000], [0, 1000 * speed])
  return <motion.div style={{ y }}>{children}</motion.div>
}

Demo-Specific Techniques

Attention Pulse

Draw attention to features:

<motion.div
  animate={{ scale: [1, 1.05, 1], boxShadow: ["0 0 0 0 rgba(59,130,246,0)", "0 0 0 8px rgba(59,130,246,0.3)", "0 0 0 0 rgba(59,130,246,0)"] }}
  transition={{ duration: 2, repeat: Infinity }}
/>

Before/After Slider

function BeforeAfter({ before, after }) {
  const [split, setSplit] = useState(50)
  return (
    <div className="relative overflow-hidden" onMouseMove={e => setSplit((e.nativeEvent.offsetX / e.currentTarget.offsetWidth) * 100)}>
      <div className="absolute inset-0">{before}</div>
      <motion.div className="absolute inset-0" style={{ clipPath: `inset(0 0 0 ${split}%)` }}>{after}</motion.div>
    </div>
  )
}

Number Counter

function Counter({ from = 0, to, duration = 2 }) {
  const [value, setValue] = useState(from)
  useEffect(() => {
    const start = Date.now()
    const tick = () => {
      const progress = Math.min((Date.now() - start) / (duration * 1000), 1)
      setValue(Math.floor(from + (to - from) * progress))
      if (progress < 1) requestAnimationFrame(tick)
    }
    tick()
  }, [from, to, duration])
  return <motion.span>{value.toLocaleString()}</motion.span>
}

Performance Guidelines

  1. Use will-change: transform sparingly on animated elements
  2. Prefer transform and opacity over layout-triggering properties
  3. Use layoutId for shared element transitions instead of manual calculations
  4. Set viewport={{ once: true }} for scroll animations that don’t need to repeat
  5. Use AnimatePresence with mode="wait" for clean exit/enter sequences

Demo Structure Template

export default function ProductDemo() {
  return (
    <>
      {/* Hero with dramatic entrance */}
      <section><HeroReveal /></section>
      
      {/* Scroll-triggered feature sections */}
      {features.map(f => (
        <ScrollReveal key={f.id}><FeatureSection {...f} /></ScrollReveal>
      ))}
      
      {/* Interactive demo sequence */}
      <section><DemoSequence steps={demoSteps} /></section>
      
      {/* Stats with counters */}
      <section><StatsSection /></section>
      
      {/* Final CTA with attention animation */}
      <section><CTAWithPulse /></section>
    </>
  )
}

Quick Reference

Effect Key Props
Fade in initial={{ opacity: 0 }} animate={{ opacity: 1 }}
Slide up initial={{ y: 40 }} animate={{ y: 0 }}
Scale initial={{ scale: 0.9 }} animate={{ scale: 1 }}
Stagger Parent: staggerChildren: 0.1
On scroll whileInView={{ opacity: 1 }} viewport={{ once: true }}
On hover whileHover={{ scale: 1.05 }}
Spring transition={{ type: "spring", stiffness: 300 }}
Sequence transition={{ delay: 0.5 }} or keyframe arrays
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment