Solar system
Pure CSSOrbits are rings on a tilted plane, each spinning at its own speed. Every planet undoes both rotations, so it always faces the camera.
A tapered tower of four leaning trapezoids with a pyramid cap, on a grass block. The sails turn with one rotateZ loop while the whole model turns slowly, so the depth reads.
Your edited version
rotateX(-20deg); a child spins the whole model with rotateY, so you see it from every side.clip-path), each standing on its bottom edge. rotateY(side) and a translateZ of 24 units put it on its side of the base; rotateX(5.59deg) then leans it in by atan((24 − 15) / 92), exactly enough to bring its top edge in to the narrower top.transform-origin = the hub) and turned by 90° steps. Their parent loops rotate(0 → -360deg). It needs preserve-3d too: a flat, zero-size group gets sorted as one plane and hides behind the tower.--d), so the model looks lit from the front-left as it turns. backface-visibility: hidden skips the sides facing away.--u, tied to the canvas: the grass block is 150 units square and the tower 92 high, so the windmill is the same share of a gallery card, the editor and a recording canvas.frustum: rotateY → translateZ → rotateX(lean)clip-path trapezoids and trianglesrotateZ sails on a hub in front of the capone model spun with rotateY<div class="scene">
<div class="windmill">
<div class="spin">
<div class="hill">
<i style="--n:0;--d:0"></i>
<i style="--n:1;--d:0.2"></i>
<i style="--n:2;--d:0.36"></i>
<i style="--n:3;--d:0.12"></i>
<i class="top"></i>
</div>
<div class="tree" style="--x:-50;--z:-44;--k:1"><i></i><i></i></div>
<div class="tree" style="--x:52;--z:34;--k:0.8"><i></i><i></i></div>
<div class="tower">
<i class="front" style="--n:0;--d:0"></i>
<i style="--n:1;--d:0.2"></i>
<i style="--n:2;--d:0.36"></i>
<i style="--n:3;--d:0.12"></i>
</div>
<div class="cap">
<i style="--n:0;--d:0"></i>
<i style="--n:1;--d:0.2"></i>
<i style="--n:2;--d:0.36"></i>
<i style="--n:3;--d:0.12"></i>
</div>
<div class="rotor">
<div class="sails"><b style="--n:0"></b><b style="--n:1"></b><b style="--n:2"></b><b style="--n:3"></b><u></u></div>
</div>
</div>
</div>
</div>
.scene {
/* one base unit: every length below is a multiple of it, so the windmill is the same share of
a gallery card, the editor, a full screen and a recording canvas */
--u: 0.25vmin;
display: grid;
place-items: center;
perspective: calc(800 * var(--u));
}
.windmill {
--grass: color-mix(in srgb, #2ee6d6 35%, #4f9a3a);
--earth: color-mix(in srgb, #ffb547 30%, #4a3226);
--plaster: color-mix(in srgb, #ffb547 8%, #f2ede5);
--roof: color-mix(in srgb, #ff4d9d 70%, #4a1024);
--wood: #5b3b25;
position: relative;
width: calc(210 * var(--u));
height: calc(200 * var(--u));
transform-style: preserve-3d;
transform: rotateX(-20deg); /* the camera looks down a little */
}
/* the model's origin: the middle of the grass top */
.spin {
position: absolute;
left: 50%;
top: calc(164 * var(--u));
transform-style: preserve-3d;
animation: turn 28s linear infinite;
}
.hill,
.tower,
.cap,
.rotor,
.tree {
position: absolute;
transform-style: preserve-3d;
}
/* grass block: four earth sides hanging from the top edge, and the top laid flat */
.hill i {
position: absolute;
left: calc(-75 * var(--u));
top: 0;
width: calc(150 * var(--u));
height: calc(24 * var(--u));
background:
linear-gradient(rgb(0 0 0 / var(--d)), rgb(0 0 0 / var(--d))), /* baked-in shade */
linear-gradient(var(--grass) 0 calc(5 * var(--u)), transparent calc(7 * var(--u))),
linear-gradient(var(--earth), color-mix(in srgb, var(--earth) 65%, #000));
backface-visibility: hidden;
transform: rotateY(calc(var(--n) * 90deg)) translateZ(calc(75 * var(--u)));
}
.hill .top {
top: calc(-75 * var(--u));
height: calc(150 * var(--u));
background:
radial-gradient(circle at 60% 30%, #ffe0a8 0 calc(1.5 * var(--u)), transparent calc(2 * var(--u))) 0 0 / calc(34 * var(--u)) calc(30 * var(--u)),
linear-gradient(#c9a066, #c9a066) 50% 100% / calc(12 * var(--u)) 50% no-repeat, /* the path to the door */
radial-gradient(closest-side, color-mix(in srgb, var(--grass) 70%, #fff), var(--grass) 70%, color-mix(in srgb, var(--grass) 80%, #000));
transform: rotateX(90deg);
}
/* a tree: two crossed planes, each cut to a tree outline; --x and --z are plain numbers, in units */
.tree {
transform: translate3d(calc(var(--x) * var(--u)), 0, calc(var(--z) * var(--u))) scale(var(--k));
}
.tree i {
position: absolute;
left: calc(-13 * var(--u));
top: calc(-44 * var(--u));
width: calc(26 * var(--u));
height: calc(44 * var(--u));
background: linear-gradient(color-mix(in srgb, var(--grass) 80%, #fff), color-mix(in srgb, var(--grass) 70%, #000) 80%, var(--wood) 80%);
clip-path: polygon(50% 0, 70% 32%, 60% 32%, 88% 64%, 74% 64%, 100% 80%, 58% 80%, 58% 100%, 42% 100%, 42% 80%, 0 80%, 26% 64%, 12% 64%, 40% 32%, 30% 32%);
}
.tree i + i {
transform: rotateY(90deg);
}
/* Tower sides: trapezoids 48 units wide at the base, 30 at the top. Each stands on its bottom
edge (the origin), steps out 24 units and leans in by atan(9 / 92) = 5.59deg. */
.tower i {
position: absolute;
left: calc(-24 * var(--u));
top: calc(-92.44 * var(--u));
width: calc(48 * var(--u));
height: calc(92.44 * var(--u)); /* the slanted length: √(92² + 9²) */
clip-path: polygon(18.75% 0, 81.25% 0, 100% 100%, 0 100%);
transform-origin: 50% 100%;
transform: rotateY(calc(var(--n) * 90deg)) translateZ(calc(24 * var(--u))) rotateX(5.59deg);
backface-visibility: hidden;
background:
linear-gradient(rgb(0 0 0 / var(--d)), rgb(0 0 0 / var(--d))),
linear-gradient(var(--wood) 0 calc(6 * var(--u)), transparent calc(6 * var(--u))),
radial-gradient(circle at 50% 38%, #ffb547 0 calc(3.5 * var(--u)), var(--wood) calc(4 * var(--u)) calc(5.5 * var(--u)), transparent calc(6 * var(--u))),
var(--plaster);
}
.tower .front {
background:
linear-gradient(var(--wood) 0 calc(6 * var(--u)), transparent calc(6 * var(--u))),
radial-gradient(circle at 50% 38%, #ffb547 0 calc(3.5 * var(--u)), var(--wood) calc(4 * var(--u)) calc(5.5 * var(--u)), transparent calc(6 * var(--u))),
radial-gradient(circle at 50% calc(100% - calc(16 * var(--u))), var(--wood) 0 calc(7.5 * var(--u)), transparent calc(8 * var(--u))),
linear-gradient(var(--wood), var(--wood)) 50% 100% / calc(15 * var(--u)) calc(16 * var(--u)) no-repeat,
var(--plaster);
}
/* the cap: the same trick with triangles, standing on the tower's top (y = -92 units) */
.cap i {
position: absolute;
left: calc(-21 * var(--u));
top: calc(-128.62 * var(--u));
width: calc(42 * var(--u));
height: calc(36.62 * var(--u));
clip-path: polygon(50% 0, 100% 100%, 0 100%);
transform-origin: 50% 100%;
transform: rotateY(calc(var(--n) * 90deg)) translateZ(calc(21 * var(--u))) rotateX(34.99deg);
backface-visibility: hidden;
background:
linear-gradient(rgb(0 0 0 / var(--d)), rgb(0 0 0 / var(--d))),
repeating-linear-gradient(transparent 0 calc(5 * var(--u)), rgb(0 0 0 / 0.22) calc(5 * var(--u)) calc(6 * var(--u))),
linear-gradient(color-mix(in srgb, var(--roof) 75%, #fff), var(--roof));
}
/* the hub: above the tower, in front of the cap */
.rotor {
transform: translate3d(0, calc(-100 * var(--u)), calc(27 * var(--u)));
}
.sails {
position: absolute;
transform-style: preserve-3d; /* sort each sail on its own */
animation: sails 6s linear infinite;
}
/* one blade pointing up from the hub: stock on the left edge, lattice and cloth beside it */
.sails b {
position: absolute;
left: calc(-2 * var(--u));
bottom: calc(5 * var(--u));
width: calc(24 * var(--u));
height: calc(70 * var(--u));
transform-origin: calc(2 * var(--u)) calc(100% + calc(5 * var(--u))); /* the hub */
transform: rotate(calc(var(--n) * 90deg));
background:
linear-gradient(90deg, var(--wood) 0 calc(4 * var(--u)), transparent calc(4 * var(--u))),
linear-gradient(90deg, transparent calc(100% - calc(2 * var(--u))), var(--wood) 0) 0 0 / 100% 80% no-repeat,
repeating-linear-gradient(transparent 0 calc(7 * var(--u)), var(--wood) calc(7 * var(--u)) calc(8.5 * var(--u))) 0 0 / 100% 80% no-repeat,
linear-gradient(#fff6e9, #efd5ab) calc(4 * var(--u)) 0 / calc(100% - calc(4 * var(--u))) 80% no-repeat;
}
.sails u {
position: absolute;
left: calc(-6 * var(--u));
top: calc(-6 * var(--u));
width: calc(12 * var(--u));
height: calc(12 * var(--u));
border-radius: 50%;
background: radial-gradient(circle at 35% 35%, #c8b5a8, var(--wood) 70%);
transform: translateZ(calc(1 * var(--u)));
}
@keyframes turn {
to { transform: rotateY(360deg); }
}
@keyframes sails {
to { transform: rotate(-360deg); }
}