Zonal
A pure CSS zonal spinner featuring two coloured orbs that flash in and out at randomised positions using CSS custom properties, offset by half a cycle.
Generated using Grepped's AI UI component generator — created from scratch, not pulled from a library.
spinnerzonalloaderorbteleportcss-variables
Live Preview — customize or regenerate this in the workspace
Loading preview…
Design Intent
Two orbs that flash between random positions using CSS custom property coordinate offsets, staggered by half a cycle.
CSS
css
body { margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center; background: #0f0f0f; }
:root { --primary: #6366f1; --secondary: #ec4899; }
.zonal {
--size: 20;
height: calc(var(--size) * 1px);
width: calc(var(--size) * 1px);
position: relative;
}
.zonal:after,
.zonal:before {
animation: zonal-flash 4s infinite ease;
border-radius: 100%;
content: '';
position: absolute;
height: 100%;
width: 100%;
}
.zonal:after {
background-color: var(--primary);
--x1: 55; --x2: -196; --x3: -122; --x4: 2; --x5: 198;
--y1: 113; --y2: 221; --y3: -141; --y4: -164; --y5: -44;
}
.zonal:before {
animation-delay: calc(4s * -0.5);
background-color: var(--secondary);
--x1: -151; --x2: -192; --x3: -112; --x4: -109; --x5: 155;
--y1: 222; --y2: -121; --y3: -227; --y4: -115; --y5: 129;
}
@keyframes zonal-flash {
0% { transform: scale(0) translate(calc(var(--x1) * 1%), calc(var(--y1) * 1%)); }
10% { transform: scale(1) translate(calc(var(--x1) * 1%), calc(var(--y1) * 1%)); }
20% { transform: scale(0) translate(calc(var(--x2) * 1%), calc(var(--y2) * 1%)); }
30% { transform: scale(1) translate(calc(var(--x2) * 1%), calc(var(--y2) * 1%)); }
40% { transform: scale(0) translate(calc(var(--x3) * 1%), calc(var(--y3) * 1%)); }
50% { transform: scale(1) translate(calc(var(--x3) * 1%), calc(var(--y3) * 1%)); }
60% { transform: scale(0) translate(calc(var(--x4) * 1%), calc(var(--y4) * 1%)); }
70% { transform: scale(1) translate(calc(var(--x4) * 1%), calc(var(--y4) * 1%)); }
80% { transform: scale(0) translate(calc(var(--x5) * 1%), calc(var(--y5) * 1%)); }
90% { transform: scale(1) translate(calc(var(--x5) * 1%), calc(var(--y5) * 1%)); }
100% { transform: scale(0) translate(calc(var(--x1) * 1%), calc(var(--y1) * 1%)); }
}HTML
html
<div class="zonal"></div>Full Source
html
<!DOCTYPE html>
<html>
<head>
<style>
body { margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center; background: #0f0f0f; }
:root { --primary: #6366f1; --secondary: #ec4899; }
.zonal {
--size: 20;
height: calc(var(--size) * 1px);
width: calc(var(--size) * 1px);
position: relative;
}
.zonal:after,
.zonal:before {
animation: zonal-flash 4s infinite ease;
border-radius: 100%;
content: '';
position: absolute;
height: 100%;
width: 100%;
}
.zonal:after {
background-color: var(--primary);
--x1: 55; --x2: -196; --x3: -122; --x4: 2; --x5: 198;
--y1: 113; --y2: 221; --y3: -141; --y4: -164; --y5: -44;
}
.zonal:before {
animation-delay: calc(4s * -0.5);
background-color: var(--secondary);
--x1: -151; --x2: -192; --x3: -112; --x4: -109; --x5: 155;
--y1: 222; --y2: -121; --y3: -227; --y4: -115; --y5: 129;
}
@keyframes zonal-flash {
0% { transform: scale(0) translate(calc(var(--x1) * 1%), calc(var(--y1) * 1%)); }
10% { transform: scale(1) translate(calc(var(--x1) * 1%), calc(var(--y1) * 1%)); }
20% { transform: scale(0) translate(calc(var(--x2) * 1%), calc(var(--y2) * 1%)); }
30% { transform: scale(1) translate(calc(var(--x2) * 1%), calc(var(--y2) * 1%)); }
40% { transform: scale(0) translate(calc(var(--x3) * 1%), calc(var(--y3) * 1%)); }
50% { transform: scale(1) translate(calc(var(--x3) * 1%), calc(var(--y3) * 1%)); }
60% { transform: scale(0) translate(calc(var(--x4) * 1%), calc(var(--y4) * 1%)); }
70% { transform: scale(1) translate(calc(var(--x4) * 1%), calc(var(--y4) * 1%)); }
80% { transform: scale(0) translate(calc(var(--x5) * 1%), calc(var(--y5) * 1%)); }
90% { transform: scale(1) translate(calc(var(--x5) * 1%), calc(var(--y5) * 1%)); }
100% { transform: scale(0) translate(calc(var(--x1) * 1%), calc(var(--y1) * 1%)); }
}
</style>
</head>
<body>
<div class="zonal"></div>
</body>
</html>