Pure CSS Shapes & solids #loop #shape

Cut gem in pure CSS

A crown of trapezoid facets leaning 45° above the girdle and a deep pavilion of triangles below, with a glint that walks from facet to facet.

  • 85 lines of CSS
  • 21 lines of HTML
  • No JavaScript
  • No dependencies
  • Snippet under MIT
GitHub

How it works

  1. Every length is a multiple of one base unit, --u, so the stone is the same share of a gallery card, the editor and a recording canvas. The numbers below are those units.
  2. The widest ring (the girdle) is 8 edges, 60 units from the axis. Every facet is hinged on one of those edges: rotateY(i × 45deg) translateZ(calc(60 * var(--u))).
  3. Crown facets stand on the girdle and lean in 45°: they rise 24 while stepping 24 inward, so their top edge is 36/60 as long. That ratio is the trapezoid's clip-path: 20% to 80%.
  4. Pavilion facets hang from the girdle (transform-origin: top) and lean in by atan(60 / 72) ≈ 39.8°, so a 72-deep point forms where the tips meet.
  5. The glint is a white streak on every facet that only fades in and out. Its animation-delay grows with --i, so the sparkle walks around the stone. Only opacity animates.

Key techniques

  • trapezoid + triangle clip-paths
  • hinge on the girdle: transform-origin top / bottom
  • lean = atan(radius / depth)
  • staggered opacity glint

Copy-paste code

HTML 21 lines

<div class="scene">
  <div class="gem">
    <i class="crown" style="--i:0"></i>
    <i class="crown" style="--i:1"></i>
    <i class="crown" style="--i:2"></i>
    <i class="crown" style="--i:3"></i>
    <i class="crown" style="--i:4"></i>
    <i class="crown" style="--i:5"></i>
    <i class="crown" style="--i:6"></i>
    <i class="crown" style="--i:7"></i>
    <i class="pav" style="--i:0"></i>
    <i class="pav" style="--i:1"></i>
    <i class="pav" style="--i:2"></i>
    <i class="pav" style="--i:3"></i>
    <i class="pav" style="--i:4"></i>
    <i class="pav" style="--i:5"></i>
    <i class="pav" style="--i:6"></i>
    <i class="pav" style="--i:7"></i>
    <b></b>
  </div>
</div>

CSS 85 lines

.scene {
  /* one base unit: every length below is a multiple of it, so the stone is the same share of a
     card, the editor, a full screen and a recording canvas */
  --u: 0.55vmin;
  perspective: calc(800 * var(--u));
}

.gem {
  position: relative;
  width: calc(49.71 * var(--u)); /* one girdle edge = 2 × 60 × tan(180deg / 8) */
  height: calc(96 * var(--u));   /* crown 24 + pavilion 72 */
  transform-style: preserve-3d;
  animation: spin 14s linear infinite;
}

.gem i {
  --alpha: 0.2;
  position: absolute;
  left: 0;
  width: 100%;
}

.gem i:nth-child(odd) {
  --alpha: 0.4;
}

/* the glint: fades in and out, one facet after another */
.gem i::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 30%, rgb(255 255 255 / 0.8) 50%, transparent 70%);
  opacity: 0;
  animation: glint 3.2s ease-in-out infinite;
  animation-delay: calc(var(--i) * -0.4s + var(--late, 0s));
}

/* crown: trapezoids standing on the girdle, leaning 45deg inward */
.gem .crown {
  bottom: calc(72 * var(--u));
  height: calc(33.94 * var(--u)); /* slant = √2 × 24 */
  transform-origin: 50% 100%;
  transform: rotateY(calc(var(--i) * 45deg)) translateZ(calc(60 * var(--u))) rotateX(45deg);
  clip-path: polygon(20% 0, 80% 0, 100% 100%, 0 100%); /* top edge = 36 / 60 of the bottom */
  background:
    linear-gradient(rgb(46 230 214 / 0.8), rgb(46 230 214 / 0.8)) top / 100% calc(1.5 * var(--u)) no-repeat,
    linear-gradient(rgb(46 230 214 / 0.8), rgb(46 230 214 / 0.8)) bottom / 100% calc(1.5 * var(--u)) no-repeat,
    linear-gradient(to top, rgb(46 230 214 / var(--alpha)), rgb(46 230 214 / 0.12));
}

/* pavilion: triangles hanging from the girdle, leaning in by atan(60 / 72) */
.gem .pav {
  --late: -1.7s;
  top: calc(24 * var(--u));
  height: calc(93.72 * var(--u)); /* slant = √(72² + 60²) */
  transform-origin: 50% 0;
  transform: rotateY(calc(var(--i) * 45deg)) translateZ(calc(60 * var(--u))) rotateX(-39.81deg);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  background:
    linear-gradient(to top right, transparent 50%, rgb(139 108 255 / 0.8) 50% calc(50% + 1.2 * var(--u)), transparent calc(50% + 1.2 * var(--u))) left / 50% 100% no-repeat,
    linear-gradient(to top left, transparent 50%, rgb(139 108 255 / 0.8) 50% calc(50% + 1.2 * var(--u)), transparent calc(50% + 1.2 * var(--u))) right / 50% 100% no-repeat,
    linear-gradient(rgb(139 108 255 / var(--alpha)), rgb(255 77 157 / 0.14));
}

/* table: a flat octagon on top, 36 from the axis */
.gem b {
  position: absolute;
  left: calc(50% - 36 * var(--u));
  top: calc(-36 * var(--u));
  width: calc(72 * var(--u));
  height: calc(72 * var(--u));
  clip-path: polygon(29.29% 0, 70.71% 0, 100% 29.29%, 100% 70.71%, 70.71% 100%, 29.29% 100%, 0 70.71%, 0 29.29%);
  background: linear-gradient(135deg, rgb(46 230 214 / 0.55), rgb(255 255 255 / 0.35), rgb(46 230 214 / 0.3));
  transform: rotateX(90deg);
}

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

@keyframes glint {
  0%, 55%, 100% { opacity: 0; }
  78%           { opacity: 0.9; }
}
Standalone snippet · plain CSS, no build step, no dependencies.