Twisting puzzle cube
Pure CSSA 3×3×3 cube built as three layers that take turns twisting a quarter turn. Each layer is one flat box with the stickers painted on by gradients.
Eleven latitude discs stack into a banded sphere around a shaded disc that always faces you. That disc cuts the rings in half, so their far side slips behind the planet while a moon circles through.
Your edited version
a sits R·sin(a) above the equator and has radius R·cos(a). CSS does the trig itself: cos(var(--k) * 15deg).latitude discs: R·cos / R·sinbillboard disc hides what is behindintersecting planes are split and sortednested counter-rotations<div class="scene">
<div class="planet">
<div class="body">
<i style="--k:-5"></i>
<i style="--k:-4"></i>
<i style="--k:-3"></i>
<i style="--k:-2"></i>
<i style="--k:-1"></i>
<i style="--k:0"></i>
<i style="--k:1"></i>
<i style="--k:2"></i>
<i style="--k:3"></i>
<i style="--k:4"></i>
<i style="--k:5"></i>
<u></u>
<b></b>
<b></b>
<b></b>
<div class="orbit">
<div class="arm"><div class="moon"></div></div>
</div>
</div>
</div>
</div>
.scene {
perspective: 800px;
}
.planet {
--R: 40px;
position: relative;
width: 0;
height: 0;
transform-style: preserve-3d;
animation: spin 40s linear infinite;
}
/* the planet's frame, tilted 12deg on its axis */
.body {
position: absolute;
transform-style: preserve-3d;
transform: rotateZ(-12deg);
}
/* latitude discs: k = -5 ... 5, every 15deg */
.body i {
--r: calc(cos(var(--k) * 15deg) * var(--R));
--c: color-mix(in srgb, #ffb547 80%, #ff4d9d);
position: absolute;
top: calc(var(--r) * -1);
left: calc(var(--r) * -1);
width: calc(var(--r) * 2);
height: calc(var(--r) * 2);
border-radius: 50%;
background: radial-gradient(circle, var(--c) 55%, color-mix(in srgb, var(--c) 70%, #000));
transform: translateY(calc(sin(var(--k) * 15deg) * var(--R) * -1)) rotateX(90deg);
}
.body i:nth-child(odd) { --c: color-mix(in srgb, #ffb547 55%, #fff); }
.body i:nth-child(4n + 1) { --c: color-mix(in srgb, #ff4d9d 70%, #ffb547); }
.body i:nth-child(-n + 2),
.body i:nth-child(n + 10) { --c: color-mix(in srgb, #8b6cff 55%, #ffb547); } /* polar caps */
/* the body: a shaded disc that always faces you (undoes the tilt, the spin, the camera height) */
.body u {
position: absolute;
inset: calc(var(--R) * -1);
border-radius: 50%;
background: radial-gradient(circle at 36% 30%, #ffdaa3, #ffb547 32%, #ff6c83 62%, #614cb3);
animation: face-you 40s linear infinite;
}
/* rings: flat circles in the equator plane */
.body b {
position: absolute;
border-radius: 50%;
transform: rotateX(90deg);
}
.body b:nth-of-type(1) { inset: -52px; border: 7px solid rgb(139 108 255 / 0.38); }
.body b:nth-of-type(2) { inset: -62px; border: 8px solid rgb(197 186 253 / 0.85); }
.body b:nth-of-type(3) { inset: -72.8px; border: 2px solid rgb(139 108 255 / 0.85); }
/* the moon's orbit: a faint dashed circle, inclined 8deg, turning on its own axis */
.orbit {
position: absolute;
inset: -92px;
border: 1px dashed rgb(46 230 214 / 0.4);
border-radius: 50%;
transform-style: preserve-3d;
animation: orbit 7s linear infinite;
}
/* undo the orbit's turn ... */
.arm {
position: absolute;
top: -7px;
left: calc(50% - 7px);
width: 14px;
height: 14px;
transform-style: preserve-3d;
animation: unorbit 7s linear infinite;
}
/* ... then the incline, the tilt, the spin and the camera height */
.moon {
position: absolute;
inset: 0;
border-radius: 50%;
background: radial-gradient(circle at 34% 30%, #fff 0 8%, #2ee6d6 45%, #125c56);
animation: moon 40s linear infinite;
}
@keyframes spin {
from { transform: rotateX(-22deg) rotateY(0deg); }
to { transform: rotateX(-22deg) rotateY(360deg); }
}
@keyframes face-you {
from { transform: rotateZ(12deg) rotateY(0deg) rotateX(22deg); }
to { transform: rotateZ(12deg) rotateY(-360deg) rotateX(22deg); }
}
@keyframes orbit {
from { transform: rotateX(82deg) rotateZ(0deg); }
to { transform: rotateX(82deg) rotateZ(360deg); }
}
@keyframes unorbit {
to { transform: rotateZ(-360deg); }
}
@keyframes moon {
from { transform: rotateX(-82deg) rotateZ(12deg) rotateY(0deg) rotateX(22deg); }
to { transform: rotateX(-82deg) rotateZ(12deg) rotateY(-360deg) rotateX(22deg); }
}
@use 'sass:math';
$R: 40px; // planet radius
$view: 22deg; // camera height above the horizon
$axis: 12deg; // the planet's axial tilt
$spin: 40s;
$moon-t: 7s;
$orbit: 92px;
$incline: 8deg; // the moon's orbit against the rings
// Undo every turn above an element, innermost first, so it faces you (billboarding).
$face-body: rotateZ($axis);
$face-view: rotateX($view);
.d-planet {
position: relative;
width: 0;
height: 0;
transform-style: preserve-3d;
animation: d-planet-spin $spin linear infinite;
// the planet's own frame: tilted on its axis. Discs, rings and orbit all live in it.
&__body {
position: absolute;
transform-style: preserve-3d;
transform: rotateZ(-$axis);
}
// 11 latitude discs, every 15deg from -75deg to 75deg: a disc at latitude a sits R·sin(a) up and
// has radius R·cos(a). Together their rims trace the sphere; their tops show as bands.
i {
--r: calc(cos(var(--k) * 15deg) * #{$R});
position: absolute;
top: calc(var(--r) * -1);
left: calc(var(--r) * -1);
width: calc(var(--r) * 2);
height: calc(var(--r) * 2);
border-radius: 50%;
background: radial-gradient(circle, var(--c) 55%, color-mix(in srgb, var(--c) 70%, #000));
transform: translateY(calc(sin(var(--k) * 15deg) * #{-$R})) rotateX(90deg);
--c: color-mix(in srgb, var(--warm) 80%, var(--hot));
&:nth-child(odd) {
--c: color-mix(in srgb, var(--warm) 55%, #fff);
}
&:nth-child(4n + 1) {
--c: color-mix(in srgb, var(--hot) 70%, var(--warm));
}
// polar caps a little cooler
&:nth-child(-n + 2),
&:nth-child(n + 10) {
--c: color-mix(in srgb, var(--accent) 55%, var(--warm));
}
}
// The body: a shaded disc through the centre that always faces you. It hides whatever passes
// behind the planet and nothing in front of it, exactly as a real sphere would.
u {
position: absolute;
inset: -$R;
border-radius: 50%;
background: radial-gradient(
circle at 36% 30%,
color-mix(in srgb, var(--warm) 50%, #fff),
var(--warm) 32%,
color-mix(in srgb, var(--hot) 70%, var(--warm)) 62%,
color-mix(in srgb, var(--accent) 70%, #000) 100%
);
animation: d-planet-face $spin linear infinite;
}
// rings: flat circles in the equator plane (rotateX(90deg)). The body disc crosses them, so
// the browser draws their far half behind the planet and the near half in front.
b {
position: absolute;
border-radius: 50%;
transform: rotateX(90deg);
&:nth-of-type(1) {
inset: -$R * 1.3;
border: 7px solid color-mix(in srgb, var(--accent) 38%, transparent);
}
&:nth-of-type(2) {
inset: -$R * 1.55;
border: 8px solid color-mix(in srgb, color-mix(in srgb, var(--accent) 40%, var(--text)) 85%, transparent);
}
&:nth-of-type(3) {
inset: -$R * 1.82;
border: 2px solid color-mix(in srgb, var(--accent) 85%, transparent);
}
}
// the moon's orbit: a faint dashed circle, a little inclined, turning on its own axis (rotateZ)
&__orbit {
position: absolute;
inset: -$orbit;
border: 1px dashed color-mix(in srgb, var(--accent-2) 40%, transparent);
border-radius: 50%;
transform-style: preserve-3d;
animation: d-planet-orbit $moon-t linear infinite;
}
// the moon rides the top of the orbit circle; two nested turns undo the orbit and then the
// tilts and the spin, so it is always a round face
&__arm {
position: absolute;
top: -7px;
left: calc(50% - 7px);
width: 14px;
height: 14px;
transform-style: preserve-3d;
animation: d-planet-unorbit $moon-t linear infinite;
}
&__moon {
position: absolute;
inset: 0;
border-radius: 50%;
background: radial-gradient(
circle at 34% 30%,
#fff 0 8%,
var(--accent-2) 45%,
color-mix(in srgb, var(--accent-2) 40%, #000) 100%
);
animation: d-planet-moon $spin linear infinite;
}
}
@keyframes d-planet-spin {
from {
transform: rotateX(-$view) rotateY(0deg);
}
to {
transform: rotateX(-$view) rotateY(360deg);
}
}
// inverse of body tilt, then of the spin, then of the camera height
@keyframes d-planet-face {
from {
transform: $face-body rotateY(0deg) $face-view;
}
to {
transform: $face-body rotateY(-360deg) $face-view;
}
}
@keyframes d-planet-orbit {
from {
transform: rotateX(90deg - $incline) rotateZ(0deg);
}
to {
transform: rotateX(90deg - $incline) rotateZ(360deg);
}
}
@keyframes d-planet-unorbit {
to {
transform: rotateZ(-360deg);
}
}
@keyframes d-planet-moon {
from {
transform: rotateX($incline - 90deg) $face-body rotateY(0deg) $face-view;
}
to {
transform: rotateX($incline - 90deg) $face-body rotateY(-360deg) $face-view;
}
}