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
 Try now →
 Sign up
<script lang="ts">
	import CardBody from './CardBody.svelte';
	import CardContainer from './CardContainer.svelte';
	import CardItem from './CardItem.svelte';
	let isMouseEntered = false;
</script>
<CardContainer bind:isMouseEntered className="inter-var">
	<CardBody
		className="bg-gray-50 relative group/card  dark:hover:shadow-2xl dark:hover:shadow-emerald-500/[0.1] dark:bg-black dark:border-white/[0.2] border-black/[0.1] w-auto sm:w-[30rem] h-auto rounded-xl p-6 border  "
	>
		<CardItem
			{isMouseEntered}
			translateZ="50"
			className="text-xl font-bold text-neutral-600 dark:text-white"
		>
			Make things float in air
		</CardItem>
		<CardItem
			{isMouseEntered}
			translateZ="60"
			className="text-neutral-500 text-sm max-w-sm mt-2 dark:text-neutral-300"
		>
			Hover over this card to unleash the power of CSS perspective
		</CardItem>
		<CardItem {isMouseEntered} translateZ="100" className="w-full mt-4">
			<img
				src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?q=80&w=2560&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
				height="1000"
				width="1000"
				class="h-60 w-full rounded-xl object-cover group-hover/card:shadow-xl"
				alt="thumbnail"
			/>
		</CardItem>
		<div class="mt-20 flex items-center justify-between">
			<CardItem
				{isMouseEntered}
				translateZ={20}
				className="px-4 py-2 rounded-xl text-xs font-normal dark:text-white"
			>
				Try now →
			</CardItem>
			<CardItem
				{isMouseEntered}
				translateZ={20}
				className="px-4 py-2 rounded-xl bg-black dark:bg-white dark:text-black text-white text-xs font-bold"
			>
				Sign up
			</CardItem>
		</div>
	</CardBody>
</CardContainer>Examples
With Rotation
Make things float in air
 Hover over this card to unleash the power of CSS perspective
 Try now →
 Sign up
<script lang="ts">
	import CardBody from './CardBody.svelte';
	import CardContainer from './CardContainer.svelte';
	import CardItem from './CardItem.svelte';
	let isMouseEntered = false;
</script>
<CardContainer bind:isMouseEntered className="inter-var">
	<CardBody
		className="bg-gray-50 relative group/card  dark:hover:shadow-2xl dark:hover:shadow-emerald-500/[0.1] dark:bg-black dark:border-white/[0.2] border-black/[0.1] w-auto sm:w-[30rem] h-auto rounded-xl p-6 border  "
	>
		<CardItem
			{isMouseEntered}
			translateZ="50"
			className="text-xl font-bold text-neutral-600 dark:text-white"
		>
			Make things float in air
		</CardItem>
		<CardItem
			{isMouseEntered}
			translateZ="60"
			className="text-neutral-500 text-sm max-w-sm mt-2 dark:text-neutral-300"
		>
			Hover over this card to unleash the power of CSS perspective
		</CardItem>
		<CardItem {isMouseEntered} translateZ="100" rotateX={20} rotateZ={-10} className="w-full mt-4">
			<img
				src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?q=80&w=2560&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
				height="1000"
				width="1000"
				class="h-60 w-full rounded-xl object-cover group-hover/card:shadow-xl"
				alt="thumbnail"
			/>
		</CardItem>
		<div class="mt-20 flex items-center justify-between">
			<CardItem
				{isMouseEntered}
				translateZ={20}
				translateX={-40}
				className="px-4 py-2 rounded-xl text-xs font-normal dark:text-white"
			>
				Try now →
			</CardItem>
			<CardItem
				{isMouseEntered}
				translateZ={20}
				translateX={40}
				className="px-4 py-2 rounded-xl bg-black dark:bg-white dark:text-black text-white text-xs font-bold"
			>
				Sign up
			</CardItem>
		</div>
	</CardBody>
</CardContainer>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 |