3D Card Effect

A card perspective effect, hover over the card to elevate card elements.

Make things float in air
Hover over this card to unleash the power of CSS perspective
thumbnail
Try now →
Sign up

Examples

With Rotation

Make things float in air
Hover over this card to unleash the power of CSS perspective
thumbnail
Try now →
Sign up

Installation

Install Dependencies

npm i svelte-motion clsx tailwind-merge

Add util file

src/lib/utils/cn.ts
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
        
export function cn(...inputs: ClassValue[]) {
    return twMerge(clsx(inputs));
}

Copy the source code

src/lib/components/ui/ThreeDCardEffect/CardContainer.svelte
<script lang="ts">
	import { cn } from '$lib/utils';

	export let className: string | undefined = undefined;
	export let containerClassName: string | undefined = undefined;
	export let isMouseEntered = false;

	let containerRef: HTMLDivElement;

	const handleMouseMove = (e: MouseEvent) => {
		if (!containerRef) return;
		const { left, top, width, height } = containerRef.getBoundingClientRect();
		const x = (e.clientX - left - width / 2) / 25;
		const y = (e.clientY - top - height / 2) / 25;
		containerRef.style.transform = `rotateY(${x}deg) rotateX(${y}deg)`;
	};

	const handleMouseEnter = (e: MouseEvent) => {
		isMouseEntered = true;
		if (!containerRef) return;
	};

	const handleMouseLeave = (e: MouseEvent) => {
		if (!containerRef) return;
		isMouseEntered = false;
		containerRef.style.transform = `rotateY(0deg) rotateX(0deg)`;
	};
</script>

<div
	class={cn('flex items-center justify-center py-20', containerClassName)}
	style="perspective: 1000px;"
>
	<div
		bind:this={containerRef}
		on:mouseenter={handleMouseEnter}
		on:mousemove={handleMouseMove}
		on:mouseleave={handleMouseLeave}
		class={cn(
			'relative flex items-center justify-center transition-all duration-200 ease-linear',
			className
		)}
		style="transform-style: preserve-3d;"
	>
		<slot />
	</div>
</div>
src/lib/components/ui/ThreeDCardEffect/CardBody.svelte
<script lang="ts">
	import { cn } from '@/utils';

	export let className: string;
</script>

<div
	class={cn(
		'h-96 w-96 [transform-style:preserve-3d]  [&>*]:[transform-style:preserve-3d]',
		className
	)}
>
	<slot />
</div>
src/lib/components/ui/ThreeDCardEffect/CardItem.svelte
<script lang="ts">
	import { cn } from '$lib/utils';

	export let className: string | undefined = undefined;
	export let translateX: number | string | undefined = 0;
	export let translateY: number | string | undefined = 0;
	export let translateZ: number | string | undefined = 0;
	export let rotateX: number | string | undefined = 0;
	export let rotateY: number | string | undefined = 0;
	export let rotateZ: number | string | undefined = 0;
	export let isMouseEntered: boolean = false;

	let ref: HTMLDivElement;

	$: isMouseEntered, handleAnimations();

	const handleAnimations = () => {
		if (!ref) return;
		if (isMouseEntered) {
			ref.style.transform = `translateX(${translateX}px) translateY(${translateY}px) translateZ(${translateZ}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) rotateZ(${rotateZ}deg)`;
		} else {
			ref.style.transform = `translateX(0px) translateY(0px) translateZ(0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg)`;
		}
	};
</script>

<div
	bind:this={ref}
	class={cn('w-fit transition duration-200 ease-linear', className)}
	{...$$props}
>
	<slot />
</div>
src/lib/components/ui/ThreeDCardEffect/index.ts
import CardContainer from './CardContainer.svelte';
import CardBody from './CardBody.svelte';
import CardItem from './CardItem.svelte';

export { CardContainer, CardBody, CardItem };

Props

CardContainer
Prop Type Description
className string | undefined The CSS class to be applied to the container
containerClassName string | undefined The CSS class to be applied to the outer container
isMouseEntered boolean Default: false. Bind the value of a parent isMouseEntered to this so that it can be used for items.
CardBody
Prop Type Description
className string | undefined The CSS class to be applied to the body
CardItem
Prop Type Description
className string | undefined The CSS class to be applied to the item.
translateX number | string | undefined The X translation of the item.
translateY number | string | undefined The Y translation of the item.
translateZ number | string | undefined The Z translation of the item.
rotateX number | string | undefined The X rotation of the item.
rotateY number | string | undefined The Y rotation of the item.
rotateZ number | string | undefined The Z rotation of the item.
isMouseEntered boolean Default: false. Pass in whether the mouse hase entered the container