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.
A glass tube with rings of light rising inside. CSS has no curves in 3D, so the tube is 24 flat strips; Sass computes their width.
Your edited version
2 × r × tan(180° / n). For r = 80px and n = 24 that is ≈ 21px; add 1px so no seams show.rotateY(i × 15deg) translateZ(r). Semi-transparent strips let the far side show through, which is what makes it read as glass.rotateX(90deg). They rise by animating translate, which is applied before transform, so "up" is the tube's own vertical.strip width = 2r × tan(180° / n)rotateY + translateZper-strip shading<div class="scene">
<div class="cylinder">
<i style="--i:0"></i>
<i style="--i:1"></i>
<i style="--i:2"></i>
<i style="--i:3"></i>
<i style="--i:4"></i>
<i style="--i:5"></i>
<i style="--i:6"></i>
<i style="--i:7"></i>
<i style="--i:8"></i>
<i style="--i:9"></i>
<i style="--i:10"></i>
<i style="--i:11"></i>
<i style="--i:12"></i>
<i style="--i:13"></i>
<i style="--i:14"></i>
<i style="--i:15"></i>
<i style="--i:16"></i>
<i style="--i:17"></i>
<i style="--i:18"></i>
<i style="--i:19"></i>
<i style="--i:20"></i>
<i style="--i:21"></i>
<i style="--i:22"></i>
<i style="--i:23"></i>
<b></b><s></s>
<u></u><u></u><u></u>
</div>
</div>
.scene {
perspective: 800px;
}
.cylinder {
--r: 80px;
--h: 180px;
position: relative;
width: 22px; /* 2 × 80 × tan(7.5deg) + 1px */
height: var(--h);
transform-style: preserve-3d;
animation: cylinder-spin 14s linear infinite;
}
/* see-through strips: the far side of the tube shows through the near one */
.cylinder i {
position: absolute;
inset: 0;
background:
linear-gradient(rgb(255 255 255 / 0.55) 0 2px, transparent 2px calc(100% - 2px), rgb(255 255 255 / 0.4) calc(100% - 2px)),
linear-gradient(hsl(calc(170 + var(--i) * 6) 85% 62% / 0.5), hsl(calc(250 + var(--i) * 3) 80% 58% / 0.14));
transform: rotateY(calc(var(--i) * 15deg)) translateZ(var(--r));
}
/* lid (b) and floor (s): glass discs laid flat */
.cylinder b,
.cylinder s {
position: absolute;
left: calc(50% - var(--r));
width: calc(var(--r) * 2);
height: calc(var(--r) * 2);
box-sizing: border-box;
border: 1px solid rgb(255 255 255 / 0.4);
border-radius: 50%;
background: radial-gradient(circle, rgb(255 255 255 / 0.16), rgb(139 108 255 / 0.22) 70%);
transform: rotateX(90deg);
}
.cylinder b { top: calc(var(--r) * -1); }
.cylinder s {
top: calc(var(--h) - var(--r));
box-shadow: 0 0 40px rgb(46 230 214 / 0.55);
}
/* rings of light rising through the tube. `translate` moves them up the tube's own vertical,
before the rotateX that lays them flat. */
.cylinder u {
position: absolute;
top: calc(var(--h) - var(--r) + 8px);
left: calc(50% - var(--r) + 8px);
width: calc(var(--r) * 2 - 16px);
height: calc(var(--r) * 2 - 16px);
box-sizing: border-box;
border: 2px solid #2ee6d6;
border-radius: 50%;
box-shadow: 0 0 14px #2ee6d6, inset 0 0 14px #2ee6d6;
opacity: 0;
transform: rotateX(90deg);
animation: rise 3.6s linear infinite;
}
.cylinder u:nth-of-type(2) { animation-delay: -1.2s; }
.cylinder u:nth-of-type(3) { animation-delay: -2.4s; }
@keyframes cylinder-spin {
from { transform: rotateX(-20deg) rotateY(0deg); }
to { transform: rotateX(-20deg) rotateY(360deg); }
}
@keyframes rise {
0% { opacity: 0; translate: 0 -4px; }
15%, 80% { opacity: 1; }
100% { opacity: 0; translate: 0 calc(var(--h) * -1 + 12px); }
}
@use 'sass:math';
$n: 24;
$r: 58px;
$h: 132px;
// Width of one flat strip so that $n of them close the circle (+1px overlap against seams).
$strip: 2 * $r * math.tan(math.div(180deg, $n)) + 1px;
// A glass tube: 24 see-through strips (the far side shows through the near one), glowing rims,
// and rings of light travelling up inside it.
.d-cylinder {
position: relative;
width: $strip;
height: $h;
transform-style: preserve-3d;
animation: d-cylinder-spin 14s linear infinite;
// --i (0…23) comes from the markup: the hue sweeps round the tube
i {
position: absolute;
inset: 0;
background:
// bright rims at the top and bottom edge
linear-gradient(rgb(255 255 255 / 0.55) 0 2px, transparent 2px calc(100% - 2px), rgb(255 255 255 / 0.4) calc(100% - 2px)),
linear-gradient(hsl(calc(170 + var(--i) * 6) 85% 62% / 0.5), hsl(calc(250 + var(--i) * 3) 80% 58% / 0.14));
transform: rotateY(calc(var(--i) * #{math.div(360deg, $n)})) translateZ($r);
}
// lid and floor: glass discs with a rim
b,
s {
position: absolute;
left: calc(50% - #{$r});
width: $r * 2;
height: $r * 2;
border: 1px solid rgb(255 255 255 / 0.4);
border-radius: 50%;
background: radial-gradient(circle, rgb(255 255 255 / 0.16), color-mix(in srgb, var(--accent) 22%, transparent) 70%);
transform: rotateX(90deg);
}
b {
top: -$r;
}
s {
top: $h - $r;
box-shadow: 0 0 40px color-mix(in srgb, var(--accent-2) 55%, transparent);
}
// three rings of light rising through the tube, one after another; `translate` moves them in
// the tube's own vertical, before the rotateX that lays them flat
u {
position: absolute;
top: $h - $r;
left: calc(50% - #{$r - 6px});
width: ($r - 6px) * 2;
height: ($r - 6px) * 2;
border: 2px solid var(--accent-2);
border-radius: 50%;
box-shadow:
0 0 14px var(--accent-2),
inset 0 0 14px var(--accent-2);
opacity: 0;
transform: rotateX(90deg);
animation: d-cylinder-rise 3.6s linear infinite;
&:nth-of-type(2) {
animation-delay: -1.2s;
}
&:nth-of-type(3) {
animation-delay: -2.4s;
}
}
}
@keyframes d-cylinder-spin {
from {
transform: rotateX(-20deg) rotateY(0deg);
}
to {
transform: rotateX(-20deg) rotateY(360deg);
}
}
@keyframes d-cylinder-rise {
0% {
opacity: 0;
translate: 0 -4px;
}
15%,
80% {
opacity: 1;
}
100% {
opacity: 0;
translate: 0 ($h * -1 + 12px);
}
}