Perspective and CSS 3D transforms
CSS transforms can move elements along the z-axis and rotate them around three-dimensional axes. Perspective projects that virtual depth onto a flat screen: nearby surfaces appear larger, distant surfaces smaller, and rotations gain believable foreshortening.
A reliable scene separates the camera, the 3D object, and its faces. That structure prevents transforms from fighting each other and makes hover, focus, and reduced-motion behavior easier to control.
Separate the scene, object, and faces
Put perspective on a parent scene. Add transform-style: preserve-3d to the object whose children should retain z-depth. Position and rotate individual faces inside that object.
A smaller perspective distance creates stronger distortion; a larger value looks flatter. Start around 800–1200px for interface cards and adjust according to the object's size.
- perspective
- Defines the camera distance for transformed descendants.
- transform-style
- Keeps child transforms in a shared 3D space when set to
preserve-3d. - backface-visibility
- Hides the reverse side of a rotated surface in card-flip patterns.
.scene { perspective: 900px; }
.card {
position: relative;
transform-style: preserve-3d;
transition: transform 600ms cubic-bezier(.2,.7,.2,1);
}
.card.is-flipped { transform: rotateY(180deg); }
.card__face {
position: absolute;
inset: 0;
backface-visibility: hidden;
}
.card__back { transform: rotateY(180deg); }Property perspective versus perspective()
The perspective property gives sibling objects one shared camera and vanishing point. The perspective() transform function applies perspective inside a single element's transform list, so each element can appear to have its own camera.
Transform functions are applied in order and do not generally commute. perspective() rotateY() translateZ() can produce a different result from the same functions rearranged. Keep a documented order and test from the final interaction states.
Avoid accidental flattening
Some grouping properties flatten descendants even when preserve-3d is present. Clipping, filtering, and certain opacity or blending combinations can collapse the scene into the parent's plane.
If a scene must also clip rounded corners, add a separate outer wrapper for clipping and keep the 3D object inside it. Separating responsibilities is more dependable than stacking every effect on one box.
Design readable, controllable motion
Large rotations can make text blurry or hard to track. Keep text near its resting plane, avoid tiny type on angled surfaces, and provide a non-animated state under reduced-motion preferences.
Do not make important content available only on hover. A flip card should also respond to keyboard activation and expose its state through an actual button or other semantic control.
@media (prefers-reduced-motion: reduce) {
.card { transition: opacity 160ms ease; transform: none; }
.card__back { transform: none; }
}Live catalog examples
These production examples use perspective / transform. Open any effect to inspect its complete source and experiment in the browser editor.
Browser support
Support notes refer to stable, unprefixed behavior unless a prefix is explicitly listed.
| Feature | Browsers | Note |
|---|---|---|
| 3D transforms / perspective | All modern browsers | Current versions do not require prefixes. |
| transform-style: preserve-3d | All modern browsers | Grouping properties can still force descendants to flatten. |
| backface-visibility | All modern browsers | Test antialiasing and focus behavior on both faces. |
Common pitfalls
rotateY is used without perspective
Add a camera with parent
perspectiveor theperspective()transform function.preserve-3d is missing
Apply it to the object whose transformed children must share depth.
Transform functions are reordered
Order changes the coordinate system and therefore the final geometry.
Clipping and 3D live on the same element
Use separate wrappers so rounded clipping does not unexpectedly flatten the scene.
Frequently asked questions
- What perspective value should I use?
- Start around 800–1200px for card-sized UI. Smaller values exaggerate depth; larger values flatten it.
- Why can I see mirrored text?
- The back face is visible. Add `backface-visibility: hidden` to each face.
- Why are my children still flat?
- Check `transform-style: preserve-3d` and look for an ancestor property that forces flattening.
- Are 3D transforms accessible?
- They can be when content remains semantic, interactions work without hover, and reduced-motion users receive a stable alternative.
Related guides
- :hoverInteraction effects with :hover and transitionsDesign responsive hover feedback that also works for keyboards, touch input, and reduced-motion users.
- @keyframesCSS animations with @keyframesBuild reliable CSS animation timelines, combine multiple motions, and keep them smooth and accessible.
- --custom-propertyThemeable effects with CSS custom propertiesTurn repeated values into a clear component API and animate typed custom properties safely with @property.
- backdrop-filterFrosted glass with backdrop-filterBlur and adjust the scene behind translucent surfaces without sacrificing contrast, fallback quality, or scrolling performance.
Last updated:
All guides