AnimationsSimpleApril 8, 2026

Emoji Pill Spring Reveal

A hidden pill element sits behind a button rotated 180deg and scaled to zero. On hover it spins to 0deg, scales to full size, and springs into view using a Framer Motion spring transition.

View Full Demo →
demo.jsx
import EmojiPillSpringReveal from "./index.jsx";
import { emojiPillSpringReveal } from "@/content/animations/demo-data.js";
import styles from "./demo.module.css";

export default function EmojiPillSpringRevealDemo() {
  return (
    <div className={styles.demo}>
      <div className={styles.hero}>
        <p className={styles.label}>Available for work</p>
        <h1 className={styles.heading}>Let&apos;s build<br />something great</h1>
        <EmojiPillSpringReveal {...emojiPillSpringReveal} />
      </div>
    </div>
  );
}
index.jsx
"use client";

import { motion } from "framer-motion";
import styles from "./styles.module.css";

export default function EmojiPillSpringReveal({ buttons = [] }) {
  return (
    <div className={styles.row}>
      {buttons.map((btn) => (
        <motion.a
          key={btn.label}
          href={btn.href}
          className={styles.button}
          whileHover="hover"
          initial="rest"
        >
          <span className={styles.label}>{btn.label}</span>
          <motion.span
            className={styles.pill}
            variants={{
              rest: { rotate: 180, scale: 0, opacity: 0 },
              hover: {
                rotate: 0,
                scale: 1,
                opacity: 1,
                transition: { type: "spring", stiffness: 400, damping: 20 },
              },
            }}
          >
            {btn.emoji}
          </motion.span>
        </motion.a>
      ))}
    </div>
  );
}
demo.module.css
.demo {
  min-height: 100vh;
  background: #0a0a0a;
  display: flex;
  align-items: center;
  justify-content: center;
}

.hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 2.5rem;
  padding: 4rem 2rem;
}

.label {
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.35);
}

.heading {
  font-size: clamp(3rem, 8vw, 7rem);
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 0.95;
  color: #ffffff;
}
styles.module.css
.row {
  display: flex;
  gap: 1rem;
  padding: 3rem;
  justify-content: center;
  flex-wrap: wrap;
}

.button {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 1.5rem;
  font-size: 0.9375rem;
  font-weight: 500;
  font-family: inherit;
  color: #1a1a1a;
  background: #f5f5f5;
  border: 1px solid #e5e5e5;
  border-radius: 9999px;
  text-decoration: none;
  cursor: pointer;
  letter-spacing: -0.01em;
  overflow: hidden;
}

.label {
  position: relative;
  z-index: 1;
}

.pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
  transform-origin: center;
}
  • framer-motion

Apr 27, 2026

ANIMATIONS

Cursor Hover Label

A custom cursor label that follows the mouse and fades in when hovering trigger elements. Uses GSAP quickTo for smooth tracking. Trigger elements use data-cursor-label attributes to set label text. Inspired by portfolio/agency sites like Studio PIC.

Apr 27, 2026

ANIMATIONS

Section Transition 01

GSAP ScrollTrigger-based section transition system. Sibling sections opt into parallax, pin, or reveal modes via data attributes. Supports y offset, overlay opacity, and overlay color per section. Mobile strategy simplifies motion on smaller screens.

Apr 27, 2026

ANIMATIONS

Text Reveal 01

GSAP SplitText-based text reveal system. Text elements opt in with data-reveal-01 and split into lines, words, or characters. Supports load-time reveals, scroll-triggered reveals, scrubbed scroll reveals, and manual split-only mode for custom timelines. Per-element overrides for duration, stagger, delay, ease, and replay behavior.