The Real World - Practical 3D Components
Apply your CSS 3D skills to real-world projects. This final tutorial in the series shows you how to build a layered parallax header and a 3D cylindrical carousel.
Part of the series: Unlocking CSS 3D
- Part 1The Mental Model - Before You Write a Line of CSS 3D
- Part 2The Foundation - Mastering 2D Transforms
- Part 3Entering the Third Dimension - The Flipping Card
- Part 4Assembling a 3D Object - The CSS Cube
- Part 5The Final Polish - Interactivity, Lighting, and Performance
- Part 6The Real World - Practical 3D Components
Welcome to the final article of Unlocking CSS 3D. Over the past four articles, we have built a powerful foundation. We started with a mental model, mastered 2D transforms, constructed a flipping card, and finally assembled and polished a fully interactive 3D cube. You have all the primitive skills.
Now, we answer the most important question: "How do I use this in a real project?"
This article is about application. We will move beyond abstract shapes and build two practical, real-world components that use 3D transforms to solve common UI problems in elegant and engaging ways. These examples will serve as a blueprint, giving you the confidence to integrate 3D effects into your own work.
Project 1: The Layered Parallax Header
The Goal: Create an immersive hero section where different layers of content move at different speeds on scroll, creating an illusion of depth. This is a subtle but powerful effect that makes a page feel more dynamic and premium.
The Core Concept: We will use the translateZ transform. Elements with a negative translateZ are pushed "further away" into the 3D scene. Thanks to perspective, these distant elements will appear to move more slowly when the page is scrolled, creating the parallax effect naturally.
1. Setting up the Scene
We need a container that will act as our 3D "scene." This container needs two crucial properties: a perspective to create the 3D space and transform-style: preserve-3d so its children can exist at different depths. We also need to control its scroll behavior.
// app/components/ParallaxHeader.tsx
'use client';
import { useState, useEffect, useRef } from 'react';
export const ParallaxHeader = () => {
const [scrollY, setScrollY] = useState(0);
useEffect(() => {
const handleScroll = () => {
setScrollY(window.scrollY);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return (
<div
className='h-[150vh] bg-slate-900' // Extra height to allow for scrolling
>
<header className='sticky top-0 h-screen w-full [perspective:1000px] overflow-hidden'>
{/* The 3D stage that moves with the scroll */}
<div
className='w-full h-full [transform-style:preserve-3d]'
style={{ transform: `translateZ(${-scrollY}px)` }} // This moves the whole scene "backwards"
>
{/* Our layers will go here */}
</div>
</header>
</div>
);
};The key is transform: translateZ(${-scrollY}px). As the user scrolls down, the entire 3D stage moves away from the viewer, into the screen.
2. Creating and Positioning the Layers
Now, let's add our content layers. Each layer will be a div positioned absolutely. The magic is in giving them different translateZ values.
- A
divwithtranslateZ(-500px)will be very far away. - A
divwithtranslateZ(0px)will be at the baseline (the screen plane). - A
divwithtranslateZ(200px)will be closer to the viewer.
To counteract the shrinking effect of negative translateZ, we'll also need to apply a corresponding scale to make distant objects appear the correct size initially. The formula is scale = 1 - (translateZ / perspective).
// Inside the [transform-style:preserve-3d] div
// Layer 1: The background (very far away)
// translateZ(-900px) inside a 1000px perspective needs a scale of 1 - (-900/1000) = 1.9
<div className="absolute inset-0 flex items-center justify-center [transform:translateZ(-900px)_scale(1.9)]">
<h1 className="text-8xl font-black text-slate-700">BACKGROUND</h1>
</div>
// Layer 2: The mid-ground
// translateZ(-400px) -> scale(1.4)
<div className="absolute inset-0 flex items-center justify-center [transform:translateZ(-400px)_scale(1.4)]">
<p className="text-4xl font-bold text-sky-400">Mid-ground Content</p>
</div>
// Layer 3: The foreground (closest to the user)
<div className="absolute inset-0 flex items-center justify-center [transform:translateZ(0px)_scale(1)]">
<h2 className="text-6xl font-extrabold text-cyan-300 bg-slate-900/50 p-4 rounded-lg">
FOREGROUND
</h2>
</div>The Result: As you scroll down the page, the entire scene recedes. But because the "BACKGROUND" text is positioned much further away in Z-space, it appears to move much slower than the "FOREGROUND" text. You've created a silky-smooth parallax effect using pure CSS transforms, driven by a single state update in React.
Project 2: The 3D Carousel
The Goal: Build a carousel that displays its items in a 3D cylindrical shape, rather than a flat line. This adds a visually impressive and tactile feel to a very common UI pattern.
The Core Concept: We will arrange N items around a circle. Using some basic trigonometry, we can calculate the rotateY and translateZ for each item. The entire cylinder can then be rotated by updating a single CSS variable.
1. Setting up the Carousel Structure
// app/components/Carousel3D.tsx
'use client';
import { useState } from 'react';
const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'];
const anglePerItem = 360 / items.length;
// Radius of the carousel circle in pixels
const radius = 200;
export const Carousel3D = () => {
const [rotation, setRotation] = useState(0);
return (
<div className='w-full h-96 flex flex-col items-center justify-center gap-8'>
<div className='w-[200px] h-[120px] [perspective:1000px]'>
<div
className='w-full h-full [transform-style:preserve-3d] transition-transform duration-500'
style={{ transform: `rotateY(${rotation}deg)` }}
>
{/* Carousel items will go here */}
</div>
</div>
<div className='flex gap-4'>
<button
onClick={() => setRotation(rotation + anglePerItem)}
className='...'
>
Prev
</button>
<button
onClick={() => setRotation(rotation - anglePerItem)}
className='...'
>
Next
</button>
</div>
</div>
);
};We have a scene, a rotating stage, and state to control the rotation.
2. Positioning the Items on the Circle
This is the key. For each item, we need to:
- Rotate it into its position on the circle (
rotateY). - Push it out from the center to form the circle's edge (
translateZ).
// Inside the rotating stage div
{
items.map((item, index) => {
const itemAngle = index * anglePerItem;
return (
<div
key={item}
className='absolute w-[200px] h-[120px] bg-sky-800/80 border-2 border-sky-400/50 rounded-lg
flex items-center justify-center text-2xl font-bold'
style={{
// 1. Rotate the item to its spot on the circle's edge
// 2. Push it out from the center by the radius
transform: `rotateY(${itemAngle}deg) translateZ(${radius}px)`,
}}
>
{item}
</div>
);
});
}The Result: You now have a cylinder of items. Clicking the "Next" and "Prev" buttons updates the rotation state, which smoothly spins the entire stage, bringing the next item to the front. It's a fully functional, visually stunning carousel built on the exact same rotateY and translateZ principles we used to build our cube.
Your Journey Is Just Beginning
This series was designed to give you a new set of tools, a new vocabulary for design, and a new dimension to play in. You started with the abstract theory and have now applied it to build real, practical components.
You are no longer just placing rectangles on a screen. You are an architect of immersive digital spaces.
The principles you've learned—perspective, preserve-3d, the "orient then push" model, performant animation with transform—are universal. You can now use them to deconstruct impressive effects you see on the web, and more importantly, to invent your own.
Go build something amazing.