Pure CSS Products & branding #loop #sass-loop #product #logo #brand

Extruded logo in pure CSS

One clip-path shape repeated in 14 layers, 2px apart. The stack reads as a solid block; back layers are darker and the front one carries a sweeping shine.

  • 65 lines of CSS
  • 7 lines of HTML
  • No JavaScript
  • No dependencies
  • MIT licensed

How it works

  1. The mark is one clip-path: polygon(evenodd, …). The path runs round the hexagon, jumps in and traces the triangle; with evenodd the area enclosed twice becomes a hole.
  2. Fourteen copies of that shape are stacked 2px apart with translateZ(calc((var(--i) - 6.5) * 2px)). Seen at an angle their edges overlap into a solid side, and the hole gets inner walls for free.
  3. Each layer lays a dark gradient over the colour, with an alpha that shrinks as --i grows: rgb(6 7 20 / calc(0.6 - var(--i) * 0.033)). The side fades from bright to dark like a lit extrusion.
  4. The front layer is the face. Its shine is a pseudo-element moved with translateX only; the layer’s clip-path clips it to the logo, so it never spills out.

Key techniques

  • clip-path polygon with a hole (evenodd)
  • translateZ per layer from --i
  • darkening by depth with calc() alpha
  • shine moved with transform only

Copy-paste code

HTML 7 lines

<div class="scene">
  <div class="logo">
    <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>
  </div>
</div>

CSS 65 lines

.scene {
  perspective: 800px;
}

.logo {
  position: relative;
  width: 116px;
  height: 116px;
  transform-style: preserve-3d;
  animation: sway 6s ease-in-out infinite alternate;
}

/* soft glow far behind the mark */
.logo::before {
  content: '';
  position: absolute;
  inset: -26px;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgb(139 108 255 / 0.38), transparent);
  transform: translateZ(-60px);
}

.logo > i {
  /* back layers darker */
  --shade: calc(0.6 - var(--i) * 0.033);
  position: absolute;
  inset: 0;
  /* hexagon, then a jump in to trace the "play" triangle: evenodd makes it a hole */
  clip-path: polygon(evenodd,
    50% 0, 93.3% 25%, 93.3% 75%, 50% 100%, 6.7% 75%, 6.7% 25%, 50% 0,
    39% 31%, 71% 50%, 39% 69%, 39% 31%);
  background:
    linear-gradient(rgb(6 7 20 / var(--shade)), rgb(6 7 20 / var(--shade))),
    linear-gradient(135deg, #8b6cff, #ff4d9d);
  /* 14 layers, 2px apart, centred on the middle */
  transform: translateZ(calc((var(--i) - 6.5) * 2px));
}

/* the front layer is the face */
.logo > i:last-child {
  background:
    radial-gradient(circle at 30% 18%, rgb(255 255 255 / 0.45), transparent 45%),
    linear-gradient(135deg, #a28aff, #ff4d9d 70%, #ffb547);
}

/* the shine: clipped by the layer's clip-path, moved with transform only */
.logo > i:last-child::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 35%, rgb(255 255 255 / 0.7) 50%, transparent 65%);
  transform: translateX(-100%);
  animation: shine 4s ease-in-out infinite;
}

@keyframes sway {
  from { transform: rotateX(-10deg) rotateY(-40deg); }
  to   { transform: rotateX(-4deg) rotateY(40deg); }
}

/* both ends are off the logo, so the jump back is never seen */
@keyframes shine {
  0%, 45%   { transform: translateX(-100%); }
  80%, 100% { transform: translateX(100%); }
}

Sass source 99 lines

$n: 14; // layers
$gap: 2px; // distance between layers: 14 × 2px ≈ 26px of extrusion
$s: 116px;

// Hexagon with a "play" triangle cut out of it. With `evenodd` the path runs round the outer
// hexagon, jumps in to trace the triangle, and every area enclosed twice becomes a hole.
$shape: polygon(
  evenodd,
  50% 0,
  93.3% 25%,
  93.3% 75%,
  50% 100%,
  6.7% 75%,
  6.7% 25%,
  50% 0,
  39% 31%,
  71% 50%,
  39% 69%,
  39% 31%
);

.d-logo3d {
  position: relative;
  width: $s;
  height: $s;
  transform-style: preserve-3d;
  // `alternate` + ease-in-out: seamless, and it lingers on the two angles that show the depth.
  animation: d-logo3d-sway 6s ease-in-out infinite alternate;

  // soft coloured glow far behind the mark
  &::before {
    content: '';
    position: absolute;
    inset: -26px;
    border-radius: 50%;
    background: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 38%, transparent), transparent);
    transform: translateZ(-60px);
  }

  // Sass only hands out the index; the depth and the shading are one calc() each.
  @for $i from 0 to $n {
    > i:nth-child(#{$i + 1}) {
      --i: #{$i};
    }
  }

  > i {
    // back layers are darker: the extrusion reads as a side lit from the front
    --shade: calc(0.6 - var(--i) * 0.033);
    position: absolute;
    inset: 0;
    clip-path: $shape;
    background:
      linear-gradient(rgb(6 7 20 / var(--shade)), rgb(6 7 20 / var(--shade))),
      linear-gradient(135deg, var(--accent), var(--hot));
    transform: translateZ(calc((var(--i) - #{($n - 1) * 0.5}) * #{$gap}));
  }

  // the front layer is the face: brighter, with a glassy top light and the moving shine
  > i:last-child {
    background:
      radial-gradient(circle at 30% 18%, rgb(255 255 255 / 0.45), transparent 45%),
      linear-gradient(135deg, color-mix(in srgb, var(--accent) 80%, #fff), var(--hot) 70%, var(--warm));

    // clip-path on the layer also clips this child, so the shine never leaves the logo
    &::after {
      content: '';
      position: absolute;
      inset: 0;
      background: linear-gradient(105deg, transparent 35%, rgb(255 255 255 / 0.7) 50%, transparent 65%);
      transform: translateX(-100%);
      animation: d-logo3d-shine 4s ease-in-out infinite;
    }
  }
}

@keyframes d-logo3d-sway {
  from {
    transform: rotateX(-10deg) rotateY(-40deg);
  }

  to {
    transform: rotateX(-4deg) rotateY(40deg);
  }
}

// Off to the left, wait, sweep, off to the right. Both ends are invisible, so the jump back
// is never seen.
@keyframes d-logo3d-shine {
  0%,
  45% {
    transform: translateX(-100%);
  }

  80%,
  100% {
    transform: translateX(100%);
  }
}
Standalone snippet · plain CSS, no build step, no dependencies.