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 map drawn with gradients lies on a tilted plane. The pins undo the plane’s rotation around their tip, so they stand up and face you, then bounce in turn above a pulsing ring.
Your edited version
rotateX(54deg) rotateZ(-14deg).rotateZ(14deg) rotateX(-54deg), around transform-origin: 50% 100%: its tip. It stands up exactly on its spot and faces you.translateY and a squash scale after it, so the hop happens in screen space. animation-delay: calc(var(--i) * 0.8s) makes the pins take turns.--u, tied to the canvas: the map is 190 × 150 units and a pin 26 wide, so the scene is the same share of a gallery card, the editor and a recording canvas.map from layered gradientspin: inverse rotation, transform-origin bottomring stays in the ground planestaggered animation-delay<div class="scene">
<div class="map">
<div class="plane">
<i style="--x:24%;--y:34%;--c:#ff4d9d;--i:0"><b></b></i>
<i style="--x:58%;--y:66%;--c:#ffb547;--i:1"><b></b></i>
<i style="--x:80%;--y:28%;--c:#8b6cff;--i:2"><b></b></i>
</div>
</div>
</div>
.scene {
/* one base unit: every length below is a multiple of it, so the map is the same share of a
gallery card, the editor, a full screen and a recording canvas */
--u: 0.4vmin;
display: grid;
place-items: center;
perspective: calc(800 * var(--u));
}
.map {
position: relative;
width: calc(210 * var(--u));
height: calc(180 * var(--u));
transform-style: preserve-3d;
}
.plane {
box-sizing: content-box; /* its size is its inside, its edge outside it, on any page */
--land: #1c5a60;
--water: #262a55;
--road: rgb(255 181 71 / 0.6);
position: absolute;
left: calc(10 * var(--u));
top: calc(18 * var(--u));
width: calc(190 * var(--u));
height: calc(150 * var(--u));
border: calc(1 * var(--u)) solid rgb(46 230 214 / 0.45);
border-radius: calc(14 * var(--u));
background:
linear-gradient(28deg, transparent calc(50% - calc(1.5 * var(--u))), var(--road) 0 calc(50% + calc(1.5 * var(--u))), transparent 0),
linear-gradient(90deg, transparent 63%, var(--road) 0 calc(63% + calc(3 * var(--u))), transparent 0),
radial-gradient(circle at 115% 125%, transparent 56%, var(--water) 0 59%, transparent 0),
radial-gradient(ellipse 30% 25% at 26% 32%, var(--land) 98%, transparent),
radial-gradient(ellipse 26% 22% at 66% 68%, var(--land) 98%, transparent),
radial-gradient(ellipse 15% 13% at 82% 24%, var(--land) 98%, transparent),
radial-gradient(ellipse 12% 10% at 26% 80%, var(--land) 98%, transparent),
linear-gradient(90deg, rgb(236 238 251 / 0.07) calc(1 * var(--u)), transparent calc(1 * var(--u))) 0 0 / calc(19 * var(--u)) calc(19 * var(--u)),
linear-gradient(rgb(236 238 251 / 0.07) calc(1 * var(--u)), transparent calc(1 * var(--u))) 0 0 / calc(19 * var(--u)) calc(19 * var(--u)),
var(--water);
transform-style: preserve-3d;
transform: rotateX(54deg) rotateZ(-14deg);
}
/* the anchor: a point on the map, one unit above it */
.plane i {
position: absolute;
left: var(--x);
top: var(--y);
width: 0;
height: 0;
transform-style: preserve-3d;
transform: translateZ(calc(1 * var(--u)));
}
.plane i::before,
.plane i::after {
content: '';
position: absolute;
border-radius: 50%;
animation: 2.4s infinite;
animation-delay: calc(var(--i) * 0.8s);
}
/* pulse ring, flat on the ground */
.plane i::before {
top: calc(-16 * var(--u));
left: calc(-16 * var(--u));
width: calc(32 * var(--u));
height: calc(32 * var(--u));
border: calc(2 * var(--u)) solid var(--c);
box-sizing: border-box;
opacity: 0;
animation-name: ring;
animation-timing-function: ease-out;
}
/* contact shadow */
.plane i::after {
top: calc(-4 * var(--u));
left: calc(-7 * var(--u));
width: calc(14 * var(--u));
height: calc(8 * var(--u));
background: rgb(0 0 0 / 0.45);
animation-name: shadow;
animation-timing-function: ease-in-out;
}
/* the pin: tip at bottom centre = transform-origin */
.plane b {
position: absolute;
bottom: 0;
left: calc(-13 * var(--u));
width: calc(26 * var(--u));
height: calc(32 * var(--u));
transform-origin: 50% 100%;
transform: rotateZ(14deg) rotateX(-54deg); /* the plane, undone */
animation: bounce 2.4s ease-in-out infinite;
animation-delay: calc(var(--i) * 0.8s);
}
/* head: three round corners, turned so the sharp one points down */
.plane b::before {
content: '';
position: absolute;
width: calc(26 * var(--u));
height: calc(26 * var(--u));
border-radius: 50% 50% 50% 0;
background: radial-gradient(circle at 70% 30%, color-mix(in srgb, var(--c) 40%, #fff), var(--c) 55%, color-mix(in srgb, var(--c) 60%, #000));
transform: rotate(-45deg);
}
.plane b::after {
content: '';
position: absolute;
top: calc(8 * var(--u));
left: calc(8 * var(--u));
width: calc(10 * var(--u));
height: calc(10 * var(--u));
border-radius: 50%;
background: #fff;
}
@keyframes bounce {
0%, 36%, 100% { transform: rotateZ(14deg) rotateX(-54deg) translateY(0) scale(1, 1); }
14% { transform: rotateZ(14deg) rotateX(-54deg) translateY(calc(-16 * var(--u))) scale(0.94, 1.06); }
26% { transform: rotateZ(14deg) rotateX(-54deg) translateY(0) scale(1.14, 0.86); }
}
@keyframes shadow {
0%, 36%, 100% { opacity: 1; transform: scale(1); }
14% { opacity: 0.45; transform: scale(0.6); }
}
@keyframes ring {
0%, 24% { opacity: 0; transform: scale(0.2); }
27% { opacity: 1; transform: scale(0.3); }
70%, 100% { opacity: 0; transform: scale(1.5); }
}