AnimationsSimpleApril 9, 2026

Refracted Glass

Glass distortion effect rendered entirely with SVG filters — no libraries or external services. feTurbulence generates organic noise, feDisplacementMap warps the image through it, and an animated baseFrequency gives the glass a slow, living shift over time.

View Full Demo →
demo.jsx
import RefractedGlass from "./index.jsx";
import styles from "./demo.module.css";

export default function RefractedGlassDemo() {
  return (
    <div className={styles.demo}>
      <img
        src="/demo-assets/models/model3.png"
        alt=""
        className={styles.bg}
        aria-hidden="true"
      />
      <div className={styles.overlay} />
      <div className={styles.content}>
        <div className={styles.text}>
          <p className={styles.label}>SVG — Strip Distortion</p>
          <h1 className={styles.heading}>Refracted<br />Glass</h1>
        </div>
        <RefractedGlass />
      </div>
    </div>
  );
}
index.jsx
"use client";

import { useState } from "react";
import styles from "./styles.module.css";

const STRIP_COUNT = 10;

const STRIPS = Array.from({ length: STRIP_COUNT }, (_, i) => ({
  x1: (i / STRIP_COUNT) * 100,
  x2: ((i + 1) / STRIP_COUNT) * 100,
  offset: Math.sin(i * 1.1 + 0.4) * 13,
}));

export default function RefractedGlass({
  image = "/demo-assets/models/model3.png",
  altText = "",
}) {
  const [hovered, setHovered] = useState(false);

  return (
    <div
      className={styles.wrapper}
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={() => setHovered(false)}
    >
      {STRIPS.map(({ x1, x2, offset }, i) => (
        <div
          key={i}
          className={styles.strip}
          style={{
            clipPath: `polygon(${x1}% 0%, ${x2}% 0%, ${x2}% 100%, ${x1}% 100%)`,
            transform: `translateY(${hovered ? offset : 0}px)`,
          }}
        >
          <img src={image} alt={i === 0 ? altText : ""} className={styles.img} />
        </div>
      ))}
    </div>
  );
}
demo.module.css
.demo {
  position: relative;
  min-height: 100vh;
  background: #080808;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* Blurred model image sits behind as ambient texture */
.bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 20%;
  filter: blur(60px) saturate(1.4);
  opacity: 0.25;
  transform: scale(1.1);
}

.overlay {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, transparent 30%, #080808 80%);
}

.content {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 5rem;
  padding: 5rem 4rem;
  width: 100%;
  max-width: 1100px;
}

.text {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.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, 6vw, 6rem);
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 0.95;
  color: #ffffff;
}

@media (max-width: 767px) {
  .content {
    flex-direction: column;
    gap: 3rem;
    padding: 4rem 2rem;
    text-align: center;
  }
}
styles.module.css
.wrapper {
  aspect-ratio: 1 / 1.25;
  border-radius: 0.75em;
  width: 100%;
  max-width: 32em;
  position: relative;
  overflow: hidden;
  background: #111;
}

.strip {
  position: absolute;
  inset: 0;
  transition: transform 700ms cubic-bezier(0.25, 1, 0.5, 1);
}

.img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

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.