Pure CSS Cards & galleries #loop #sass-loop

Ring carousel in pure CSS

Panels arranged on a circle. The radius is computed in Sass with math.tan().

  • 32 lines of CSS
  • 8 lines of HTML
  • No JavaScript
  • No dependencies
  • MIT licensed
GitHub

How it works

  1. Every panel sits in the same spot, then gets rotateY(n × 360° / count) followed by translateZ(radius).
  2. Order matters: rotate first, then translate — so each panel moves outward along its own rotated axis.
  3. Radius for a closed ring: (width / 2) / tan(180° / count). For 8 panels of 80px that is ≈ 97px; add a little for gaps.
  4. Spin the ring, and tilt a wrapper so you look slightly down on it.

Key techniques

  • preserve-3d
  • rotateY + translateZ
  • sass:math

Copy-paste code

HTML 8 lines

<div class="scene">
  <div class="ring">
    <div style="--i:0">1</div><div style="--i:1">2</div>
    <div style="--i:2">3</div><div style="--i:3">4</div>
    <div style="--i:4">5</div><div style="--i:5">6</div>
    <div style="--i:6">7</div><div style="--i:7">8</div>
  </div>
</div>

CSS 32 lines

.scene {
  perspective: 900px;
}

.ring {
  --count: 8;
  --radius: 115px;
  position: relative;
  width: 80px;
  height: 110px;
  transform-style: preserve-3d;
  animation: ring-spin 14s linear infinite;
}

.ring > div {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border-radius: 10px;
  font: 800 1.4rem system-ui;
  color: #fff;
  background: hsl(calc(250 + var(--i) * 18) 80% 60% / 0.85);
  transform:
    rotateY(calc(var(--i) * 360deg / var(--count)))
    translateZ(var(--radius));
}

@keyframes ring-spin {
  from { transform: rotateX(-14deg) rotateY(0deg); }
  to   { transform: rotateX(-14deg) rotateY(-360deg); }
}

Sass source 44 lines

@use 'sass:math';

$count: 8;
$width: 72px;
// Distance from the centre so that $count panels of $width form a closed ring (+ a gap).
$radius: math.div($width * 0.5, math.tan(math.div(180deg, $count))) + 16px;

.d-carousel {
  transform-style: preserve-3d;
  transform: rotateX(-14deg);

  &__ring {
    position: relative;
    width: $width;
    height: 96px;
    transform-style: preserve-3d;
    animation: d-carousel-spin 14s linear infinite;
  }

  i {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    border-radius: 10px;
    color: #fff;
    font-size: 22px;
    font-style: normal;
    font-weight: 800;

    @for $i from 1 through $count {
      &:nth-child(#{$i}) {
        background: hsla(250 + $i * 18, 80%, 60%, 0.85);
        transform: rotateY(math.div(360deg, $count) * $i) translateZ($radius);
      }
    }
  }
}

@keyframes d-carousel-spin {
  to {
    transform: rotateY(-360deg);
  }
}
Standalone snippet · plain CSS, no build step, no dependencies.