<div class="pl">
  <div class="pl__sphere"></div>
  <span class="pl__sphere-shadow"></span>
  <div class="pl__sphere"></div>
  <span class="pl__sphere-shadow"></span>
  <div class="pl__sphere"></div>
  <span class="pl__sphere-shadow"></span>
  <div class="pl__sphere"></div>
  <span class="pl__sphere-shadow"></span>
</div>
// Mixins
@mixin moveFilter($brightness,$blur)
  filter: brightness($brightness) blur($blur)
  -webkit-filter: brightness($brightness) blur($blur)
@mixin sphere($color)
  background: lighten($color,15%)
  box-shadow: 0 -0.75em 0.5em $color inset, 0 0 0.5em fade-out(lighten($color,10%),0.3)
// Normal styles
$sphereDiam: 2em
$spaceBetween: 1em
$sphereCount: 4
$xGridOffset: 0.5em
$animDur: 2s
*
  border: 0
  box-sizing: border-box
  margin: 0
  padding: 0
\:root
  font-size: calc(16px + (32 - 16) * (100vw - 320px) / (1280 - 320))
body
  font: 1em/1.5 sans-serif
  height: 100vh
.pl
  background: linear-gradient(rgb(4,23,73) calc(50% + 1.5em),rgb(6,39,121) calc(50% + 2.5em) calc(50% + 10.5em), rgb(4,23,73))
  position: relative
  overflow: hidden
  perspective: 800px
  transform-style: preserve-3d
  &, &:before
    width: 100%
    height: 100%
  &:before, &__sphere, &__sphere-shadow
    position: absolute
  &:before, &__sphere-shadow
    display: block
  &:before
    background: linear-gradient(rgb(6,39,121),rgba(6,39,121,0)), radial-gradient(100% 100% at 50% 50%,rgba(6,39,121,0) 37.5%,rgb(6,39,121) 50%), repeating-linear-gradient(0deg,rgba(8,55,170,0) 0 0.95em,rgb(8,55,170) 0.95em 1em), repeating-linear-gradient(90deg,rgba(8,55,170,0) 0 0.9em,rgb(8,55,170) 0.9em 1em) $xGridOffset 0
    content: ""
    top: 50%
    left: 50%
    transform: translate(-50%,-50%) rotateX(90deg) translateZ(-4em)
    width: 34em
    height: 34em
  &__sphere
    animation-name: moveSphere
    @include sphere(hsl(223,90%,55%))
    transform: translateZ(10em)
    &, &-shadow
      animation:
        duration: $animDur
        timing-function: ease-in-out
        iteration-count: infinite
      border-radius: 50%
      top: calc(50% - #{$sphereDiam / 2})
      left: calc(50% - #{$sphereDiam / 2})
      width: $sphereDiam
      height: $sphereDiam
      z-index: 1
    &-shadow
      animation-name: moveSphereShadow
      background-image: radial-gradient(100% 100% at center,rgba(0,0,0,0.2) 45%,rgba(0,0,0,0) 50%)
      transform: translateY(4em) translateZ(10em) rotateX(90deg)
    @for $s from 1 through $sphereCount
      &:nth-of-type(#{$s}), &-shadow:nth-of-type(#{$s})
        left: calc(50% - #{($sphereDiam + $spaceBetween) * (($sphereCount - ($s - 1)) - ($sphereCount / 2)) - ($spaceBetween / 2)})
        @if $s > 1
          animation-delay: ($animDur * 0.05) * ($s - 1)
// Animations
@keyframes moveSphere
  from, to
    @include moveFilter(100%,0)
    transform: translateZ(10em)
  25%, 75%
    @include moveFilter(100%,0)
    transform: translateZ(12em)
  50%
    @include moveFilter(80%,4px)
    transform: translateZ(-10em)
@keyframes moveSphereShadow
  from, to
    transform: translateY(4em) translateZ(10em) rotateX(90deg)
  25%, 75%
    transform: translateY(4em) translateZ(12em) rotateX(90deg)
  50%
    transform: translateY(4em) translateZ(-10em) rotateX(90deg)
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.