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.

  • 82 lines of CSS
  • 21 lines of HTML
  • No JavaScript
  • No dependencies
  • MIT licensed

How it works

  1. The widest ring (the girdle) is 8 edges, 60px from the axis. Every facet is hinged on one of those edges: rotateY(i × 45deg) translateZ(60px).
  2. Crown facets stand on the girdle and lean in 45°: they rise 24px while stepping 24px inward, so their top edge is 36/60 as long. That ratio is the trapezoid's clip-path: 20% to 80%.
  3. Pavilion facets hang from the girdle (transform-origin: top) and lean in by atan(60 / 72) ≈ 39.8°, so a 72px-deep point forms where the tips meet.
  4. 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 82 lines

.scene {
  perspective: 800px;
}

.gem {
  position: relative;
  width: 49.71px; /* one girdle edge = 2 × 60px × tan(180deg / 8) */
  height: 96px;   /* crown 24px + pavilion 72px */
  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: 72px;
  height: 33.94px; /* slant = √2 × 24px */
  transform-origin: 50% 100%;
  transform: rotateY(calc(var(--i) * 45deg)) translateZ(60px) 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% 1.5px no-repeat,
    linear-gradient(rgb(46 230 214 / 0.8), rgb(46 230 214 / 0.8)) bottom / 100% 1.5px 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: 24px;
  height: 93.72px; /* slant = √(72² + 60²) */
  transform-origin: 50% 0;
  transform: rotateY(calc(var(--i) * 45deg)) translateZ(60px) 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.2px), transparent calc(50% + 1.2px)) left / 50% 100% no-repeat,
    linear-gradient(to top left, transparent 50%, rgb(139 108 255 / 0.8) 50% calc(50% + 1.2px), transparent calc(50% + 1.2px)) 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, 36px from the axis */
.gem b {
  position: absolute;
  left: calc(50% - 36px);
  top: -36px;
  width: 72px;
  height: 72px;
  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; }
}

Sass source 119 lines

@use 'sass:math';

$n: 8;
$G: 60px; // girdle (widest ring) apothem
$T: 36px; // table (flat top) apothem
$hc: $G - $T; // crown height, equal to the step inwards, so crown facets lean exactly 45deg
$hp: 72px; // pavilion depth
$wg: 2 * $G * math.tan(math.div(180deg, $n)); // width of one girdle edge
$Lc: math.sqrt(2) * $hc; // slant length of a crown facet
$Lp: math.hypot($hp, $G); // slant length of a pavilion facet
$lean-p: math.atan(math.div($G, $hp)); // pavilion lean from vertical
$edge: color-mix(in srgb, var(--c) 80%, transparent);

.d-diamond {
  position: relative;
  width: $wg;
  height: $hc + $hp;
  transform-style: preserve-3d;
  animation: d-diamond-spin 14s linear infinite;

  i {
    --a: 20%;
    position: absolute;
    left: 0;
    width: $wg;

    &:nth-child(odd) {
      --a: 40%;
    }

    // the glint: a white streak on every facet, fading in and out on a delay that walks round the gem
    &::after {
      content: '';
      position: absolute;
      inset: 0;
      background: linear-gradient(115deg, transparent 30%, rgb(255 255 255 / 0.8) 50%, transparent 70%);
      opacity: 0;
      animation: d-diamond-glint 3.2s ease-in-out infinite;
      animation-delay: calc(var(--i) * -0.4s + var(--late, 0s));
    }
  }

  // crown: trapezoids standing on the girdle, hinged at their bottom edge, leaning 45deg inwards
  &__c {
    --c: var(--accent-2);
    bottom: $hp;
    height: $Lc;
    transform-origin: 50% 100%;
    transform: rotateY(calc(var(--i) * #{math.div(360deg, $n)})) translateZ($G) rotateX(45deg);
    // the top edge shrinks to T/G of the bottom edge
    clip-path: polygon(
      #{math.div(1 - math.div($T, $G), 2) * 100%} 0,
      #{math.div(1 + math.div($T, $G), 2) * 100%} 0,
      100% 100%,
      0 100%
    );
    background:
      linear-gradient($edge, $edge) top / 100% 1.5px no-repeat,
      linear-gradient($edge, $edge) bottom / 100% 1.5px no-repeat,
      linear-gradient(to top, color-mix(in srgb, var(--c) var(--a), transparent), color-mix(in srgb, var(--c) 12%, transparent));
  }

  // pavilion: long triangles hanging from the girdle, hinged at their top edge, meeting in a point
  &__p {
    --c: var(--accent);
    --late: -1.7s;
    top: $hc;
    height: $Lp;
    transform-origin: 50% 0;
    transform: rotateY(calc(var(--i) * #{math.div(360deg, $n)})) translateZ($G) rotateX(-$lean-p);
    clip-path: polygon(0 0, 100% 0, 50% 100%);
    // slanted edges are drawn with "to corner" gradients: their 50% line is the box diagonal
    background:
      linear-gradient(to top right, transparent 50%, $edge 50%, $edge calc(50% + 1.2px), transparent calc(50% + 1.2px)) left /
        50% 100% no-repeat,
      linear-gradient(to top left, transparent 50%, $edge 50%, $edge calc(50% + 1.2px), transparent calc(50% + 1.2px)) right /
        50% 100% no-repeat,
      linear-gradient(to bottom, color-mix(in srgb, var(--c) var(--a), transparent), color-mix(in srgb, var(--hot) 14%, transparent));
  }

  // table: an octagon laid flat on top
  b {
    position: absolute;
    left: calc(50% - #{$T});
    top: -$T;
    width: $T * 2;
    height: $T * 2;
    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,
      color-mix(in srgb, var(--accent-2) 55%, transparent),
      rgb(255 255 255 / 0.35) 50%,
      color-mix(in srgb, var(--accent-2) 30%, transparent)
    );
    transform: rotateX(90deg);
  }
}

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

  to {
    transform: rotateX(-18deg) rotateY(360deg);
  }
}

@keyframes d-diamond-glint {
  0%,
  55%,
  100% {
    opacity: 0;
  }

  78% {
    opacity: 0.9;
  }
}
Standalone snippet · plain CSS, no build step, no dependencies.