Mask Cursor

A collection of mask components examples, showcasing different color combinations and effects.

Mask Cursor

Preview

I'm a Full Stack Developer who has an eye for design

First rule of fight club is you do not talk about fight club

Source Code
Source Code
tsx
"use client";

import {
  motion,
  useMotionValue,
  useSpring,
  useMotionTemplate,
} from "framer-motion";
import { useState, useEffect, useRef } from "react";
import useMousePosition from "../../app/utils/useMousePosition";
import { cn } from "@/lib/utils";

const MaskCursor = () => {
  const { x, y } = useMousePosition();
  const [isHovered, setIsHovered] = useState(false);
  const containerRef = useRef<HTMLDivElement>(null);

  const size = isHovered ? 300 : 40;

  const maskX = useMotionValue(0);
  const maskY = useMotionValue(0);
  const maskSize = useMotionValue(size);

  const smoothX = useSpring(maskX, { stiffness: 300, damping: 30 });
  const smoothY = useSpring(maskY, { stiffness: 300, damping: 30 });
  const smoothSize = useSpring(maskSize, { stiffness: 300, damping: 30 });

  const maskPosition = useMotionTemplate`${smoothX}px ${smoothY}px`;
  const maskSizeValue = useMotionTemplate`${smoothSize}px`;

  useEffect(() => {
    if (!containerRef.current || x == null || y == null) return;

    const rect = containerRef.current.getBoundingClientRect();

    const relativeX = x - rect.left;
    const relativeY = y - rect.top;

    maskX.set(relativeX - size / 2);
    maskY.set(relativeY - size / 2);
    maskSize.set(size);
  }, [x, y, size, maskX, maskY, maskSize]);

  return (
    <div
      ref={containerRef}
      className={cn(
        "relative flex h-[50vh] items-center justify-center overflow-hidden",
      )}
    >
      {/* MASK LAYER */}
      <motion.div
        className={cn(
          "absolute inset-0 flex items-center justify-center",
          "bg-[#ec4e39] text-black",
          "mask-repeat-no-repeat mask-[url('/CircleSvg.svg')]",
          "[-webkit-mask-image:url('/CircleSvg.svg')] [-webkit-mask-repeat:no-repeat]",
        )}
        style={{
          WebkitMaskPosition: maskPosition,
          WebkitMaskSize: maskSizeValue,
        }}
      >
        <p
          className={cn("w-[800px] cursor-default p-6 text-center text-2xl")}
          onMouseEnter={() => setIsHovered(true)}
          onMouseLeave={() => setIsHovered(false)}
        >
          I&apos;m a Full Stack Developer who has an eye for design
        </p>
      </motion.div>

      {/* BASE TEXT */}
      <p
        className={cn(
          "w-[800px] p-6 text-center text-2xl",
          "text-neutral-800 dark:text-neutral-200",
        )}
      >
        First rule of fight club is you do not talk about fight club
      </p>
    </div>
  );
};

export default MaskCursor;


// utils/useMousePosition.tsx
import { useState, useEffect } from "react";

const useMousePosition = () => {
  const [mousePosition, setMousePosition] = useState({ x: null, y: null });

  const updateMousePosition = (e: any) => {
    setMousePosition({ x: e.clientX, y: e.clientY });
  };

  useEffect(() => {
    window.addEventListener("mousemove", updateMousePosition);

    return () => window.removeEventListener("mousemove", updateMousePosition);
  }, []);

  return mousePosition;
};

export default useMousePosition;


// public/CircleSvg.svg
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="30" cy="30" r="30" fill="#D9D9D9"/>
</svg>

Text Reveal

Preview

Paisa hi paisa hoga πŸ’°πŸ’ΈπŸ€‘

Mast plan hai 😎🧠

Source Code
Source Code
tsx
"use client";

import {
  motion,
  useMotionTemplate,
  useMotionValue,
  useSpring,
} from "framer-motion";
import { useRef } from "react";
import { cn } from "@/lib/utils";

const HorizontalProgressReveal = () => {
  const containerRef = useRef<HTMLDivElement>(null);

  // Motion value for reveal progress (0 β†’ 100)
  const reveal = useMotionValue(0);
  const smoothReveal = useSpring(reveal, {
    stiffness: 300,
    damping: 30,
  });

  // Bind to CSS mask size
  const maskSize = useMotionTemplate`${smoothReveal}%`;

  const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
    const rect = e.currentTarget.getBoundingClientRect();
    const x = e.clientX;

    // Convert mouse X to percentage
    const progress = ((x - rect.left) / rect.width) * 100;

    // Clamp between 0–100
    reveal.set(Math.max(0, Math.min(100, progress)));
  };

  const handleMouseLeave = () => {
    reveal.set(0);
  };

  return (
    <div
      ref={containerRef}
      onMouseMove={handleMouseMove}
      onMouseLeave={handleMouseLeave}
      className={cn(
        "relative flex h-[40vh] items-center justify-center overflow-hidden",
      )}
    >
      {/* REVEALED TEXT */}
      <motion.div
        className={cn(
          "absolute inset-0 flex items-center justify-center",
          "bg-neutral-100 text-white dark:bg-zinc-900",
          "mask-no-repeat [-webkit-mask-repeat:no-repeat]",
          "mask-left [-webkit-mask-position:left]",
          "mask-[linear-gradient(#000,#000)] [-webkit-mask-image:linear-gradient(#000,#000)]",
          "dark:mask-[linear-gradient(#fff,#fff)] dark:[-webkit-mask-image:linear-gradient(#fff,#fff)]",
        )}
        style={{
          WebkitMaskSize: maskSize,
        }}
      >
        <p
          className={cn(
            "max-w-3xl px-6 text-center text-3xl font-semibold md:text-4xl",
            "text-neutral-800 dark:text-neutral-200",
          )}
        >
          Paisa hi paisa hoga πŸ’°πŸ’ΈπŸ€‘
        </p>
      </motion.div>

      {/* BASE TEXT */}
      <p
        className={cn(
          "max-w-3xl px-6 text-center text-3xl font-semibold md:text-4xl",
          "text-neutral-800 dark:text-neutral-200",
        )}
      >
        Mast plan hai 😎🧠
      </p>
    </div>
  );
};

export default HorizontalProgressReveal;


// utils/useMousePosition.tsx
import { useState, useEffect } from "react";

const useMousePosition = () => {
  const [mousePosition, setMousePosition] = useState({ x: null, y: null });

  const updateMousePosition = (e: any) => {
    setMousePosition({ x: e.clientX, y: e.clientY });
  };

  useEffect(() => {
    window.addEventListener("mousemove", updateMousePosition);

    return () => window.removeEventListener("mousemove", updateMousePosition);
  }, []);

  return mousePosition;
};

export default useMousePosition;
Share this post