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'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