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 donut made of 24 circles. Each one turns to its angle and walks out with translateX, so it stands edge-on around the hole like a slice of the tube.
Your edited version
border-radius: 50%).rotateY(i × 15deg) and then translateX, not translateZ. That keeps it in the plane through the axis, edge-on to the path, like a slice of the tube. (translateZ would lay it tangent, like a carousel panel.)translateX distance is the radius from the hole's centre to the tube's centre; the ring size is the tube's thickness.cos(i × 15deg): teal at ring 0, violet halfway round, teal again at the end, so there is no seam.rotateY + translateX (not translateZ)border-radius: 50% cross-sectionsSass @for colour ramptwo-axis spin<div class="scene">
<div class="torus">
<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>
</div>
</div>
.scene {
perspective: 800px;
}
.torus {
position: relative;
width: 40px; /* tube thickness */
height: 40px;
transform-style: preserve-3d;
animation: tumble 16s linear infinite;
}
.torus i {
--hue: calc(214 - 39 * cos(var(--i) * 15deg)); /* 175 teal ... 253 violet ... 175 */
position: absolute;
inset: 0;
border: 2px solid hsl(var(--hue) 85% 64%);
border-radius: 50%;
background: hsl(var(--hue) 85% 64% / 0.14);
box-shadow: inset 0 0 10px hsl(var(--hue) 85% 64% / 0.45);
/* 24 × 15deg = 360deg; translateX keeps each ring edge-on to the circle */
transform: rotateY(calc(var(--i) * 15deg)) translateX(56px);
}
/* spins on two axes; both end 360deg after they start, so the loop is seamless */
@keyframes tumble {
from { transform: rotateZ(24deg) rotateX(-60deg) rotateY(0deg); }
to { transform: rotateZ(24deg) rotateX(300deg) rotateY(360deg); }
}
@use 'sass:math';
$n: 24;
$R: 56px; // from the centre of the hole to the centre of the tube
$d: 40px; // tube diameter
.d-torus {
position: relative;
width: $d;
height: $d;
transform-style: preserve-3d;
animation: d-torus-tumble 16s linear infinite;
// Each ring is one cross-section of the tube. rotateY picks the direction, translateX walks out
// along that direction, so the ring stands in the plane that contains the axis, edge-on to the
// circle it sits on (translateZ would lay it tangent instead, like a carousel panel).
i {
--c: color-mix(in srgb, var(--accent-2) var(--mix), var(--accent));
position: absolute;
inset: 0;
border: 2px solid var(--c);
border-radius: 50%;
background: color-mix(in srgb, var(--c) 14%, transparent);
box-shadow: inset 0 0 10px color-mix(in srgb, var(--c) 45%, transparent);
transform: rotateY(calc(var(--i) * #{math.div(360deg, $n)})) translateX($R);
}
// colour runs violet, teal, violet around the ring so there is no seam
@for $i from 0 to $n {
i:nth-child(#{$i + 1}) {
--i: #{$i};
--mix: #{math.round(math.div(math.abs($i - $n * 0.5), $n * 0.5) * 100) * 1%};
}
}
}
@keyframes d-torus-tumble {
from {
transform: rotateZ(24deg) rotateX(-60deg) rotateY(0deg);
}
to {
transform: rotateZ(24deg) rotateX(300deg) rotateY(360deg);
}
}