<!--
This is a less-JS-more-CSS version of this:
https://codepen.io/amit_sheen/pen/myypOOg
-->
<div class="baseLight"></div>
<div class="shadows">
<div class="shadow" style="--i: 0"></div>
<div class="shadow" style="--i: 1"></div>
<div class="shadow" style="--i: 2"></div>
<div class="shadow" style="--i: 3"></div>
<div class="shadow" style="--i: 4"></div>
<div class="shadow" style="--i: 5"></div>
<div class="shadow" style="--i: 6"></div>
<div class="shadow" style="--i: 7"></div>
<div class="shadow" style="--i: 8"></div>
<div class="shadow" style="--i: 9"></div>
<div class="shadow" style="--i: 10"></div>
<div class="shadow" style="--i: 11"></div>
</div>
<div class="balls">
<div class="ball" style="--i: 0"></div>
<div class="ball" style="--i: 1"></div>
<div class="ball" style="--i: 2"></div>
<div class="ball" style="--i: 3"></div>
<div class="ball" style="--i: 4"></div>
<div class="ball" style="--i: 5"></div>
<div class="ball" style="--i: 6"></div>
<div class="ball" style="--i: 7"></div>
<div class="ball" style="--i: 8"></div>
<div class="ball" style="--i: 9"></div>
<div class="ball" style="--i: 10"></div>
<div class="ball" style="--i: 11"></div>
</div>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: #012;
}
:root {
--ball-size: 42;
--impect-radius: 192;
}
.baseLight {
position: fixed;
inset: calc(50% - (var(--ball-size) * 6px));
background-image: radial-gradient(closest-side, #fff, 2%, transparent);
transform: translate(calc(var(--mx) * 1px), calc(var(--my) * 1px));
}
.shadows, .balls {
position: fixed;
inset: 50%;
div {
position: absolute;
--x: calc(cos(var(--i) / 6 * pi) * 3);
--y: calc(sin(var(--i) / 6 * pi) * 3);
--hue: calc((var(--x) + var(--y)) * 4);
--dx: calc(var(--x) * var(--ball-size) - var(--mx, -9999));
--dy: calc(var(--y) * var(--ball-size) - var(--my, -9999));
--dist: calc(sqrt(var(--dx) * var(--dx) + var(--dy) * var(--dy)));
--dist-factor: calc((var(--impect-radius) - min(var(--dist), var(--impect-radius))) / var(--impect-radius));
--angle: atan2(var(--dy), var(--dx));
--_translate: calc(var(--dist-factor) * var(--ball-size) * 1px);
}
}
.shadow {
left: calc(var(--x) * var(--ball-size) * 1px);
top: calc((var(--y) - 0.5) * var(--ball-size) * 1px);
width: calc(var(--ball-size) * 2px);
height: calc(var(--ball-size) * 1px);
transform-origin: left;
background-image: linear-gradient(to right, #000, transparent);
opacity: var(--dist-factor, 0);
transform:
rotate(var(--angle))
translate(var(--_translate))
perspective(calc(var(--ball-size) * 2px))
rotateY(calc(var(--dist-factor) * -50deg - 20deg));
}
.ball {
--_bpx1: calc(var(--dist-factor) * 100% - 100%);
--_bpx2: calc(var(--dist-factor) * -100% + 50%);
left: calc((var(--x) - 0.5) * var(--ball-size) * 1px);
top: calc((var(--y) - 0.5) * var(--ball-size) * 1px);
width: calc(var(--ball-size) * 1px);
aspect-ratio: 1;
border-radius: 50%;
background-color: hsl(var(--hue), 100%, 50%);
background-image:
radial-gradient(circle at var(--_bpx1, -100%) 50%, #fff, 25%, transparent),
radial-gradient(circle at var(--_bpx2, 50%) 50%, transparent, #000a 100%);
background-repeat: no-repeat;
transform: rotate(var(--angle)) translate(var(--_translate));
&::before, &::after {
content: '';
position: absolute;
inset: calc(var(--ball-size) * 0.3px);
background-color: #fff;
border-radius: 50%;
background-image:
radial-gradient(closest-side, transparent 50%, #000),
radial-gradient(circle at 30% 50%, #000 35%, transparent 40%);
transform:
rotate(calc(var(--angle) * -1))
translate(calc(var(--ball-size) * var(--_tx, -0.2px)), calc(var(--ball-size) * -0.1px))
scaleY(var(--dist-factor, 0))
rotate(var(--angle));
}
&::after {
--_tx: 0.2px;
}
}
window.addEventListener('mousemove', (e) => {
document.body.style.setProperty('--mx', e.clientX - (window.innerWidth / 2));
document.body.style.setProperty('--my', e.clientY - (window.innerHeight / 2));
});
This Pen doesn't use any external CSS resources.