Rotate a card with rotateY and it just looks like a flat shape getting narrower. Perspective sets the viewing distance between the viewer and the z=0 plane, which is what gives the turn some depth. Children with positive depth grow, and children with negative depth shrink.
Card flips and rotating cubes depend on it, and so do most tilt-on-hover panels. You get it in one of two forms, the perspective property on a parent or the perspective() function inside a transform.
MDN’s 2026 documentation specifies that the property transforms the children of the element that declares it, never the element itself. A card that carries its own perspective therefore rotates but never foreshortens. The vanishing point stays at the parent’s center until perspective-origin moves it.
What is CSS perspective?
See the Pen
A Basic CSS Perspective Example by Nick Salloum (@callmenick)
on CodePen.
In plain terms, it sets the distance between the viewer and the z=0 plane. 3D-transformed elements then shrink or grow with depth instead of rendering flat.
The CSS Transforms Module Level 2 editor’s draft (W3C CSS Working Group, 2025) is explicit about the limits. Transformed elements stay flat planes with no depth, and perspective only changes how their 3D transforms project onto the screen.
The spec’s own example makes the difference easy to see. A 150px box rotated 50 degrees on the Y axis only narrows without perspective. Add perspective: 500px to its container and the near edge grows while the far edge shrinks.
The initial value is none, so nothing happens until you set one. It applies to transformable elements, it isn’t inherited, and the default vanishing point is 50% 50%, the center of the parent.
Support is solid. MDN lists the property as widely available since September 2015, and CSS3 3D transforms reach 97.49% global usage with partial support counted (Can I use, StatCounter data for September 2026).
Skip it and every rotateX or rotateY on a card reads as a flat squash.
How the perspective distance sets the strength of the effect
A smaller perspective value gives stronger distortion, and a larger one gives a subtler look. The W3C editor’s draft (CSS Working Group, 2025) calls 1000px a moderate amount of foreshortening and 200px an extreme amount.
Some tutorials get this backward and claim higher values strengthen the effect. MDN says it plainly. Large values cause a small transformation, small values a large one.
Valid values and units
The keyword none is the initial value and applies no perspective. Any length works for the distance, whether px, em, rem or cm. Negative values are syntax errors, so the declaration is dropped, and anything under 1px gets clamped to 1px.
Pixels stay the default for scene geometry. Whether to write the distance in px or rem follows the same logic as any other length, since rem scales with the root font size.
What happens when translateZ passes the perspective distance
An element moved along the z-axis scales by d divided by (d minus Z), where d is the perspective value and Z is the translateZ offset.
For a 500px perspective, the formula produces these scale factors.
| translateZ | Scale factor | Result |
|---|---|---|
| -500px | 0.5x | Half size |
| 0px | 1.0x | Unchanged |
| 250px | 2.0x | Twice as large |
| 400px | 5.0x | Five times as large |
| Above 500px | None | Not drawn |
At exactly 500px the element reaches the viewer’s eye and its corners stretch toward infinity. Past that point it sits behind the viewer and isn’t drawn.
If an element vanishes right after you bump translateZ, you’ve probably crossed this threshold.
Is perspective: 0 a valid value?
Depends who you ask.
Older reference copies say 0 or a negative value switches perspective off. Rocket Validator flags 0 as invalid and recommends none, or a small positive length such as 1px.
The current spec and MDN’s 2026 page agree with each other. 0 parses, values under 1px render as 1px, and negative values are errors.
The spec text is the reference here. In practice none is the off switch, while 0 gives you an extreme 1px perspective that rarely suits a layout.
Choosing a value
See the Pen
CSS3 Perspective Playground ✨ by Mehmet Burak Erman (@mburakerman)
on CodePen.
Start from the spec’s anchors, 1000px for a restrained effect and 200px for an exaggerated one. Raise the value as the element gets wider or the rotation gets steeper. And keep translateZ under half the value unless the disappearing act is what you want.
Width matters because a rotated element pushes its edges along the z-axis. By the spec formula, a 150px box at rotateY(50deg) under perspective 500px scales its near edge to about 1.13x and its far edge to about 0.90x.
Moving the vanishing point with perspective-origin
See the Pen
Simple Perspective preloader by creotip (@creotip)
on CodePen.
The perspective-origin property sets where the viewer looks at the scene on the X and Y axes, and that position becomes the vanishing point. The default is 50% 50%, the center of the parent.
It accepts the keywords left, center, right, top and bottom, alone or in pairs, plus lengths and percentages measured from the top left corner of the parent’s box. Negative values work too, such as -200% 200%, which place the viewer outside the box.
On its own the property does nothing. It moves the projection that the perspective property creates, and the two are separate declarations because perspective is not a shorthand for it.
Setting top moves the vanishing point to the top edge of the container, so the scene reads as viewed from above.
Don’t mix it up with transform-origin. The perspective-origin property sits on the parent and moves the viewer for every child at once, while transform-origin sits on the child and moves the pivot for that element’s own rotation and scaling.
Shared vanishing points are the use case the spec names. Several drawings on one page keep a common perspective when they share a perspective-origin.
Perspective property or perspective() function: which one fits?
See the Pen
Pure CSS Perspective Boxes (Animated) v3 – with Controls by Joshua Hibbert (@joshnh)
on CodePen.
Most of the time the property on a parent is the right call, because several children usually share one scene. The function is for the case where a single element needs its own depth.
| Feature | Property | Function |
|---|---|---|
| Written on | The parent element | The transformed element, inside transform |
| Affects | Direct 3D-transformed children | That element only |
| Vanishing point | Shared by all children | One per element |
| Origin set by | perspective-origin | transform-origin |
| Typical use | Card grids, flip cards, 3D scenes | One tilted image or hover effect |
The function compiles into the element’s transformation matrix. A value of perspective(500px) resolves to matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -0.002, 0, 0, 0, 1), where -0.002 is -1/d.
Order inside the transform list matters. Put perspective() first, because MDN documents transform lists as applied right to left, so the projection lands after the rotation and translation.
With the property, one declaration on the container gives a card grid, or a flip card with two faces, the same vanishing point. With the function, repeating it across a grid makes every card converge on its own center, and that looks inconsistent. It’s fine for one tilted image, and it’s the only route when each element needs a separate vanishing point.
Why perspective only reaches direct children, and what preserve-3d changes
An element only picks up its parent’s perspective matrix, and a flat parent ends the chain. Setting transform-style: preserve-3d on the middle element keeps it going.
The W3C editor’s draft (CSS Working Group, 2025) describes the accumulation rule. For each element in a 3D rendering context, the parent’s perspective matrix is multiplied in, then the walk moves up one level.
Say the container holds the perspective property and its child carries a 3D transform. If that child has 3D children of its own, it needs transform-style: preserve-3d as well. The grandchild only joins the container’s scene when the child preserves 3D. Without it, the grandchild’s rotateX just shortens it inside the child’s flat plane, as the spec’s nested example shows.
Transform-style is not inherited, so MDN’s 2026 page says it must be set on every non-leaf descendant in the chain.
A fair number of property values override preserve-3d and force the used value back to flat. The usual suspects are overflow other than visible or clip, opacity below 1, filter other than none, clip-path other than none, mask-image other than none and mix-blend-mode other than normal. Clip, isolation: isolate, mask-border-source and contain: paint do the same.
Two culprits show up constantly. One is overflow: hidden on a rounded card wrapper, the other is a fade-in that starts below opacity 1. The first flattens the scene permanently, the second until the fade finishes, and neither throws a warning.
Preserve-3d also creates a stacking context and a containing block for all descendants (CSS Working Group, 2025).
The direct-parent rule has a paper trail. In November 2011 a Mozilla engineer asked on the W3C www-style list whether the spec’s word “children” meant all descendants, and argued that limiting perspective to direct parents would be simpler.
How to build a 3D card flip with CSS perspective
See the Pen
3D Perspective cards with depth [CSS] by Fernando Cohen (@designfenix)
on CodePen.
The spec’s own flip example is the cleanest recipe. It uses a container with perspective, a wrapper with preserve-3d, two faces with backface-visibility: hidden, and a rotateY(180deg) trigger.
- Give the parent container perspective. The spec example uses 500px.
- Build the card wrapper at a fixed size (the example uses 200px by 300px) with position: relative, transform-style: preserve-3d and transition: transform 1s.
- Add two faces inside it, absolutely positioned at top: 0 and left: 0, each 100% wide and 100% tall.
- Set backface-visibility: hidden on both faces, so the mirrored reverse side never shows through.
- Pre-rotate the back face with transform: rotateY(180deg), so its content reads correctly after the turn.
- Toggle a class on the wrapper that sets transform: rotateY(180deg). The spec flips the card on click.
Duration and easing are the part worth tuning by eye, and a tool that generates CSS transitions shortens that loop.
Put perspective on the card itself and its own rotation gets no projection, so the flip reads as a flat squash. Forget preserve-3d and the faces flatten into the wrapper, with the back face sitting on top of the front. Leave out backface-visibility and you see the mirrored front face through the back.
Ready-made variations are collected in a set of CSS flip card examples.
When CSS perspective fails or breaks the layout
Usually the property is on the wrong element. The rest comes down to side effects of a non-none value and a couple of browser bugs, and each has its own fix.
Wrong element or missing 3D transform
| Symptom | Cause | Fix |
|---|---|---|
| Card tilts but looks flat | Perspective sits on the rotated element | Move it to the parent |
| Nothing moves in 3D | Children use only 2D transforms | Add rotateX, rotateY or translateZ |
| Span or link ignores transforms | Non-replaced inline boxes are not transformable | Set display to inline-block or block |
The second row has a simple reason. With only 2D transforms every point sits at Z=0, where the scale factor is d divided by d, exactly 1, so perspective has nothing to project.
Stacking context and fixed-position side effects
Any perspective value other than none turns the element into a stacking context and a containing block for fixed-position descendants (MDN, 2026).
A fixed header or modal placed inside the container then anchors to that container instead of the viewport. Its z-index also competes only with elements inside the same context.
The spec’s own editors flag the requirement as unnecessary in a draft note, and suspect web compatibility keeps them from removing it.
The way out is to keep fixed elements outside the perspective container, or wrap only the 3D elements in a smaller perspective parent.
Support, prefixes and known engine bugs
Can I use splits its 3D transform support rows at Chrome 36, Firefox 16, Safari 9 and Opera 23. Tohoho Web’s reference table names the same versions as the end of vendor prefixes, so prefixed code only matters for browsers older than those.
Internet Explorer 10 and 11 count as partial support and Opera Mini has none (Can I use, StatCounter data for September 2026).
Backface-visibility is the newest piece of the set. MDN’s 2026 page dates it to March 2022, so flip cards deserve a cross-browser compatibility pass on the oldest browsers you still support.
A couple of engine bugs were still open as of October 2026.
In Firefox, a display: table child of a perspective parent gets no perspective, and putting the property on the table affects the wrong box. Mozilla Bugzilla 726601 is still NEW after 14 years, last updated in 2022. The workaround is cheap. Wrap the table in a div and transform the div.
WebKitGTK renders the effect on the first frame only and ignores scroll updates. WebKit Bugzilla 285268 was reported on 2024-12-31 and was still NEW at its latest change on 2026-09-28.
CSS perspective examples
Funky CSS Show by Alkshendra Maurya
See the Pen
CSS Perspective example by Alkshendra Maurya (@alkshendra)
on CodePen.
Typo-Artistry from Noah Blon
See the Pen
3D CSS Typography by Noah Blon (@noahblon)
on CodePen.
Buttons that Make You Go WHOA
See the Pen
Perspective button hover effect by Comehope (@comehope)
on CodePen.
Get Deep with 3D Transforms
See the Pen
CSS 3D Transforms: Translation by Alvin Wan (@alvinwan)
on CodePen.
Loader Like You’ve Never Seen by Jhey
See the Pen
Side by Side Perspective Loader 😇 by Jhey (@jh3y)
on CodePen.
CSS3 Wonderland by Oliver Taylor
See the Pen
CSS3 3D Perspective by Oliver Taylor (@olivertaylor)
on CodePen.
Picture Perfect by Eriksen
See the Pen
3D perspective mouse hover image by Eriksen (@eriksenlezama)
on CodePen.
Isometric Beats by Hexagoncircle
See the Pen
CSSometric by Ryan Mulligan (@hexagoncircle)
on CodePen.
Film Buffs, Attention!
See the Pen
Divtober 27: Film by Alvaro Montoro (@alvaromontoro)
on CodePen.
Dive into 3D with interaminense
See the Pen
Css Perspective by Adriano Interaminense (@interaminense)
on CodePen.
The Future of Phones by jkantner
See the Pen
Isometric iPhones by Jon Kantner (@jkantner)
on CodePen.
Text That Floats Like a Feather
See the Pen
CSS Perspective Text Hover by James Bosworth (@bosworthco)
on CodePen.
Trapezoid Tales
See the Pen
Building a trapezoid by Claudio Procida (@claudiopro)
on CodePen.
Slide in Style with Alex
See the Pen
Smooth 3d perspective slider by Alex Nozdriukhin (@alexnoz)
on CodePen.
Objects, Eyes, and All That Jazz
See the Pen
css perspective transform by Hassaan ALalosy (@HassaanALalosy)
on CodePen.
Sphere That Keeps You Waiting
See the Pen
Perspective Sphere Preloader by Jon Kantner (@jkantner)
on CodePen.
Classic Comp Vibes
See the Pen
CSS 3D – Perspective Example by Adobe Inspire Magazine (@AdobeInspireMagazine)
on CodePen.
Tilt It Like Henry Desroches
See the Pen
Perspective Tilty Images by Henry Desroches (@xdesro)
on CodePen.
Fly Over Places with Akhil Sai Ram
See the Pen
World Places (CSS 3d hover) by Akhil Sai Ram (@akhil_001)
on CodePen.
See it in 3D with Jérémy Heleine
See the Pen
3D Perspective View by SitePoint (@SitePoint)
on CodePen.
CSS Perspective FAQ
Can perspective be animated?
See the Pen
3d Perspective Spheres by Jayshua Nelson (@jayshua)
on CodePen.
Yes. MDN lists the animation type as a length, so the value interpolates between two lengths like any other.
Transform-style behaves differently. It animates discretely, so preserve-3d switches on or off at once instead of easing.
How do I build a 3D cube with perspective?
See the Pen
CSS Perspective Transforms by Niall (@niallains)
on CodePen.
Set perspective on the parent and transform-style: preserve-3d on a cube wrapper, then place six absolutely positioned faces.
MDN’s demo uses 100px faces. Each takes translateZ(50px), half the face width, after rotateY for the back, left and right faces or rotateX for the top and bottom.
Does perspective change the layout of surrounding elements?
No. Transforms change shape and position without disrupting normal document flow, according to MDN, so neighbors keep their space.
The one layout side effect is positioning, since a non-none value makes the element the containing block for fixed-position descendants.
How do I turn perspective off again?
Set perspective: none, its initial value. Inside a transform list, perspective(none) does the same job and resolves to the identity matrix.
Resetting also removes the stacking context and fixed-position containing block that any other value creates, which is often what repairs a misplaced fixed header.
The Right Order for Setting Up CSS Perspective
CSS perspective works reliably when structure comes before tuning. Declare the property on the parent first. Then set preserve-3d down the chain, and leave the distance and origin for last.
The order matters because a wrong parent or a flattened middle element produces a flat result at any value. Changing pixels first only hides the structural fault.
The trade-off is shared geometry. A parent property gives every child one vanishing point, and separate vanishing points cost one perspective() function per element.
Everything here was checked in October 2026, with the Firefox display: table bug and the WebKitGTK scroll bug both still open. Recheck this advice if either closes, and test those engines first when a scene uses tables or reacts to scrolling.
- What is Backend in Web Development? - September 13, 2026
- What is Frontend Development? - September 11, 2026
- How to Make a Button in Figma: Design Best Practices - September 10, 2026


