04 / 20
All 20 in 20 CSS 3D Effects
- 01CSS 3D Transform Pipeline Reference Build
- 02Perspective Property Versus Perspective Function
- 03Perspective Origin as the Camera Position
- 04Transform Function Order Changes the Result
- 05Backface Visibility and Two Sided Elements
- 06Preserve 3D and What Flattens a Scene
- 07Building a CSS Cube from Six Faces
- 08Transform Origin in Three Dimensions
- 09TranslateZ Versus Scale for Depth
- 10Z Fighting on Coplanar 3D Surfaces
- 11Rotate3d and the Transform Matrix
- 12Ring Layout with Rotate and TranslateZ
- 13Depth Layering with Stacked Z Planes
- 14Pointer Tilt Cards with Parallax Depth Layers
- 15Page Turn Flipbook with Hinged Rotation
- 16Cube Face Navigation with Drag and Auto Rotate
- 17Extruded Synth Panel with Draggable Faders
- 18Perspective Floor Grid Geometry
- 19Nested 3D Contexts in a Pointer Steered Scene
- 20Helix Distribution with Trigonometric Angles
Transform Function Order Changes the Result
translateX(120px) rotate(45deg) and rotate(45deg) translateX(120px) land in different places, because each function operates in the coordinate space the previous one left behind. Both orders run on identical elements with their travel traced, and the same lesson is then repeated in 3D where it bites harder.
Untitled UI Pro: a 10,000+ component Figma UI kit · from $129 one-time
Untitled UI · Affiliate link
The code
<section class="d3-04" aria-labelledby="d3-04-title">
<h2 class="d3-04__sr" id="d3-04-title">Transform order compared</h2>
<div class="d3-04__theme">
<input class="d3-04__theme-input" type="checkbox" id="d3-04-theme">
<label class="d3-04__theme-label" for="d3-04-theme">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 3.5a8.5 8.5 0 0 0 0 17Z" fill="currentColor"/></svg>
<span class="d3-04__sr">Toggle dark theme</span>
</label>
</div>
<div class="d3-04__stage">
<div class="d3-04__viewport">
<header class="d3-04__head">
<span class="d3-04__kicker">Northbound Freight — dispatch board</span>
<strong>Two routes, same two moves</strong>
</header>
<div class="d3-04__lanes">
<section class="d3-04__lane">
<p class="d3-04__cap d3-04__cap--a"><span class="d3-04__tag d3-04__tag--a">Route A</span> <code>translateX() rotate()</code> — move first, then turn on the spot</p>
<div class="d3-04__field">
<span class="d3-04__origin" aria-hidden="true"></span>
<span class="d3-04__trace d3-04__trace--line" aria-hidden="true"></span>
<div class="d3-04__tile d3-04__tile--a">
<span class="d3-04__no">A-118</span>
<b>Depot → Bay 4</b>
<i class="d3-04__bars" aria-hidden="true"></i>
</div>
</div>
<ul class="d3-04__steps"><li><b>1</b> translateX(7rem)</li><li><b>2</b> rotate(45deg)</li></ul>
</section>
<section class="d3-04__lane">
<p class="d3-04__cap d3-04__cap--b"><span class="d3-04__tag d3-04__tag--b">Route B</span> <code>rotate() translateX()</code> — turn first, then move along the new axis</p>
<div class="d3-04__field">
<span class="d3-04__origin" aria-hidden="true"></span>
<span class="d3-04__trace d3-04__trace--arc" aria-hidden="true"></span>
<div class="d3-04__tile d3-04__tile--b">
<span class="d3-04__no">B-902</span>
<b>Depot → Bay 9</b>
<i class="d3-04__bars" aria-hidden="true"></i>
</div>
</div>
<ul class="d3-04__steps"><li><b>1</b> rotate(45deg)</li><li><b>2</b> translateX(7rem)</li></ul>
</section>
</div>
<p class="d3-04__note">Both parcels leave the same dock with the same two instructions. Route B travels down a rotated axis, so it lands somewhere else entirely.</p>
</div>
</div>
</section><section class="d3-04" aria-labelledby="d3-04-title">
<h2 class="d3-04__sr" id="d3-04-title">Transform order compared</h2>
<div class="d3-04__theme">
<input class="d3-04__theme-input" type="checkbox" id="d3-04-theme">
<label class="d3-04__theme-label" for="d3-04-theme">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 3.5a8.5 8.5 0 0 0 0 17Z" fill="currentColor"/></svg>
<span class="d3-04__sr">Toggle dark theme</span>
</label>
</div>
<div class="d3-04__stage">
<div class="d3-04__viewport">
<header class="d3-04__head">
<span class="d3-04__kicker">Northbound Freight — dispatch board</span>
<strong>Two routes, same two moves</strong>
</header>
<div class="d3-04__lanes">
<section class="d3-04__lane">
<p class="d3-04__cap d3-04__cap--a"><span class="d3-04__tag d3-04__tag--a">Route A</span> <code>translateX() rotate()</code> — move first, then turn on the spot</p>
<div class="d3-04__field">
<span class="d3-04__origin" aria-hidden="true"></span>
<span class="d3-04__trace d3-04__trace--line" aria-hidden="true"></span>
<div class="d3-04__tile d3-04__tile--a">
<span class="d3-04__no">A-118</span>
<b>Depot → Bay 4</b>
<i class="d3-04__bars" aria-hidden="true"></i>
</div>
</div>
<ul class="d3-04__steps"><li><b>1</b> translateX(7rem)</li><li><b>2</b> rotate(45deg)</li></ul>
</section>
<section class="d3-04__lane">
<p class="d3-04__cap d3-04__cap--b"><span class="d3-04__tag d3-04__tag--b">Route B</span> <code>rotate() translateX()</code> — turn first, then move along the new axis</p>
<div class="d3-04__field">
<span class="d3-04__origin" aria-hidden="true"></span>
<span class="d3-04__trace d3-04__trace--arc" aria-hidden="true"></span>
<div class="d3-04__tile d3-04__tile--b">
<span class="d3-04__no">B-902</span>
<b>Depot → Bay 9</b>
<i class="d3-04__bars" aria-hidden="true"></i>
</div>
</div>
<ul class="d3-04__steps"><li><b>1</b> rotate(45deg)</li><li><b>2</b> translateX(7rem)</li></ul>
</section>
</div>
<p class="d3-04__note">Both parcels leave the same dock with the same two instructions. Route B travels down a rotated axis, so it lands somewhere else entirely.</p>
</div>
</div>
</section>.d3-04 {
--d3-04-bg: #f6f0e4;
--d3-04-surf: #fffdf8;
--d3-04-line: #ddd2ba;
--d3-04-ink: #221c12;
--d3-04-mut: #6c6250;
--d3-04-a: #1f6b4f;
--d3-04-a: oklch(45% 0.11 158);
--d3-04-b: #b02c2c;
--d3-04-b: oklch(50% 0.17 25);
--d3-04-dist: clamp(3.5rem, 14vw, 7rem);
--d3-04-angle: 45deg;
--d3-04-beat: 6s;
--d3-04-font-display: "Bookman Old Style", "Rockwell", "Zilla Slab", Georgia, serif;
width: 100%;
min-height: 100vh;
display: block;
position: relative;
box-sizing: border-box;
background-color: var(--d3-04-bg);
background-image: radial-gradient(52rem 26rem at 90% -8%, color-mix(in oklab, var(--d3-04-b) 14%, transparent), transparent 60%), radial-gradient(48rem 24rem at -6% 104%, color-mix(in oklab, var(--d3-04-a) 16%, transparent), transparent 62%);
color: var(--d3-04-ink);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
line-height: 1.5;
}
.d3-04 *,
.d3-04 *::before,
.d3-04 *::after {
box-sizing: border-box;
}
.d3-04__sr {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.d3-04:has(.d3-04__theme-input:checked) {
--d3-04-bg: #17140f;
--d3-04-surf: #201c15;
--d3-04-line: #3b342a;
--d3-04-ink: #f4eee2;
--d3-04-mut: #a99d88;
--d3-04-a: #6fd8a8;
--d3-04-a: oklch(82% 0.13 160);
--d3-04-b: #f4907f;
--d3-04-b: oklch(78% 0.13 28);
}
@media (prefers-color-scheme: dark) {
.d3-04 {
--d3-04-bg: #17140f;
--d3-04-surf: #201c15;
--d3-04-line: #3b342a;
--d3-04-ink: #f4eee2;
--d3-04-mut: #a99d88;
--d3-04-a: #6fd8a8;
--d3-04-a: oklch(82% 0.13 160);
--d3-04-b: #f4907f;
--d3-04-b: oklch(78% 0.13 28);
}
.d3-04:has(.d3-04__theme-input:checked) {
--d3-04-bg: #f6f0e4;
--d3-04-surf: #fffdf8;
--d3-04-line: #ddd2ba;
--d3-04-ink: #221c12;
--d3-04-mut: #6c6250;
--d3-04-a: #1f6b4f;
--d3-04-a: oklch(45% 0.11 158);
--d3-04-b: #b02c2c;
--d3-04-b: oklch(50% 0.17 25);
}
}
@media (prefers-color-scheme: light) {
.d3-04:has(.d3-04__theme-input:checked) {
--d3-04-bg: #17140f;
--d3-04-surf: #201c15;
--d3-04-line: #3b342a;
--d3-04-ink: #f4eee2;
--d3-04-mut: #a99d88;
--d3-04-a: #6fd8a8;
--d3-04-a: oklch(82% 0.13 160);
--d3-04-b: #f4907f;
--d3-04-b: oklch(78% 0.13 28);
}
}
.d3-04__theme {
position: absolute;
inset-block-start: clamp(0.75rem, 3vw, 1.5rem);
inset-inline-end: clamp(0.75rem, 3vw, 1.5rem);
z-index: 20;
}
.d3-04__theme-input {
position: absolute;
opacity: 0;
inline-size: 100%;
block-size: 100%;
margin: 0;
cursor: pointer;
}
.d3-04__theme-label {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border: 1px solid var(--d3-04-line);
border-radius: 4px;
background: var(--d3-04-surf);
color: var(--d3-04-ink);
cursor: pointer;
box-shadow: 3px 3px 0 var(--d3-04-line);
}
.d3-04__theme-label svg {
inline-size: 20px;
block-size: 20px;
}
.d3-04__theme-input:focus-visible + .d3-04__theme-label {
outline: 2px solid var(--d3-04-b);
outline-offset: 3px;
}
.d3-04__stage {
display: grid;
grid-template-columns: minmax(0, 1fr);
place-items: center;
min-block-size: 100vh;
padding: clamp(3.5rem, 9vw, 5rem) clamp(0.75rem, 4vw, 2.5rem);
}
.d3-04__viewport {
inline-size: 100%;
max-inline-size: 64rem;
overflow: hidden;
display: grid;
gap: clamp(1rem, 3vw, 1.6rem);
padding: clamp(1rem, 3.5vw, 2rem);
border: 1px solid var(--d3-04-line);
border-radius: 6px;
background: var(--d3-04-surf);
box-shadow: 10px 10px 0 color-mix(in oklab, var(--d3-04-ink) 10%, transparent);
}
.d3-04__head {
display: grid;
gap: 0.15rem;
padding-block-end: 0.9rem;
border-block-end: 2px solid var(--d3-04-ink);
}
.d3-04__kicker {
font: 700 0.66rem/1.4 ui-monospace, Menlo, monospace;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--d3-04-mut);
}
.d3-04__head strong {
font: 700 clamp(1.3rem, 4.4vw, 2.1rem)/1.05 var(--d3-04-font-display);
letter-spacing: -0.01em;
}
.d3-04__lanes {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
gap: clamp(0.9rem, 3vw, 1.5rem);
}
.d3-04__lane {
display: grid;
gap: 0.7rem;
}
.d3-04__cap {
margin: 0;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.4rem;
font: 500 clamp(0.74rem, 2vw, 0.84rem)/1.35 system-ui, sans-serif;
color: var(--d3-04-mut);
}
.d3-04__cap code {
font: 600 0.95em/1 ui-monospace, Menlo, monospace;
color: var(--d3-04-ink);
}
.d3-04__cap--a code {
color: var(--d3-04-a);
}
.d3-04__cap--b code {
color: var(--d3-04-b);
}
.d3-04__tag {
padding: 0.2rem 0.5rem;
border-radius: 3px;
font: 800 0.62rem/1.5 ui-monospace, Menlo, monospace;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--d3-04-surf);
}
.d3-04__tag--a {
background: var(--d3-04-a);
}
.d3-04__tag--b {
background: var(--d3-04-b);
}
.d3-04__field {
position: relative;
block-size: clamp(11rem, 32vw, 14rem);
border: 1px solid var(--d3-04-line);
border-radius: 4px;
overflow: hidden;
background-color: color-mix(in oklab, var(--d3-04-bg) 60%, var(--d3-04-surf));
background-image: linear-gradient(color-mix(in oklab, var(--d3-04-line) 55%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in oklab, var(--d3-04-line) 55%, transparent) 1px, transparent 1px);
background-size: 18px 18px;
}
.d3-04__origin {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 20%;
inline-size: 9px;
block-size: 9px;
margin: -4.5px 0 0 -4.5px;
border-radius: 999px;
background: var(--d3-04-ink);
box-shadow: 0 0 0 4px color-mix(in oklab, var(--d3-04-ink) 15%, transparent);
}
.d3-04__trace {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 20%;
border: 1px dashed var(--d3-04-mut);
opacity: 0.75;
}
.d3-04__trace--line {
inline-size: var(--d3-04-dist);
block-size: 0;
border-width: 1px 0 0 0;
}
.d3-04__trace--arc {
inline-size: calc(var(--d3-04-dist) * 2);
block-size: calc(var(--d3-04-dist) * 2);
margin: calc(var(--d3-04-dist) * -1) 0 0 calc(var(--d3-04-dist) * -1);
border-radius: 999px;
}
.d3-04__tile {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 20%;
inline-size: clamp(5.5rem, 17vw, 7.5rem);
margin: -2.1rem 0 0 -0.9rem;
padding: 0.5rem 0.6rem 0.55rem;
display: grid;
gap: 0.15rem;
border-radius: 5px;
color: #fff;
box-shadow: 0 14px 26px -12px rgba(0, 0, 0, 0.5);
}
.d3-04__no {
font: 800 0.6rem/1.4 ui-monospace, Menlo, monospace;
letter-spacing: 0.12em;
opacity: 0.85;
}
.d3-04__tile b {
font: 700 clamp(0.72rem, 2vw, 0.82rem)/1.15 var(--d3-04-font-display);
}
.d3-04__bars {
display: block;
block-size: 0.85rem;
margin-block-start: 0.15rem;
border-radius: 2px;
background-image: repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.9) 0 2px, transparent 2px 4px, rgba(255, 255, 255, 0.9) 4px 5px, transparent 5px 7px);
}
.d3-04__tile--a {
background: linear-gradient(150deg, var(--d3-04-a), color-mix(in oklab, var(--d3-04-a) 60%, #05231a));
animation: d3-04-order-a var(--d3-04-beat) ease-in-out infinite;
}
.d3-04__tile--b {
background: linear-gradient(150deg, var(--d3-04-b), color-mix(in oklab, var(--d3-04-b) 60%, #2c0606));
animation: d3-04-order-b var(--d3-04-beat) ease-in-out infinite;
}
@keyframes d3-04-order-a {
0%,
12% {
transform: translateX(0) rotate(0deg);
}
50%,
62% {
transform: translateX(var(--d3-04-dist)) rotate(var(--d3-04-angle));
}
100% {
transform: translateX(0) rotate(0deg);
}
}
@keyframes d3-04-order-b {
0%,
12% {
transform: rotate(0deg) translateX(0);
}
50%,
62% {
transform: rotate(var(--d3-04-angle)) translateX(var(--d3-04-dist));
}
100% {
transform: rotate(0deg) translateX(0);
}
}
.d3-04__steps {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
.d3-04__steps li {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.3rem 0.55rem;
border: 1px solid var(--d3-04-line);
border-radius: 999px;
font: 500 0.72rem/1.2 ui-monospace, Menlo, monospace;
color: var(--d3-04-mut);
}
.d3-04__steps b {
display: grid;
place-items: center;
inline-size: 1.1rem;
block-size: 1.1rem;
border-radius: 999px;
background: var(--d3-04-ink);
color: var(--d3-04-surf);
font-size: 0.62rem;
}
.d3-04__note {
margin: 0;
max-inline-size: 46ch;
font: 500 clamp(0.74rem, 2vw, 0.84rem)/1.5 system-ui, sans-serif;
color: var(--d3-04-mut);
text-wrap: pretty;
}
@media (prefers-reduced-motion: reduce) {
.d3-04__tile--a {
animation: none;
transform: translateX(var(--d3-04-dist)) rotate(var(--d3-04-angle));
}
.d3-04__tile--b {
animation: none;
transform: rotate(var(--d3-04-angle)) translateX(var(--d3-04-dist));
}
}.d3-04 {
--d3-04-bg: #f6f0e4;
--d3-04-surf: #fffdf8;
--d3-04-line: #ddd2ba;
--d3-04-ink: #221c12;
--d3-04-mut: #6c6250;
--d3-04-a: #1f6b4f;
--d3-04-a: oklch(45% 0.11 158);
--d3-04-b: #b02c2c;
--d3-04-b: oklch(50% 0.17 25);
--d3-04-dist: clamp(3.5rem, 14vw, 7rem);
--d3-04-angle: 45deg;
--d3-04-beat: 6s;
--d3-04-font-display: "Bookman Old Style", "Rockwell", "Zilla Slab", Georgia, serif;
width: 100%;
min-height: 100vh;
display: block;
position: relative;
box-sizing: border-box;
background-color: var(--d3-04-bg);
background-image: radial-gradient(52rem 26rem at 90% -8%, color-mix(in oklab, var(--d3-04-b) 14%, transparent), transparent 60%), radial-gradient(48rem 24rem at -6% 104%, color-mix(in oklab, var(--d3-04-a) 16%, transparent), transparent 62%);
color: var(--d3-04-ink);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
line-height: 1.5;
}
.d3-04 *,
.d3-04 *::before,
.d3-04 *::after {
box-sizing: border-box;
}
.d3-04__sr {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.d3-04:has(.d3-04__theme-input:checked) {
--d3-04-bg: #17140f;
--d3-04-surf: #201c15;
--d3-04-line: #3b342a;
--d3-04-ink: #f4eee2;
--d3-04-mut: #a99d88;
--d3-04-a: #6fd8a8;
--d3-04-a: oklch(82% 0.13 160);
--d3-04-b: #f4907f;
--d3-04-b: oklch(78% 0.13 28);
}
@media (prefers-color-scheme: dark) {
.d3-04 {
--d3-04-bg: #17140f;
--d3-04-surf: #201c15;
--d3-04-line: #3b342a;
--d3-04-ink: #f4eee2;
--d3-04-mut: #a99d88;
--d3-04-a: #6fd8a8;
--d3-04-a: oklch(82% 0.13 160);
--d3-04-b: #f4907f;
--d3-04-b: oklch(78% 0.13 28);
}
.d3-04:has(.d3-04__theme-input:checked) {
--d3-04-bg: #f6f0e4;
--d3-04-surf: #fffdf8;
--d3-04-line: #ddd2ba;
--d3-04-ink: #221c12;
--d3-04-mut: #6c6250;
--d3-04-a: #1f6b4f;
--d3-04-a: oklch(45% 0.11 158);
--d3-04-b: #b02c2c;
--d3-04-b: oklch(50% 0.17 25);
}
}
@media (prefers-color-scheme: light) {
.d3-04:has(.d3-04__theme-input:checked) {
--d3-04-bg: #17140f;
--d3-04-surf: #201c15;
--d3-04-line: #3b342a;
--d3-04-ink: #f4eee2;
--d3-04-mut: #a99d88;
--d3-04-a: #6fd8a8;
--d3-04-a: oklch(82% 0.13 160);
--d3-04-b: #f4907f;
--d3-04-b: oklch(78% 0.13 28);
}
}
.d3-04__theme {
position: absolute;
inset-block-start: clamp(0.75rem, 3vw, 1.5rem);
inset-inline-end: clamp(0.75rem, 3vw, 1.5rem);
z-index: 20;
}
.d3-04__theme-input {
position: absolute;
opacity: 0;
inline-size: 100%;
block-size: 100%;
margin: 0;
cursor: pointer;
}
.d3-04__theme-label {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border: 1px solid var(--d3-04-line);
border-radius: 4px;
background: var(--d3-04-surf);
color: var(--d3-04-ink);
cursor: pointer;
box-shadow: 3px 3px 0 var(--d3-04-line);
}
.d3-04__theme-label svg {
inline-size: 20px;
block-size: 20px;
}
.d3-04__theme-input:focus-visible + .d3-04__theme-label {
outline: 2px solid var(--d3-04-b);
outline-offset: 3px;
}
.d3-04__stage {
display: grid;
grid-template-columns: minmax(0, 1fr);
place-items: center;
min-block-size: 100vh;
padding: clamp(3.5rem, 9vw, 5rem) clamp(0.75rem, 4vw, 2.5rem);
}
.d3-04__viewport {
inline-size: 100%;
max-inline-size: 64rem;
overflow: hidden;
display: grid;
gap: clamp(1rem, 3vw, 1.6rem);
padding: clamp(1rem, 3.5vw, 2rem);
border: 1px solid var(--d3-04-line);
border-radius: 6px;
background: var(--d3-04-surf);
box-shadow: 10px 10px 0 color-mix(in oklab, var(--d3-04-ink) 10%, transparent);
}
.d3-04__head {
display: grid;
gap: 0.15rem;
padding-block-end: 0.9rem;
border-block-end: 2px solid var(--d3-04-ink);
}
.d3-04__kicker {
font: 700 0.66rem/1.4 ui-monospace, Menlo, monospace;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--d3-04-mut);
}
.d3-04__head strong {
font: 700 clamp(1.3rem, 4.4vw, 2.1rem)/1.05 var(--d3-04-font-display);
letter-spacing: -0.01em;
}
.d3-04__lanes {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
gap: clamp(0.9rem, 3vw, 1.5rem);
}
.d3-04__lane {
display: grid;
gap: 0.7rem;
}
.d3-04__cap {
margin: 0;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.4rem;
font: 500 clamp(0.74rem, 2vw, 0.84rem)/1.35 system-ui, sans-serif;
color: var(--d3-04-mut);
}
.d3-04__cap code {
font: 600 0.95em/1 ui-monospace, Menlo, monospace;
color: var(--d3-04-ink);
}
.d3-04__cap--a code {
color: var(--d3-04-a);
}
.d3-04__cap--b code {
color: var(--d3-04-b);
}
.d3-04__tag {
padding: 0.2rem 0.5rem;
border-radius: 3px;
font: 800 0.62rem/1.5 ui-monospace, Menlo, monospace;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--d3-04-surf);
}
.d3-04__tag--a {
background: var(--d3-04-a);
}
.d3-04__tag--b {
background: var(--d3-04-b);
}
.d3-04__field {
position: relative;
block-size: clamp(11rem, 32vw, 14rem);
border: 1px solid var(--d3-04-line);
border-radius: 4px;
overflow: hidden;
background-color: color-mix(in oklab, var(--d3-04-bg) 60%, var(--d3-04-surf));
background-image: linear-gradient(color-mix(in oklab, var(--d3-04-line) 55%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in oklab, var(--d3-04-line) 55%, transparent) 1px, transparent 1px);
background-size: 18px 18px;
}
.d3-04__origin {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 20%;
inline-size: 9px;
block-size: 9px;
margin: -4.5px 0 0 -4.5px;
border-radius: 999px;
background: var(--d3-04-ink);
box-shadow: 0 0 0 4px color-mix(in oklab, var(--d3-04-ink) 15%, transparent);
}
.d3-04__trace {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 20%;
border: 1px dashed var(--d3-04-mut);
opacity: 0.75;
}
.d3-04__trace--line {
inline-size: var(--d3-04-dist);
block-size: 0;
border-width: 1px 0 0 0;
}
.d3-04__trace--arc {
inline-size: calc(var(--d3-04-dist) * 2);
block-size: calc(var(--d3-04-dist) * 2);
margin: calc(var(--d3-04-dist) * -1) 0 0 calc(var(--d3-04-dist) * -1);
border-radius: 999px;
}
.d3-04__tile {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 20%;
inline-size: clamp(5.5rem, 17vw, 7.5rem);
margin: -2.1rem 0 0 -0.9rem;
padding: 0.5rem 0.6rem 0.55rem;
display: grid;
gap: 0.15rem;
border-radius: 5px;
color: #fff;
box-shadow: 0 14px 26px -12px rgba(0, 0, 0, 0.5);
}
.d3-04__no {
font: 800 0.6rem/1.4 ui-monospace, Menlo, monospace;
letter-spacing: 0.12em;
opacity: 0.85;
}
.d3-04__tile b {
font: 700 clamp(0.72rem, 2vw, 0.82rem)/1.15 var(--d3-04-font-display);
}
.d3-04__bars {
display: block;
block-size: 0.85rem;
margin-block-start: 0.15rem;
border-radius: 2px;
background-image: repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.9) 0 2px, transparent 2px 4px, rgba(255, 255, 255, 0.9) 4px 5px, transparent 5px 7px);
}
.d3-04__tile--a {
background: linear-gradient(150deg, var(--d3-04-a), color-mix(in oklab, var(--d3-04-a) 60%, #05231a));
animation: d3-04-order-a var(--d3-04-beat) ease-in-out infinite;
}
.d3-04__tile--b {
background: linear-gradient(150deg, var(--d3-04-b), color-mix(in oklab, var(--d3-04-b) 60%, #2c0606));
animation: d3-04-order-b var(--d3-04-beat) ease-in-out infinite;
}
@keyframes d3-04-order-a {
0%,
12% {
transform: translateX(0) rotate(0deg);
}
50%,
62% {
transform: translateX(var(--d3-04-dist)) rotate(var(--d3-04-angle));
}
100% {
transform: translateX(0) rotate(0deg);
}
}
@keyframes d3-04-order-b {
0%,
12% {
transform: rotate(0deg) translateX(0);
}
50%,
62% {
transform: rotate(var(--d3-04-angle)) translateX(var(--d3-04-dist));
}
100% {
transform: rotate(0deg) translateX(0);
}
}
.d3-04__steps {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
.d3-04__steps li {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.3rem 0.55rem;
border: 1px solid var(--d3-04-line);
border-radius: 999px;
font: 500 0.72rem/1.2 ui-monospace, Menlo, monospace;
color: var(--d3-04-mut);
}
.d3-04__steps b {
display: grid;
place-items: center;
inline-size: 1.1rem;
block-size: 1.1rem;
border-radius: 999px;
background: var(--d3-04-ink);
color: var(--d3-04-surf);
font-size: 0.62rem;
}
.d3-04__note {
margin: 0;
max-inline-size: 46ch;
font: 500 clamp(0.74rem, 2vw, 0.84rem)/1.5 system-ui, sans-serif;
color: var(--d3-04-mut);
text-wrap: pretty;
}
@media (prefers-reduced-motion: reduce) {
.d3-04__tile--a {
animation: none;
transform: translateX(var(--d3-04-dist)) rotate(var(--d3-04-angle));
}
.d3-04__tile--b {
animation: none;
transform: rotate(var(--d3-04-angle)) translateX(var(--d3-04-dist));
}
}Here's a working CSS 3D Effect from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Transform Function Order Changes the Result
Source: https://codefronts.com/design-styles/css-3d/transform-function-order-changes-the-result/
translateX(120px) rotate(45deg) and rotate(45deg) translateX(120px) land in different places, because each function operates in the coordinate space the previous one left behind. Both orders run on identical elements with their travel traced, and the same lesson is then repeated in 3D where it bites harder.
## HTML
```html
<section class="d3-04" aria-labelledby="d3-04-title">
<h2 class="d3-04__sr" id="d3-04-title">Transform order compared</h2>
<div class="d3-04__theme">
<input class="d3-04__theme-input" type="checkbox" id="d3-04-theme">
<label class="d3-04__theme-label" for="d3-04-theme">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 3.5a8.5 8.5 0 0 0 0 17Z" fill="currentColor"/></svg>
<span class="d3-04__sr">Toggle dark theme</span>
</label>
</div>
<div class="d3-04__stage">
<div class="d3-04__viewport">
<header class="d3-04__head">
<span class="d3-04__kicker">Northbound Freight — dispatch board</span>
<strong>Two routes, same two moves</strong>
</header>
<div class="d3-04__lanes">
<section class="d3-04__lane">
<p class="d3-04__cap d3-04__cap--a"><span class="d3-04__tag d3-04__tag--a">Route A</span> <code>translateX() rotate()</code> — move first, then turn on the spot</p>
<div class="d3-04__field">
<span class="d3-04__origin" aria-hidden="true"></span>
<span class="d3-04__trace d3-04__trace--line" aria-hidden="true"></span>
<div class="d3-04__tile d3-04__tile--a">
<span class="d3-04__no">A-118</span>
<b>Depot → Bay 4</b>
<i class="d3-04__bars" aria-hidden="true"></i>
</div>
</div>
<ul class="d3-04__steps"><li><b>1</b> translateX(7rem)</li><li><b>2</b> rotate(45deg)</li></ul>
</section>
<section class="d3-04__lane">
<p class="d3-04__cap d3-04__cap--b"><span class="d3-04__tag d3-04__tag--b">Route B</span> <code>rotate() translateX()</code> — turn first, then move along the new axis</p>
<div class="d3-04__field">
<span class="d3-04__origin" aria-hidden="true"></span>
<span class="d3-04__trace d3-04__trace--arc" aria-hidden="true"></span>
<div class="d3-04__tile d3-04__tile--b">
<span class="d3-04__no">B-902</span>
<b>Depot → Bay 9</b>
<i class="d3-04__bars" aria-hidden="true"></i>
</div>
</div>
<ul class="d3-04__steps"><li><b>1</b> rotate(45deg)</li><li><b>2</b> translateX(7rem)</li></ul>
</section>
</div>
<p class="d3-04__note">Both parcels leave the same dock with the same two instructions. Route B travels down a rotated axis, so it lands somewhere else entirely.</p>
</div>
</div>
</section>
```
## CSS
```css
.d3-04 {
--d3-04-bg: #f6f0e4;
--d3-04-surf: #fffdf8;
--d3-04-line: #ddd2ba;
--d3-04-ink: #221c12;
--d3-04-mut: #6c6250;
--d3-04-a: #1f6b4f;
--d3-04-a: oklch(45% 0.11 158);
--d3-04-b: #b02c2c;
--d3-04-b: oklch(50% 0.17 25);
--d3-04-dist: clamp(3.5rem, 14vw, 7rem);
--d3-04-angle: 45deg;
--d3-04-beat: 6s;
--d3-04-font-display: "Bookman Old Style", "Rockwell", "Zilla Slab", Georgia, serif;
width: 100%;
min-height: 100vh;
display: block;
position: relative;
box-sizing: border-box;
background-color: var(--d3-04-bg);
background-image: radial-gradient(52rem 26rem at 90% -8%, color-mix(in oklab, var(--d3-04-b) 14%, transparent), transparent 60%), radial-gradient(48rem 24rem at -6% 104%, color-mix(in oklab, var(--d3-04-a) 16%, transparent), transparent 62%);
color: var(--d3-04-ink);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
line-height: 1.5;
}
.d3-04 *,
.d3-04 *::before,
.d3-04 *::after {
box-sizing: border-box;
}
.d3-04__sr {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.d3-04:has(.d3-04__theme-input:checked) {
--d3-04-bg: #17140f;
--d3-04-surf: #201c15;
--d3-04-line: #3b342a;
--d3-04-ink: #f4eee2;
--d3-04-mut: #a99d88;
--d3-04-a: #6fd8a8;
--d3-04-a: oklch(82% 0.13 160);
--d3-04-b: #f4907f;
--d3-04-b: oklch(78% 0.13 28);
}
@media (prefers-color-scheme: dark) {
.d3-04 {
--d3-04-bg: #17140f;
--d3-04-surf: #201c15;
--d3-04-line: #3b342a;
--d3-04-ink: #f4eee2;
--d3-04-mut: #a99d88;
--d3-04-a: #6fd8a8;
--d3-04-a: oklch(82% 0.13 160);
--d3-04-b: #f4907f;
--d3-04-b: oklch(78% 0.13 28);
}
.d3-04:has(.d3-04__theme-input:checked) {
--d3-04-bg: #f6f0e4;
--d3-04-surf: #fffdf8;
--d3-04-line: #ddd2ba;
--d3-04-ink: #221c12;
--d3-04-mut: #6c6250;
--d3-04-a: #1f6b4f;
--d3-04-a: oklch(45% 0.11 158);
--d3-04-b: #b02c2c;
--d3-04-b: oklch(50% 0.17 25);
}
}
@media (prefers-color-scheme: light) {
.d3-04:has(.d3-04__theme-input:checked) {
--d3-04-bg: #17140f;
--d3-04-surf: #201c15;
--d3-04-line: #3b342a;
--d3-04-ink: #f4eee2;
--d3-04-mut: #a99d88;
--d3-04-a: #6fd8a8;
--d3-04-a: oklch(82% 0.13 160);
--d3-04-b: #f4907f;
--d3-04-b: oklch(78% 0.13 28);
}
}
.d3-04__theme {
position: absolute;
inset-block-start: clamp(0.75rem, 3vw, 1.5rem);
inset-inline-end: clamp(0.75rem, 3vw, 1.5rem);
z-index: 20;
}
.d3-04__theme-input {
position: absolute;
opacity: 0;
inline-size: 100%;
block-size: 100%;
margin: 0;
cursor: pointer;
}
.d3-04__theme-label {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border: 1px solid var(--d3-04-line);
border-radius: 4px;
background: var(--d3-04-surf);
color: var(--d3-04-ink);
cursor: pointer;
box-shadow: 3px 3px 0 var(--d3-04-line);
}
.d3-04__theme-label svg {
inline-size: 20px;
block-size: 20px;
}
.d3-04__theme-input:focus-visible + .d3-04__theme-label {
outline: 2px solid var(--d3-04-b);
outline-offset: 3px;
}
.d3-04__stage {
display: grid;
grid-template-columns: minmax(0, 1fr);
place-items: center;
min-block-size: 100vh;
padding: clamp(3.5rem, 9vw, 5rem) clamp(0.75rem, 4vw, 2.5rem);
}
.d3-04__viewport {
inline-size: 100%;
max-inline-size: 64rem;
overflow: hidden;
display: grid;
gap: clamp(1rem, 3vw, 1.6rem);
padding: clamp(1rem, 3.5vw, 2rem);
border: 1px solid var(--d3-04-line);
border-radius: 6px;
background: var(--d3-04-surf);
box-shadow: 10px 10px 0 color-mix(in oklab, var(--d3-04-ink) 10%, transparent);
}
.d3-04__head {
display: grid;
gap: 0.15rem;
padding-block-end: 0.9rem;
border-block-end: 2px solid var(--d3-04-ink);
}
.d3-04__kicker {
font: 700 0.66rem/1.4 ui-monospace, Menlo, monospace;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--d3-04-mut);
}
.d3-04__head strong {
font: 700 clamp(1.3rem, 4.4vw, 2.1rem)/1.05 var(--d3-04-font-display);
letter-spacing: -0.01em;
}
.d3-04__lanes {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
gap: clamp(0.9rem, 3vw, 1.5rem);
}
.d3-04__lane {
display: grid;
gap: 0.7rem;
}
.d3-04__cap {
margin: 0;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.4rem;
font: 500 clamp(0.74rem, 2vw, 0.84rem)/1.35 system-ui, sans-serif;
color: var(--d3-04-mut);
}
.d3-04__cap code {
font: 600 0.95em/1 ui-monospace, Menlo, monospace;
color: var(--d3-04-ink);
}
.d3-04__cap--a code {
color: var(--d3-04-a);
}
.d3-04__cap--b code {
color: var(--d3-04-b);
}
.d3-04__tag {
padding: 0.2rem 0.5rem;
border-radius: 3px;
font: 800 0.62rem/1.5 ui-monospace, Menlo, monospace;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--d3-04-surf);
}
.d3-04__tag--a {
background: var(--d3-04-a);
}
.d3-04__tag--b {
background: var(--d3-04-b);
}
.d3-04__field {
position: relative;
block-size: clamp(11rem, 32vw, 14rem);
border: 1px solid var(--d3-04-line);
border-radius: 4px;
overflow: hidden;
background-color: color-mix(in oklab, var(--d3-04-bg) 60%, var(--d3-04-surf));
background-image: linear-gradient(color-mix(in oklab, var(--d3-04-line) 55%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in oklab, var(--d3-04-line) 55%, transparent) 1px, transparent 1px);
background-size: 18px 18px;
}
.d3-04__origin {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 20%;
inline-size: 9px;
block-size: 9px;
margin: -4.5px 0 0 -4.5px;
border-radius: 999px;
background: var(--d3-04-ink);
box-shadow: 0 0 0 4px color-mix(in oklab, var(--d3-04-ink) 15%, transparent);
}
.d3-04__trace {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 20%;
border: 1px dashed var(--d3-04-mut);
opacity: 0.75;
}
.d3-04__trace--line {
inline-size: var(--d3-04-dist);
block-size: 0;
border-width: 1px 0 0 0;
}
.d3-04__trace--arc {
inline-size: calc(var(--d3-04-dist) * 2);
block-size: calc(var(--d3-04-dist) * 2);
margin: calc(var(--d3-04-dist) * -1) 0 0 calc(var(--d3-04-dist) * -1);
border-radius: 999px;
}
.d3-04__tile {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 20%;
inline-size: clamp(5.5rem, 17vw, 7.5rem);
margin: -2.1rem 0 0 -0.9rem;
padding: 0.5rem 0.6rem 0.55rem;
display: grid;
gap: 0.15rem;
border-radius: 5px;
color: #fff;
box-shadow: 0 14px 26px -12px rgba(0, 0, 0, 0.5);
}
.d3-04__no {
font: 800 0.6rem/1.4 ui-monospace, Menlo, monospace;
letter-spacing: 0.12em;
opacity: 0.85;
}
.d3-04__tile b {
font: 700 clamp(0.72rem, 2vw, 0.82rem)/1.15 var(--d3-04-font-display);
}
.d3-04__bars {
display: block;
block-size: 0.85rem;
margin-block-start: 0.15rem;
border-radius: 2px;
background-image: repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.9) 0 2px, transparent 2px 4px, rgba(255, 255, 255, 0.9) 4px 5px, transparent 5px 7px);
}
.d3-04__tile--a {
background: linear-gradient(150deg, var(--d3-04-a), color-mix(in oklab, var(--d3-04-a) 60%, #05231a));
animation: d3-04-order-a var(--d3-04-beat) ease-in-out infinite;
}
.d3-04__tile--b {
background: linear-gradient(150deg, var(--d3-04-b), color-mix(in oklab, var(--d3-04-b) 60%, #2c0606));
animation: d3-04-order-b var(--d3-04-beat) ease-in-out infinite;
}
@keyframes d3-04-order-a {
0%,
12% {
transform: translateX(0) rotate(0deg);
}
50%,
62% {
transform: translateX(var(--d3-04-dist)) rotate(var(--d3-04-angle));
}
100% {
transform: translateX(0) rotate(0deg);
}
}
@keyframes d3-04-order-b {
0%,
12% {
transform: rotate(0deg) translateX(0);
}
50%,
62% {
transform: rotate(var(--d3-04-angle)) translateX(var(--d3-04-dist));
}
100% {
transform: rotate(0deg) translateX(0);
}
}
.d3-04__steps {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
.d3-04__steps li {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.3rem 0.55rem;
border: 1px solid var(--d3-04-line);
border-radius: 999px;
font: 500 0.72rem/1.2 ui-monospace, Menlo, monospace;
color: var(--d3-04-mut);
}
.d3-04__steps b {
display: grid;
place-items: center;
inline-size: 1.1rem;
block-size: 1.1rem;
border-radius: 999px;
background: var(--d3-04-ink);
color: var(--d3-04-surf);
font-size: 0.62rem;
}
.d3-04__note {
margin: 0;
max-inline-size: 46ch;
font: 500 clamp(0.74rem, 2vw, 0.84rem)/1.5 system-ui, sans-serif;
color: var(--d3-04-mut);
text-wrap: pretty;
}
@media (prefers-reduced-motion: reduce) {
.d3-04__tile--a {
animation: none;
transform: translateX(var(--d3-04-dist)) rotate(var(--d3-04-angle));
}
.d3-04__tile--b {
animation: none;
transform: rotate(var(--d3-04-angle)) translateX(var(--d3-04-dist));
}
}
```Here's a working CSS 3D Effect from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Transform Function Order Changes the Result
Source: https://codefronts.com/design-styles/css-3d/transform-function-order-changes-the-result/
translateX(120px) rotate(45deg) and rotate(45deg) translateX(120px) land in different places, because each function operates in the coordinate space the previous one left behind. Both orders run on identical elements with their travel traced, and the same lesson is then repeated in 3D where it bites harder.
## HTML
```html
<section class="d3-04" aria-labelledby="d3-04-title">
<h2 class="d3-04__sr" id="d3-04-title">Transform order compared</h2>
<div class="d3-04__theme">
<input class="d3-04__theme-input" type="checkbox" id="d3-04-theme">
<label class="d3-04__theme-label" for="d3-04-theme">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 3.5a8.5 8.5 0 0 0 0 17Z" fill="currentColor"/></svg>
<span class="d3-04__sr">Toggle dark theme</span>
</label>
</div>
<div class="d3-04__stage">
<div class="d3-04__viewport">
<header class="d3-04__head">
<span class="d3-04__kicker">Northbound Freight — dispatch board</span>
<strong>Two routes, same two moves</strong>
</header>
<div class="d3-04__lanes">
<section class="d3-04__lane">
<p class="d3-04__cap d3-04__cap--a"><span class="d3-04__tag d3-04__tag--a">Route A</span> <code>translateX() rotate()</code> — move first, then turn on the spot</p>
<div class="d3-04__field">
<span class="d3-04__origin" aria-hidden="true"></span>
<span class="d3-04__trace d3-04__trace--line" aria-hidden="true"></span>
<div class="d3-04__tile d3-04__tile--a">
<span class="d3-04__no">A-118</span>
<b>Depot → Bay 4</b>
<i class="d3-04__bars" aria-hidden="true"></i>
</div>
</div>
<ul class="d3-04__steps"><li><b>1</b> translateX(7rem)</li><li><b>2</b> rotate(45deg)</li></ul>
</section>
<section class="d3-04__lane">
<p class="d3-04__cap d3-04__cap--b"><span class="d3-04__tag d3-04__tag--b">Route B</span> <code>rotate() translateX()</code> — turn first, then move along the new axis</p>
<div class="d3-04__field">
<span class="d3-04__origin" aria-hidden="true"></span>
<span class="d3-04__trace d3-04__trace--arc" aria-hidden="true"></span>
<div class="d3-04__tile d3-04__tile--b">
<span class="d3-04__no">B-902</span>
<b>Depot → Bay 9</b>
<i class="d3-04__bars" aria-hidden="true"></i>
</div>
</div>
<ul class="d3-04__steps"><li><b>1</b> rotate(45deg)</li><li><b>2</b> translateX(7rem)</li></ul>
</section>
</div>
<p class="d3-04__note">Both parcels leave the same dock with the same two instructions. Route B travels down a rotated axis, so it lands somewhere else entirely.</p>
</div>
</div>
</section>
```
## CSS
```css
.d3-04 {
--d3-04-bg: #f6f0e4;
--d3-04-surf: #fffdf8;
--d3-04-line: #ddd2ba;
--d3-04-ink: #221c12;
--d3-04-mut: #6c6250;
--d3-04-a: #1f6b4f;
--d3-04-a: oklch(45% 0.11 158);
--d3-04-b: #b02c2c;
--d3-04-b: oklch(50% 0.17 25);
--d3-04-dist: clamp(3.5rem, 14vw, 7rem);
--d3-04-angle: 45deg;
--d3-04-beat: 6s;
--d3-04-font-display: "Bookman Old Style", "Rockwell", "Zilla Slab", Georgia, serif;
width: 100%;
min-height: 100vh;
display: block;
position: relative;
box-sizing: border-box;
background-color: var(--d3-04-bg);
background-image: radial-gradient(52rem 26rem at 90% -8%, color-mix(in oklab, var(--d3-04-b) 14%, transparent), transparent 60%), radial-gradient(48rem 24rem at -6% 104%, color-mix(in oklab, var(--d3-04-a) 16%, transparent), transparent 62%);
color: var(--d3-04-ink);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
line-height: 1.5;
}
.d3-04 *,
.d3-04 *::before,
.d3-04 *::after {
box-sizing: border-box;
}
.d3-04__sr {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.d3-04:has(.d3-04__theme-input:checked) {
--d3-04-bg: #17140f;
--d3-04-surf: #201c15;
--d3-04-line: #3b342a;
--d3-04-ink: #f4eee2;
--d3-04-mut: #a99d88;
--d3-04-a: #6fd8a8;
--d3-04-a: oklch(82% 0.13 160);
--d3-04-b: #f4907f;
--d3-04-b: oklch(78% 0.13 28);
}
@media (prefers-color-scheme: dark) {
.d3-04 {
--d3-04-bg: #17140f;
--d3-04-surf: #201c15;
--d3-04-line: #3b342a;
--d3-04-ink: #f4eee2;
--d3-04-mut: #a99d88;
--d3-04-a: #6fd8a8;
--d3-04-a: oklch(82% 0.13 160);
--d3-04-b: #f4907f;
--d3-04-b: oklch(78% 0.13 28);
}
.d3-04:has(.d3-04__theme-input:checked) {
--d3-04-bg: #f6f0e4;
--d3-04-surf: #fffdf8;
--d3-04-line: #ddd2ba;
--d3-04-ink: #221c12;
--d3-04-mut: #6c6250;
--d3-04-a: #1f6b4f;
--d3-04-a: oklch(45% 0.11 158);
--d3-04-b: #b02c2c;
--d3-04-b: oklch(50% 0.17 25);
}
}
@media (prefers-color-scheme: light) {
.d3-04:has(.d3-04__theme-input:checked) {
--d3-04-bg: #17140f;
--d3-04-surf: #201c15;
--d3-04-line: #3b342a;
--d3-04-ink: #f4eee2;
--d3-04-mut: #a99d88;
--d3-04-a: #6fd8a8;
--d3-04-a: oklch(82% 0.13 160);
--d3-04-b: #f4907f;
--d3-04-b: oklch(78% 0.13 28);
}
}
.d3-04__theme {
position: absolute;
inset-block-start: clamp(0.75rem, 3vw, 1.5rem);
inset-inline-end: clamp(0.75rem, 3vw, 1.5rem);
z-index: 20;
}
.d3-04__theme-input {
position: absolute;
opacity: 0;
inline-size: 100%;
block-size: 100%;
margin: 0;
cursor: pointer;
}
.d3-04__theme-label {
display: grid;
place-items: center;
inline-size: 44px;
block-size: 44px;
border: 1px solid var(--d3-04-line);
border-radius: 4px;
background: var(--d3-04-surf);
color: var(--d3-04-ink);
cursor: pointer;
box-shadow: 3px 3px 0 var(--d3-04-line);
}
.d3-04__theme-label svg {
inline-size: 20px;
block-size: 20px;
}
.d3-04__theme-input:focus-visible + .d3-04__theme-label {
outline: 2px solid var(--d3-04-b);
outline-offset: 3px;
}
.d3-04__stage {
display: grid;
grid-template-columns: minmax(0, 1fr);
place-items: center;
min-block-size: 100vh;
padding: clamp(3.5rem, 9vw, 5rem) clamp(0.75rem, 4vw, 2.5rem);
}
.d3-04__viewport {
inline-size: 100%;
max-inline-size: 64rem;
overflow: hidden;
display: grid;
gap: clamp(1rem, 3vw, 1.6rem);
padding: clamp(1rem, 3.5vw, 2rem);
border: 1px solid var(--d3-04-line);
border-radius: 6px;
background: var(--d3-04-surf);
box-shadow: 10px 10px 0 color-mix(in oklab, var(--d3-04-ink) 10%, transparent);
}
.d3-04__head {
display: grid;
gap: 0.15rem;
padding-block-end: 0.9rem;
border-block-end: 2px solid var(--d3-04-ink);
}
.d3-04__kicker {
font: 700 0.66rem/1.4 ui-monospace, Menlo, monospace;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--d3-04-mut);
}
.d3-04__head strong {
font: 700 clamp(1.3rem, 4.4vw, 2.1rem)/1.05 var(--d3-04-font-display);
letter-spacing: -0.01em;
}
.d3-04__lanes {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
gap: clamp(0.9rem, 3vw, 1.5rem);
}
.d3-04__lane {
display: grid;
gap: 0.7rem;
}
.d3-04__cap {
margin: 0;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.4rem;
font: 500 clamp(0.74rem, 2vw, 0.84rem)/1.35 system-ui, sans-serif;
color: var(--d3-04-mut);
}
.d3-04__cap code {
font: 600 0.95em/1 ui-monospace, Menlo, monospace;
color: var(--d3-04-ink);
}
.d3-04__cap--a code {
color: var(--d3-04-a);
}
.d3-04__cap--b code {
color: var(--d3-04-b);
}
.d3-04__tag {
padding: 0.2rem 0.5rem;
border-radius: 3px;
font: 800 0.62rem/1.5 ui-monospace, Menlo, monospace;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--d3-04-surf);
}
.d3-04__tag--a {
background: var(--d3-04-a);
}
.d3-04__tag--b {
background: var(--d3-04-b);
}
.d3-04__field {
position: relative;
block-size: clamp(11rem, 32vw, 14rem);
border: 1px solid var(--d3-04-line);
border-radius: 4px;
overflow: hidden;
background-color: color-mix(in oklab, var(--d3-04-bg) 60%, var(--d3-04-surf));
background-image: linear-gradient(color-mix(in oklab, var(--d3-04-line) 55%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in oklab, var(--d3-04-line) 55%, transparent) 1px, transparent 1px);
background-size: 18px 18px;
}
.d3-04__origin {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 20%;
inline-size: 9px;
block-size: 9px;
margin: -4.5px 0 0 -4.5px;
border-radius: 999px;
background: var(--d3-04-ink);
box-shadow: 0 0 0 4px color-mix(in oklab, var(--d3-04-ink) 15%, transparent);
}
.d3-04__trace {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 20%;
border: 1px dashed var(--d3-04-mut);
opacity: 0.75;
}
.d3-04__trace--line {
inline-size: var(--d3-04-dist);
block-size: 0;
border-width: 1px 0 0 0;
}
.d3-04__trace--arc {
inline-size: calc(var(--d3-04-dist) * 2);
block-size: calc(var(--d3-04-dist) * 2);
margin: calc(var(--d3-04-dist) * -1) 0 0 calc(var(--d3-04-dist) * -1);
border-radius: 999px;
}
.d3-04__tile {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 20%;
inline-size: clamp(5.5rem, 17vw, 7.5rem);
margin: -2.1rem 0 0 -0.9rem;
padding: 0.5rem 0.6rem 0.55rem;
display: grid;
gap: 0.15rem;
border-radius: 5px;
color: #fff;
box-shadow: 0 14px 26px -12px rgba(0, 0, 0, 0.5);
}
.d3-04__no {
font: 800 0.6rem/1.4 ui-monospace, Menlo, monospace;
letter-spacing: 0.12em;
opacity: 0.85;
}
.d3-04__tile b {
font: 700 clamp(0.72rem, 2vw, 0.82rem)/1.15 var(--d3-04-font-display);
}
.d3-04__bars {
display: block;
block-size: 0.85rem;
margin-block-start: 0.15rem;
border-radius: 2px;
background-image: repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.9) 0 2px, transparent 2px 4px, rgba(255, 255, 255, 0.9) 4px 5px, transparent 5px 7px);
}
.d3-04__tile--a {
background: linear-gradient(150deg, var(--d3-04-a), color-mix(in oklab, var(--d3-04-a) 60%, #05231a));
animation: d3-04-order-a var(--d3-04-beat) ease-in-out infinite;
}
.d3-04__tile--b {
background: linear-gradient(150deg, var(--d3-04-b), color-mix(in oklab, var(--d3-04-b) 60%, #2c0606));
animation: d3-04-order-b var(--d3-04-beat) ease-in-out infinite;
}
@keyframes d3-04-order-a {
0%,
12% {
transform: translateX(0) rotate(0deg);
}
50%,
62% {
transform: translateX(var(--d3-04-dist)) rotate(var(--d3-04-angle));
}
100% {
transform: translateX(0) rotate(0deg);
}
}
@keyframes d3-04-order-b {
0%,
12% {
transform: rotate(0deg) translateX(0);
}
50%,
62% {
transform: rotate(var(--d3-04-angle)) translateX(var(--d3-04-dist));
}
100% {
transform: rotate(0deg) translateX(0);
}
}
.d3-04__steps {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
.d3-04__steps li {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.3rem 0.55rem;
border: 1px solid var(--d3-04-line);
border-radius: 999px;
font: 500 0.72rem/1.2 ui-monospace, Menlo, monospace;
color: var(--d3-04-mut);
}
.d3-04__steps b {
display: grid;
place-items: center;
inline-size: 1.1rem;
block-size: 1.1rem;
border-radius: 999px;
background: var(--d3-04-ink);
color: var(--d3-04-surf);
font-size: 0.62rem;
}
.d3-04__note {
margin: 0;
max-inline-size: 46ch;
font: 500 clamp(0.74rem, 2vw, 0.84rem)/1.5 system-ui, sans-serif;
color: var(--d3-04-mut);
text-wrap: pretty;
}
@media (prefers-reduced-motion: reduce) {
.d3-04__tile--a {
animation: none;
transform: translateX(var(--d3-04-dist)) rotate(var(--d3-04-angle));
}
.d3-04__tile--b {
animation: none;
transform: rotate(var(--d3-04-angle)) translateX(var(--d3-04-dist));
}
}
```How this works
A transform list is a sequence of coordinate-space changes, not a set of independent adjustments. Each function runs in the space produced by everything to its left. translateX(120px) rotate(45deg) moves 120px along the original X axis and then spins in place. rotate(45deg) translateX(120px) spins the axes first, so the 120px is travelled along the new, rotated X — diagonally. Same numbers, different destination.
The mental model that makes this predictable: read the list right to left as operations on the element, or left to right as operations on the coordinate system. Both readings are correct and either one works, but mixing them is what produces the guesswork. This demo shows the left-to-right reading, because the traced arc makes the moving axes visible.
In 3D the same rule is stronger and much less intuitive. rotateY(40deg) translateZ(160px) pushes the element out along a Z axis that has already been swung 40 degrees, which is precisely how a ring is built — demo 12 uses exactly this pair. Swap them to translateZ(160px) rotateY(40deg) and the element comes straight towards the camera first and then spins in place. One is an orbit, the other is a pirouette.
If the ordering is a recurring source of bugs, the individual transform properties remove the question. translate, rotate and scale as separate properties always apply in a fixed order — translate, then rotate, then scale — regardless of the order you write them in the stylesheet. That is genuinely useful for independently animating each one, at the cost of a higher browser floor: Chrome 104, Safari 14.1, Firefox 72.
Make it yours
- Change
--d3-04-distto lengthen the travel; the gap between the two orders grows linearly with it. - Change
--d3-04-angleto see the special cases: at 0deg the two orders agree, at 90deg they diverge maximally. - Retune
--d3-04-beatto slow the loop for a walkthrough, or remove the animation and read the composed end frames. - Switch the 3D lane's pair to
translateZ()first to turn the orbit into a pirouette. - Rewrite one lane using the individual
rotateandtranslateproperties to demonstrate the fixed application order. - Recolour the two lanes independently with
--d3-04-aand--d3-04-bso the comparison stays readable in either theme.
Gotchas — read before shipping
- The two orders coincide at an angle of 0 degrees, so a demo built at a small angle can look like the order does not matter. Test at 45 or 90 degrees.
- Mixing the two readings — some functions imagined as element moves, others as axis moves — is what makes chained transforms feel random. Pick one reading per list.
- A traced path drawn as a child of the transformed element is itself transformed, so the trace lies. Both traces here are siblings, outside the transform.
- The individual
rotate/translate/scaleproperties ignore the order you write them in, which is a trap in the other direction: you cannot express rotate-then-translate with them at all.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 114+ | 17.5+ | 121+ | 114+ |
Floor set by :has(), oklch(), color-mix(), text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.
oklch() and the :has() theme toggle set the floor; chained transform functions and their ordering rules have been Baseline since 2015. The individual rotate / translate / scale properties are mentioned in the notes but not used for the comparison itself, precisely because they would raise the floor to Chrome 104 / Safari 14.1 / Firefox 72. Below the oklch() floor the sRGB hex declared first applies.