Wireframe of a square tunnel receding into the page

3D in CSS

Last Tended

Status: seed

3D Space

The perspective property enables a 3D-space for child elements. Its value determines the strength of a child's z-transform effect. Large perspective values cause small transformations, small values cause large transformations.

/index.css

.parent {
    perspective: 800px;
}
Arrow pointing up labelled Y. Arrow pointing right labelled X. Arrow pointed towards to user labelled Z.

Vanishing point

perspectiveOrigin defines the vanishing point. It's default value is 50% 50%. Centered horizontally & vertically.

/index.css

.parent {
    perspective: 800px;
    perspective-origin: 50% 50%;
}
Arrow pointing up labelled Y. Arrow pointing right labelled X. Arrow pointed towards to user labelled Z.

translate3d

  • translateX(...) moves a child horizontally.
  • translateY(...) moves a child vertically.
  • translateZ(...) moves a child closer or further away. The strength of the effect is determined by the value of perspective (mentioned above).

/index.css

.parent {
    perspective: 800px;
    perspective-origin: 50% 50%;
}

.child {
    transform: translate3d(0px, 0px, -500px);
}
Arrow pointing up labelled Y. Arrow pointing right labelled X. Arrow pointed towards to user labelled Z.

rotate3d

rotate3d rotates an element around the x, y or z axis. The first 3 arguments define the x, y & z vector. The last defines the angle.

/index.css

.parent {
    perspective: 800px;
    perspective-origin: 50% 50%;
}

.child {
    transform: translate3d(0px, 0px, -500px);
    transform: rotate3d(1, 0, 0, 0deg);
}
x
y
z
Arrow pointing up labelled Y. Arrow pointing right labelled X. Arrow pointed towards to user labelled Z.

Use Case: Screen Realestate

When a web page transitions from being displayed on a desktop to a mobile device, there is less screen realestate to work with. When this happens, it is common to remove lower-priority elements from the UI (User Interface). Rotating elements on the y-axis provides more screen realestate to work this. This allows more of the UI to remain on the screen when transitioning to smaller devices.

Drag the bottom-right corner of the list below. As the width gets smaller, the ul rotates on the y-axis. Providing more space for content to the right of the list.

localhost:3000

const ul = document.querySelector('ul')
// Increase to get more of an effect
const strength = 0.1

function rotateUl() {
   const windowWidth = window.innerWidth
   const ulWidth = ul.getBoundingClientRect().width
   const rotate = Math.max((windowWidth - ulWidth) * strength, 0)

   ul.style.transform = `perspective(400px) rotateY(${rotate}deg)`
}

function onMouseDown() {
   window.addEventListener("mousemove", rotateUl)
}

function onMouseUp() {
   window.removeEventListener("mousemove", rotateUl)
}

ul.onmousedown = onMouseDown
ul.onmouseup = onMouseUp
Images of elevator floor numbers

This does come with a cost. The greater the rotation, the more readability is lost. However, we don't always need 100% readability. Graphic designer David Carson once designed numbers for the floors of a hotel. They would be the first thing a guest sees when stepping off the elevator. He created images that are a combination of the number & the word of the number (with missing letters) in unusual arrangements. His design isn't readable, but is understandable.

Use Case: Different Perspective

Sometimes a thing can be better communicated when shown from multiple perspectives. Below is an wireframe of a web page I used in a presentation. I needed to communicate there are 3 elements in the top-right corner, stacked on top of 1 another. By translating & rotating this wireframe in a 3D-space, I can show it from a 2nd perspective.

localhost:3000

#parent {
   perspective: 1000px;
}

#child {
   width: 100%;
   height: 100%;

   position: relative;

   perspective: 1000px;
   transform-style: preserve-3d;
   transition: transform var(--time);

   border: 1px solid var(--black);
}

#parent[data-rotate=true] #child {
   transform: translate3d(0, 0, -200px) rotate3d(0.1, -0.1, 0.1, 86deg);
}

.grandChild {
   transition: transform var(--time);
}

#parent[data-rotate=true] .grandChild {
   transition-delay: var(--time);
}

#parent[data-rotate=true] .grandChild:nth-child(2) {
   transform: translate3d(0, 0, 20px);
}

#parent[data-rotate=true] .grandChild:nth-child(3) {
   transform: translate3d(0, 0, 40px);
}

Above, the transform-style: preserve-3d is set on the #child element. This makes children of a transformed element exist in their own 3D-space. Without it, .grandChild elements will be flattened to the plane of the #child element. It you remove that line, you will see .grandChild elements don't translate.

Other Use Cases

  • When explaining a process, to help the user maintain context when moving from 1 step to the next.
  • A different approach to a presentation slide deck.
  • To communicate complex architecture from big picture to low-level detail without overwhelming the user. Through the use of zoom to show / hide information.
  • Flair

Where to Next?

A sci-fi robot taxi driver with no lower body
└── CSS
Arrow pointing down
YOU ARE HERE
├── 3D
├── Alignment Click Target
├── Box Sizing
├── Floating Image
├── Layout Component
├── Reset
└── Typography Setup