Hovering Card
The hovering card provides an interactive 3D card effect that responds to mouse movements, creating an engaging user experience.
V3cn
Here you will get components which you wont get anywhere.
Made by Vineet
Installation
npx v3cn add card
Make sure that you have installed your modules first and followed the introduction.
Utility File
// Util/cn.ts
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(...inputs));
}
Tailwind Config
// tailwind.config.ts
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./app/**/*.{js,ts,jsx,tsx,mdx}",
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
// Or if using `src` directory:
"./src/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
keyframes: {
smooth: {
"0%": { transform: "translateY(-5px)" },
"50%": { transform: "translateY(5px)" },
"100%": { transform: "translateY(-5px)" },
},
},
animation: {
float: "smooth 3s ease-in-out infinite",
},
},
},
plugins: [],
};
Usage
import { CardContainer, CardItem } from "@/components/ui/Card";
const Card = () => {
return (
<div className="flex justify-center items-center h-fit py-7 w-full px-2 ">
<div className="relative cursor-pointer animate-float md:block mr-8">
<CardContainer className=" cursor-pointer">
<div className="px-8 py-7 max-w-[400px] border-solid gap-5 bg-black flex flex-col justify-start item-center border-2 rounded-2xl">
<CardItem>
<img
src={"/V.png"}
alt={"platform"}
width={400}
height={400}
className="rounded-[3rem]"
/>
</CardItem>
<div className="px-4 flex flex-col justify-center items-start gap-5">
<h1 className="text-4xl text-white font-bold">V3cn</h1>
<h4 className="text-lg text-white">
Here you will get components which you wont get anywhere.
</h4>
<p className="text-purple-400">Made by Vineet & Team</p>
</div>
</div>
</CardContainer>
</div>
</div>
);
};
Here’s a detailed prop table for the CardContainer
and CardItem
components, including descriptions for each prop:
CardContainer Component Properties
Prop Name | Type | Description | Default Value |
---|---|---|---|
children | React.ReactNode | The content to be displayed within the card container. | undefined |
className | string | Custom CSS classes to apply to the card container for styling. | undefined |
containerClassName | string | Custom CSS classes to apply to the outer container that manages the perspective effect. | undefined |
Example Usage
<CardItem translateX={20} translateY={10}>
<h2>Your Content Here</h2>
</CardItem>
This format clearly outlines each prop's name, type, description, and default value, making it easy for users to understand how to use the CardItem
component. Let me know if you need any more adjustments or additional details!
Context Hook
useMouseEnter
:- Returns: An array containing the mouse enter state (
boolean
) and a setter function to update that state. - Usage: This hook should be used within a
MouseEnterProvider
to access the context state.
- Returns: An array containing the mouse enter state (
Example Usage
<CardContainer className="your-class" containerClassName="container-class">
<CardItem translateX={20} translateY={10}>
<h2>Your Content Here</h2>
</CardItem>
</CardContainer>
This prop table provides a clear reference for users implementing the CardContainer
and CardItem
components in their applications. Let me know if you need any further modifications or additional information!
Code
"use client";
import { cn } from "../utils/cn";
import React, {
createContext,
useState,
useContext,
useRef,
useEffect,
} from "react";
const MouseEnterContext = createContext<
[boolean, React.Dispatch<React.SetStateAction<boolean>>] | undefined
>(undefined);
const CardContainer = ({
children,
className,
containerClassName,
}: {
children?: React.ReactNode;
className?: string;
containerClassName?: string;
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const [isMouseEntered, setIsMouseEntered] = useState(false);
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
if (!containerRef.current) return;
const { left, top, width, height } =
containerRef.current.getBoundingClientRect();
const x = (e.clientX - left - width / 2) / 25;
const y = (e.clientY - top - height / 2) / 25;
containerRef.current.style.transform = `rotateY(${x}deg) rotateX(${y}deg)`;
};
const handleMouseEnter = (e: React.MouseEvent<HTMLDivElement>) => {
setIsMouseEntered(true);
if (!containerRef.current) return;
};
const handleMouseLeave = (e: React.MouseEvent<HTMLDivElement>) => {
if (!containerRef.current) return;
setIsMouseEntered(false);
containerRef.current.style.transform = "rotateY(0deg) rotateX(0deg)";
};
return (
<MouseEnterContext.Provider value={[isMouseEntered, setIsMouseEntered]}>
<div
className={cn("flex items-center justify-center", containerClassName)}
style={{
perspective: "1000px",
}}
>
<div
ref={containerRef}
onMouseEnter={handleMouseEnter}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
className={cn(
"flex items-center justify-center relative transition-all duration-200 ease-linear",
className
)}
style={{
transformStyle: "preserve-3d",
}}
>
{children}
</div>
</div>
</MouseEnterContext.Provider>
);
};
export const CardItem = ({
children,
className,
translateX = 0,
translateY = 0,
translateZ = 0,
rotateX = 0,
rotateY = 0,
rotateZ = 0,
...rest
}: {
as?: React.ElementType;
children: React.ReactNode;
className?: string;
translateX?: number | string;
translateY?: number | string;
translateZ?: number | string;
rotateX?: number | string;
rotateY?: number | string;
rotateZ?: number | string;
}) => {
const ref = useRef<HTMLDivElement>(null);
const [isMouseEntered] = useMouseEnter();
useEffect(() => {
handleAnimations();
}, [isMouseEntered]);
const handleAnimations = () => {
if (!ref.current) return;
if (isMouseEntered) {
ref.current.style.transform = `translateX(${translateX}px) translateY(${translateY}px) translateZ(${translateZ}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) rotateZ(${rotateZ}deg)`;
} else {
ref.current.style.transform =
"translateX(0px) translateY(0px) translateZ(0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg)";
}
};
return (
<div
ref={ref}
className={cn("w-fit transition duration-200 ease-linear", className)}
{...rest}
>
{children}
</div>
);
};
// Create a hook to use the context
export const useMouseEnter = () => {
const context = useContext(MouseEnterContext);
if (context === undefined) {
throw new Error("useMouseEnter must be used within a MouseEnterProvider");
}
return context;
};