April 8, 2026/Intermediate/Hover & Reveals

Clip-Path Polygon Card Reveal

Image cards are collapsed to a single center point using clip-path polygon and expand to full size as they enter the viewport. Combined with scale animation. Triggered by IntersectionObserver.

"use client";

import { useEffect, useRef, useState } from "react";
import { motion } from "framer-motion";
import styles from "./styles.module.css";

function RevealCard({ title, category, image, href }) {
  const ref = useRef(null);
  const [isVisible, setIsVisible] = useState(false);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsVisible(true);
          observer.disconnect();
        }
      },
      { threshold: 0.2 }
    );
    if (ref.current) observer.observe(ref.current);
    return () => observer.disconnect();
  }, []);

  return (
    <a ref={ref} href={href} className={styles.card}>
      <motion.div
        className={styles.imageWrap}
        initial={{ clipPath: "polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%)", scale: 0.8 }}
        animate={
          isVisible
            ? { clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", scale: 1 }
            : {}
        }
        transition={{ duration: 0.8, ease: [0.16, 1, 0.3, 1] }}
        style={{ willChange: "clip-path, transform" }}
      >
        <div
          className={styles.image}
          style={{ backgroundImage: `url(${image})`, backgroundColor: "#1a1a1a" }}
        />
      </motion.div>
      <div className={styles.info}>
        <h3 className={styles.title}>{title}</h3>
        <span className={styles.category}>{category}</span>
      </div>
    </a>
  );
}

export default function ClipPathPolygonCardReveal({ cards = [] }) {
  return (
    <div className={styles.grid}>
      {cards.map((card) => (
        <RevealCard key={card.title} {...card} />
      ))}
    </div>
  );
}
  • framer-motion

Related Components

3MO AGO
Pixelate Imagehover-reveals
3MO AGO
Reveal Grouphover-reveals
3MO AGO
Clip-Path Inset Revealhover-reveals