Pure CSS Shapes & solids #loop #shape

Cylinder in pure CSS

CSS has no curves in 3D, so a cylinder is 24 flat strips. Sass computes the strip width.

  • 36 lines of CSS
  • 29 lines of HTML
  • No JavaScript
  • No dependencies
  • MIT licensed
GitHub

How it works

  1. There are no curved surfaces in CSS 3D, so approximate: 24 flat strips arranged in a circle.
  2. Strip width for a closed ring is 2 × r × tan(180° / n). For r = 80px and n = 24 that is ≈ 21px; add 1px so no seams show.
  3. Placement is the carousel trick: rotateY(i × 15deg) translateZ(r).
  4. Shift the hue per strip and the flat facets read as a smooth shaded surface.

Key techniques

  • strip width = 2r × tan(180° / n)
  • rotateY + translateZ
  • per-strip shading

Copy-paste code

HTML 29 lines

<div class="scene">
  <div class="cylinder">
    <i style="--i:0"></i>
    <i style="--i:1"></i>
    <i style="--i:2"></i>
    <i style="--i:3"></i>
    <i style="--i:4"></i>
    <i style="--i:5"></i>
    <i style="--i:6"></i>
    <i style="--i:7"></i>
    <i style="--i:8"></i>
    <i style="--i:9"></i>
    <i style="--i:10"></i>
    <i style="--i:11"></i>
    <i style="--i:12"></i>
    <i style="--i:13"></i>
    <i style="--i:14"></i>
    <i style="--i:15"></i>
    <i style="--i:16"></i>
    <i style="--i:17"></i>
    <i style="--i:18"></i>
    <i style="--i:19"></i>
    <i style="--i:20"></i>
    <i style="--i:21"></i>
    <i style="--i:22"></i>
    <i style="--i:23"></i>
    <b></b>
  </div>
</div>

CSS 36 lines

.scene {
  perspective: 800px;
}

.cylinder {
  --r: 80px;
  position: relative;
  width: 22px;                 /* 2 × 80 × tan(7.5deg) + 1px */
  height: 170px;
  transform-style: preserve-3d;
  animation: cylinder-spin 9s linear infinite;
}

.cylinder i {
  position: absolute;
  inset: 0;
  background: hsl(calc(170 + var(--i) * 15) 80% 58%);
  transform: rotateY(calc(var(--i) * 15deg)) translateZ(var(--r));
}

/* lid */
.cylinder b {
  position: absolute;
  top: calc(var(--r) * -1);
  left: calc(50% - var(--r));
  width: calc(var(--r) * 2);
  height: calc(var(--r) * 2);
  border-radius: 50%;
  background: #17506e;
  transform: rotateX(90deg);
}

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

Sass source 44 lines

@use 'sass:math';

$n: 24;
$r: 62px;
// Width of one flat strip so that $n of them close the circle (+1px overlap against seams).
$strip: 2 * $r * math.tan(math.div(180deg, $n)) + 1px;

.d-cylinder {
  position: relative;
  width: $strip;
  height: 130px;
  transform-style: preserve-3d;
  animation: d-cylinder-spin 9s linear infinite;

  // --i (0…23) comes from the markup
  i {
    position: absolute;
    inset: 0;
    background: hsl(calc(170 + var(--i) * 15) 80% 58%);
    transform: rotateY(calc(var(--i) * #{math.div(360deg, $n)})) translateZ($r);
  }

  // lid
  b {
    position: absolute;
    top: -$r;
    left: calc(50% - #{$r});
    width: $r * 2;
    height: $r * 2;
    border-radius: 50%;
    background: radial-gradient(circle, #fff3, transparent 70%), #17506e;
    transform: rotateX(90deg);
  }
}

@keyframes d-cylinder-spin {
  from {
    transform: rotateX(-22deg) rotateY(0deg);
  }

  to {
    transform: rotateX(-22deg) rotateY(360deg);
  }
}
Standalone snippet · plain CSS, no build step, no dependencies.