The CSS Diagram That Explodes on Hover
The hero section of our redesigned homepage needed a visual that communicated a specific idea: your data, your AI agents, and your finished application are three distinct layers, but they connect into one system. We did not want a PNG, an SVG export, or a third-party diagram tool. We wanted something that lives in the codebase, responds to interaction, and holds up at any pixel ratio.
We built SystemSculpture: a CSS-only isometric diagram using nothing but transform, perspective, and linear-gradient. On hover, the three layers physically separate so the viewer can see the space between them.
The card stack
Three absolute-positioned cards inside a perspective: 1000px container. Every card gets the same base transform:
transform: translate(-50%, -50%) rotateX(40deg) rotateZ(-28deg);
rotateX tilts the card toward the viewer. rotateZ angles it like an isometric face. The result looks like a physical card on a tilted plane.
The layers are not separated on the Z axis. They're staggered vertically with margin-top:
.layer-data { margin-top: 98px; }
.layer-agents { margin-top: 14px; }
.layer-system { margin-top: -69px; }
Because every card shares the same rotation, moving them along the Y axis in the document flow reads as depth in the isometric view. No Z translation needed.
The explode
Here is the hover rule for all three layers, reduced to its essentials:
.studio-sculpture:hover .layer-system {
transform: translate(-50%, -60%) rotateX(35deg) rotateZ(-23deg);
}
.studio-sculpture:hover .layer-data {
transform: translate(-50%, -40%) rotateX(40deg) rotateZ(-31deg);
}
Layer-system moves up five percent. Layer-data moves down ten percent. The middle card holds its position. The translate nudges in opposite directions create a physical separation: the cards appear to lift apart like a disassembled mechanical part.
The rotation values shift slightly on hover too. Layer-system flattens a few degrees (rotateX drops from 40 to 35) as it rises, which reads as the top face turning to face the viewer. Layer-data tilts a touch further as it drops. These are small numbers, but they make the difference between cards that "slide" and cards that "peel."
The transition
Every .sculpture-layer carries:
transition: transform .8s var(--studio-spring);
Where --studio-spring is:
--studio-spring: linear(
0, .104, .348, .62, .832, .958,
1.012, 1.024, 1.016, 1.006, 1
);
The linear() function in CSS lets you define a custom easing curve by listing output values at evenly-spaced input steps. This one overshoots past 1.0 (reaching 1.024) and settles back, approximating a spring. No JavaScript, no Web Animations API, no library.
The grid background
Behind the cards is a faint grid:
.sculpture-grid {
background-image:
linear-gradient(#0b5cab0c 1px, transparent 1px),
linear-gradient(90deg, #0b5cab0c 1px, transparent 1px);
background-size: 32px 32px;
mask-image: radial-gradient(ellipse, #000 28%, transparent 72%);
}
Two perpendicular one-pixel gradient lines at 32px intervals produce the grid. A mask-image with a radial gradient fades it out toward the edges so it reads as ambient depth, not a table. The grid uses #0b5cab0c, the brand blue at about 5% opacity, which tints it without overpowering the cards.
Mobile reset
Hover states on touch screens can get stuck. A media query at the bottom of the file resets the transform for mobile:
@media (pointer: coarse) {
.studio-sculpture:hover .layer-system,
.studio-sculpture:hover .layer-data {
transform: translate(-50%, -50%) rotateX(40deg) rotateZ(-28deg);
}
}
On a coarse pointer, the hover state fires on tap and the exploded layout would freeze until the user taps elsewhere. The reset brings both layers back to their resting position so the diagram stays readable.
The whole thing is about 90 lines of CSS and zero JavaScript.