Skip to content

shadcn.io is an independent project built on shadcn/ui

Components

Free

3D Card for Svelte

A card that turns toward the pointer while its layers rise to their own depths. For Svelte with Ark UI, when a flat card should feel like an object.

UI library: Ark UI

A card that turns to face the pointer, and its layers lift off it to different heights, like the pages of a pop-up book.

Use it for one card worth a closer look: a product, a pricing tier, a featured post. A whole grid of them gets busy, and a still Card is the better choice for content people read. In Svelte the pointer's place goes into two CSS variables and CSS transforms do the rest, so nothing else is installed.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.
Use email insteadNo credit card

Anatomy

3D Card anatomy: root, body, item z 50, item z 100, item z 20
Card3D
โ””โ”€โ”€ Card3DBody
    โ””โ”€โ”€ Card3DItem (one per layer)

Usage

<script lang="ts">
import {
  Card3D, Card3DBody, Card3DItem,
} from "@/components/ui/card-3d";
</script>
<Card3D>
  <Card3DBody class="max-w-sm rounded-xl border bg-card p-6">
    <Card3DItem class="font-semibold text-xl" translateZ={50}>
      Make things float in air
    </Card3DItem>
    <Card3DItem class="mt-4 w-full" translateZ={100}>
      <img alt="Mountains above the clouds" class="rounded-lg" src="/peaks.jpg" />
    </Card3DItem>
  </Card3DBody>
</Card3D>

Accessibility

The tilt and the depth are decoration: nothing is announced, and everything in the card reads the same when it lies flat. The tilt follows a mouse or a pen. When focus moves to a link or a button inside the card, the layers rise as they do on hover, so a keyboard user sees the same depth. With reduced motion the card stays flat and the layers stay where they are.

Demos

Rotate

Layers can turn and slide as well as rise. The badge turns with rotateZ, the photo tips back with rotateX and lifts a little with translateY, and the label shifts with translateX. Each value is the layer's pose while the card is hovered, so it settles flat again when the pointer leaves. A number is in pixels or degrees; a string such as "2rem" is used as it is.

Preview width

Tilt

The same card twice: tilt set to 5 and to 20. It's how far the card turns at its edges, in degrees, and 10 by default. Keep it low for a large card or a calm page; a high value suits a small card that should feel playful.

Preview width

A post card whose title is the link. The layer is rendered as the anchor itself, so its depth and the link are one element and there is no wrapper to tab through. Tab to it and the layers rise, as they do under the pointer.

Card3DItem takes asChild and passes its props and depth to the one element inside it.

Preview width

FAQ

How do I make a layer pop out of the 3D Card?

Wrap it in a Card3DItem and give it translateZ, how far it rises toward the viewer while the card is hovered. 40 to 100 reads well, and layers at different values part from each other. translateX, translateY and the three rotate values move it too. With no pointer over the card every layer lies flat.

How do I make the tilt stronger or subtler?

Set tilt on Card3D: the angle, in degrees, the card reaches at its edges, 10 by default. The Tilt demo on this page compares 5 and 20. The depth comes from the perspective-[1000px] class on Card3D: a smaller value makes the tilt look steeper, a larger one flatter.

Can a Card3DItem be a link, a button or an image in Svelte?

Yes. Put the element inside a Card3DItem, or render the item as the element itself, as the Link demo on this page does for this target. The item only adds a class and a style, so the element keeps its own role, focus and events.

Why does a layer stay flat inside a wrapper?

Card3DBody keeps its direct children in 3D, so an item inside one plain wrapper still rises. An item nested deeper needs every wrapper above it to keep 3D too: add the transform-3d class to each. overflow-hidden, a filter or opacity under 1 on a wrapper flattens what's inside it.

What happens with reduced motion or on a touch screen?

With reduced motion the card never tilts and the layers stay in place. On a touch screen there's no hover: the card mostly lies flat while a finger scrolls the page, and the layers rise only when something inside takes focus. Keep everything the card says readable when it's flat.

Does the 3D Card re-render on every pointer move?

No. The pointer handler writes two CSS variables on the card's element and changes no state, so Svelte renders nothing as it moves. The tilt and the layers are CSS transforms with a short transition, which the browser draws without layout.

How do I install 3D Card in Svelte?

Run the command in the Installation section on this page. It adds Card3D, Card3DBody and Card3DItem to your ui folder, with no stylesheet: the tilt and the depth are transitions in the classes. The command uses the shadcn-svelte CLI.

API Reference

Card3D

<div> data-slot="card-3d"

PropType
tilt?number

How far the card turns toward the pointer at its edges, in degrees.

Default
10
class?string

Merged with the part's classes.

Card3DBody

<div> data-slot="card-3d-body"

PropType
class?string

Merged with the part's classes.

CSS variableType
--card-3d-tiltstring

Card3DItem

<div> data-slot="card-3d-item"

PropType
rotateX?number | string

Its turn about the horizontal axis while the card is hovered: degrees as a number, or any CSS angle.

Type
number | string
Default
0
rotateY?number | string

Its turn about the vertical axis while hovered. @default 0.

Type
number | string
Default
0
rotateZ?number | string

Its turn in the card's plane while hovered. @default 0.

Type
number | string
Default
0
translateX?number | string

How far it moves across while the card is hovered: pixels as a number, or any CSS length.

Type
number | string
Default
0
translateY?number | string

How far it moves down while hovered. @default 0.

Type
number | string
Default
0
translateZ?number | string

How far it rises toward the viewer while hovered: the depth that sets it apart from the card. 40 to 100 reads well.

Type
number | string
Default
0
asChild?Snippet<[PropsFn]>

Render your own element: a snippet that gets the part's props to spread on it.

Type
Snippet<[PropsFn]>
class?string

Merged with the part's classes.

CSS variableType
--card-3d-itemstring