Browser support for 3D transforms is excellent today, with a few sharp edges. The main one is that certain properties, such as overflow hidden or some filters, quietly flatten a preserve-3d context. If a face disappears or a block turns flat, look for one of those on an ancestor before you start rewriting your transforms.
A code editor, a modern browser and about twenty minutes. No libraries or build step.
Keeping everything on the grid
Palettes for isometric work start from one hue. Pick it, then shift only the lightness for the three faces, and cool the shadow slightly towards blue. Adding a second hue for accents is fine, but keep it to small elements such as windows or buttons, or the scene starts to look like a patchwork.
Set the unit
Define
--unit: 2remon the scene so every size derives from one value.Build the columns
Read each value from a
data-valueattribute and set the block height withcalc().Label the bars
Keep labels flat and upright below the chart so they stay readable.
Describe the chart
Add a visually hidden table with the same numbers for screen readers.
The code
Typography on a tilted plane is readable only in small doses. A single word on the side of a block looks great; a paragraph set at an angle is exhausting. Keep running text flat and upright, and save the skewed type for labels, numbers and the occasional headline where the shape of the word matters more than speed.
Hover effects on isometric blocks work best when they follow the same rules as the static scene. Lift a block along its vertical axis, deepen its shadow on the floor, and leave the other blocks alone. Moving a block sideways, or changing its angle, breaks the projection and makes the scene feel unstable.
Game artists have a name for the pixel-art version of isometric: two-to-one. Lines climb one pixel for every two across, which gives clean, stair-stepped edges instead of jagged ones. The angle is about 26.6 degrees rather than 30, and almost nobody notices, which is a nice reminder that looking right beats being exact.
@media (prefers-reduced-motion: no-preference) { .cube:hover { transform: translateZ(calc(var(--unit) / 2)); transition: transform 180ms ease-out; }}
Colours used
Where type fits in
Custom properties make isometric CSS far easier to maintain. Instead of hard-coding sizes, we define a single unit on the scene wrapper and multiply it everywhere else. Changing the unit rescales the whole illustration, keeps the proportions intact and means a phone layout needs one media query instead of twenty.
Colour contrast still applies to illustrations that carry meaning. A bar chart drawn as blocks needs enough contrast between the bars and the floor, and labels on the faces need the same contrast as body text. Decorative scenes have more freedom, but we still avoid pale blocks on a pale background.
SVG is often the better choice for complex scenes. CSS cubes are fun to build and easy to animate, but a city block made of hundreds of divs is hard to change and heavy to render. We draw the static parts in SVG, keep the interactive blocks in CSS, and layer the two together.
What you built
- A cube with three shaded faces
- A floor grid from gradients
- A hover state that lifts the block
Mateo Rossi
Love the palette. Could you share the exact lightness values?
Gus Lindgren
The tip about naming faces instead of colours saved our design system a lot of pain.
Farah Aziz
Any plans for a video version of this one?
Jonah BergAuthor
Same here. Adding will-change on the wrapper fixed it for me.
Jonah BergAuthor
The templates are in the downloads section of the reference post.
Soren Dahl
The paper cube exercise sounds fun. Are the templates available somewhere?
Omar Haddad
The part about layer counts was eye-opening. Our landing page had over a hundred.