Back to CSS 3D Neon Lattice CSS + JS
Share
HTML
<section class="cd-nln" aria-label="Neon Lattice 3D cube matrix demo">
  <div class="card">
    <div class="cd-nln-grid" aria-hidden="true"></div>
    <div class="cd-nln-vignette" aria-hidden="true"></div>
    <div class="cd-nln-scanline" aria-hidden="true"></div>

    <div class="matrix" data-cd-nln-matrix aria-hidden="true"></div>
  </div>
</section>
CSS
/* ─── 02 Neon Lattice — 5×5 spinning cube matrix ──────────── */
.cd-nln {
  --cd-nln-bg: #010208;

  position: relative;
  width: 100%;
  height: 560px;
  background: var(--cd-nln-bg);
  overflow: hidden;
  perspective: 1200px;
  box-sizing: border-box;
}

.cd-nln *,
.cd-nln *::before,
.cd-nln *::after { box-sizing: border-box; margin: 0; padding: 0; }

.cd-nln .card {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.cd-nln .cd-nln-grid {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(0,220,255,0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0,220,255,0.035) 1px, transparent 1px);
  background-size: 42px 42px;
  animation: cd-nln-grid-shift 30s linear infinite;
  pointer-events: none;
}
@keyframes cd-nln-grid-shift {
  from { background-position: 0 0; }
  to   { background-position: 42px 42px; }
}

.cd-nln .cd-nln-vignette {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 75% 75% at 50% 50%, transparent 35%, rgba(1,2,8,0.94) 100%);
  pointer-events: none;
}

.cd-nln .cd-nln-scanline {
  position: absolute;
  left: 0; right: 0;
  height: 2px;
  background: rgba(0, 220, 255, 0.06);
  animation: cd-nln-scan 6s linear infinite;
  pointer-events: none;
}
@keyframes cd-nln-scan {
  from { top: -2px; }
  to   { top: 100%; }
}

/* Matrix */
.cd-nln .matrix {
  display: grid;
  grid-template-columns: repeat(5, 72px);
  grid-template-rows: repeat(5, 72px);
  gap: 22px;
  transform-style: preserve-3d;
  transform: rotateX(22deg) rotateY(-18deg);
  transition: transform 0.5s cubic-bezier(0.23, 1, 0.32, 1);
  position: relative;
  z-index: 1;
}

.cd-nln .cube-wrap {
  width: 72px;
  height: 72px;
  perspective: 600px;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: cd-nln-glow-pulse var(--gp, 3s) ease-in-out infinite var(--gpd, 0s);
}
@keyframes cd-nln-glow-pulse {
  0%,100% { opacity: 0.85; }
  50%     { opacity: 1; }
}

.cd-nln .cube {
  width: 52px;
  height: 52px;
  position: relative;
  transform-style: preserve-3d;
  animation: cd-nln-spin var(--sd) linear infinite var(--dl);
}

.cd-nln .f {
  position: absolute;
  width: 52px;
  height: 52px;
  border: 1px solid rgba(255,255,255,0.04);
  backface-visibility: visible;
}
.cd-nln .f-front  { transform: translateZ(26px); }
.cd-nln .f-back   { transform: translateZ(-26px) rotateY(180deg); }
.cd-nln .f-left   { transform: translateX(-26px) rotateY(-90deg); }
.cd-nln .f-right  { transform: translateX(26px) rotateY(90deg); }
.cd-nln .f-top    { transform: translateY(-26px) rotateX(90deg); }
.cd-nln .f-bottom { transform: translateY(26px) rotateX(-90deg); }

/* Row 1 — Cyan */
.cd-nln .cube-wrap:nth-child(-n+5) .f-front  { background: rgba(0,18,38,0.95); }
.cd-nln .cube-wrap:nth-child(-n+5) .f-back   { background: rgba(0,12,28,0.95); }
.cd-nln .cube-wrap:nth-child(-n+5) .f-left   { background: rgba(0,10,24,0.92); }
.cd-nln .cube-wrap:nth-child(-n+5) .f-right  { background: rgba(0,14,30,0.92); }
.cd-nln .cube-wrap:nth-child(-n+5) .f-bottom { background: rgba(0,8,20,0.95); }
.cd-nln .cube-wrap:nth-child(-n+5) .f-top    { background: #00e8ff; box-shadow: 0 0 18px #00e8ff, 0 0 40px rgba(0,232,255,0.5); }
.cd-nln .cube-wrap:nth-child(-n+5) { filter: drop-shadow(0 0 8px rgba(0,232,255,0.35)); }

/* Row 2 — Magenta */
.cd-nln .cube-wrap:nth-child(n+6):nth-child(-n+10) .f-front  { background: rgba(28,0,38,0.95); }
.cd-nln .cube-wrap:nth-child(n+6):nth-child(-n+10) .f-back   { background: rgba(20,0,28,0.95); }
.cd-nln .cube-wrap:nth-child(n+6):nth-child(-n+10) .f-left   { background: rgba(16,0,24,0.92); }
.cd-nln .cube-wrap:nth-child(n+6):nth-child(-n+10) .f-right  { background: rgba(22,0,32,0.92); }
.cd-nln .cube-wrap:nth-child(n+6):nth-child(-n+10) .f-bottom { background: rgba(12,0,18,0.95); }
.cd-nln .cube-wrap:nth-child(n+6):nth-child(-n+10) .f-top    { background: #ff00ff; box-shadow: 0 0 18px #ff00ff, 0 0 40px rgba(255,0,255,0.5); }
.cd-nln .cube-wrap:nth-child(n+6):nth-child(-n+10) { filter: drop-shadow(0 0 8px rgba(255,0,255,0.35)); }

/* Row 3 — Lime */
.cd-nln .cube-wrap:nth-child(n+11):nth-child(-n+15) .f-front  { background: rgba(0,22,8,0.95); }
.cd-nln .cube-wrap:nth-child(n+11):nth-child(-n+15) .f-back   { background: rgba(0,15,5,0.95); }
.cd-nln .cube-wrap:nth-child(n+11):nth-child(-n+15) .f-left   { background: rgba(0,12,4,0.92); }
.cd-nln .cube-wrap:nth-child(n+11):nth-child(-n+15) .f-right  { background: rgba(0,18,6,0.92); }
.cd-nln .cube-wrap:nth-child(n+11):nth-child(-n+15) .f-bottom { background: rgba(0,10,3,0.95); }
.cd-nln .cube-wrap:nth-child(n+11):nth-child(-n+15) .f-top    { background: #00ff66; box-shadow: 0 0 18px #00ff66, 0 0 40px rgba(0,255,102,0.5); }
.cd-nln .cube-wrap:nth-child(n+11):nth-child(-n+15) { filter: drop-shadow(0 0 8px rgba(0,255,102,0.35)); }

/* Row 4 — Amber */
.cd-nln .cube-wrap:nth-child(n+16):nth-child(-n+20) .f-front  { background: rgba(30,14,0,0.95); }
.cd-nln .cube-wrap:nth-child(n+16):nth-child(-n+20) .f-back   { background: rgba(22,10,0,0.95); }
.cd-nln .cube-wrap:nth-child(n+16):nth-child(-n+20) .f-left   { background: rgba(18,8,0,0.92); }
.cd-nln .cube-wrap:nth-child(n+16):nth-child(-n+20) .f-right  { background: rgba(24,12,0,0.92); }
.cd-nln .cube-wrap:nth-child(n+16):nth-child(-n+20) .f-bottom { background: rgba(14,6,0,0.95); }
.cd-nln .cube-wrap:nth-child(n+16):nth-child(-n+20) .f-top    { background: #ffcc00; box-shadow: 0 0 18px #ffcc00, 0 0 40px rgba(255,204,0,0.5); }
.cd-nln .cube-wrap:nth-child(n+16):nth-child(-n+20) { filter: drop-shadow(0 0 8px rgba(255,204,0,0.35)); }

/* Row 5 — Rose */
.cd-nln .cube-wrap:nth-child(n+21) .f-front  { background: rgba(32,0,12,0.95); }
.cd-nln .cube-wrap:nth-child(n+21) .f-back   { background: rgba(22,0,8,0.95); }
.cd-nln .cube-wrap:nth-child(n+21) .f-left   { background: rgba(18,0,6,0.92); }
.cd-nln .cube-wrap:nth-child(n+21) .f-right  { background: rgba(26,0,10,0.92); }
.cd-nln .cube-wrap:nth-child(n+21) .f-bottom { background: rgba(12,0,4,0.95); }
.cd-nln .cube-wrap:nth-child(n+21) .f-top    { background: #ff3080; box-shadow: 0 0 18px #ff3080, 0 0 40px rgba(255,48,128,0.5); }
.cd-nln .cube-wrap:nth-child(n+21) { filter: drop-shadow(0 0 8px rgba(255,48,128,0.35)); }

@keyframes cd-nln-spin {
  from { transform: rotateX(var(--rx-start,0deg)) rotateY(var(--ry-start,0deg)) rotateZ(0deg); }
  to   { transform: rotateX(var(--rx-end,360deg)) rotateY(var(--ry-end,360deg)) rotateZ(var(--rz-end,0deg)); }
}

@media (max-width: 720px) {
  .cd-nln .matrix {
    grid-template-columns: repeat(5, 52px);
    grid-template-rows: repeat(5, 52px);
    gap: 16px;
  }
  .cd-nln .cube-wrap { width: 52px; height: 52px; }
  .cd-nln .cube, .cd-nln .f { width: 40px; height: 40px; }
  .cd-nln .f-front  { transform: translateZ(20px); }
  .cd-nln .f-back   { transform: translateZ(-20px) rotateY(180deg); }
  .cd-nln .f-left   { transform: translateX(-20px) rotateY(-90deg); }
  .cd-nln .f-right  { transform: translateX(20px) rotateY(90deg); }
  .cd-nln .f-top    { transform: translateY(-20px) rotateX(90deg); }
  .cd-nln .f-bottom { transform: translateY(20px) rotateX(-90deg); }
}

@media (prefers-reduced-motion: reduce) {
  .cd-nln .cd-nln-grid,
  .cd-nln .cd-nln-scanline,
  .cd-nln .cube-wrap,
  .cd-nln .cube { animation: none !important; }
}
JS
(() => {
  const root = document.querySelector('.cd-nln');
  if (!root) return;
  const matrix = root.querySelector('[data-cd-nln-matrix]');
  if (!matrix) return;

  // Build cubes
  const axes = [
    [360, 360, 0], [360, 0, 0], [0, 360, 0],
    [0, 0, 360], [360, 360, 360], [-360, 360, 0], [360, -360, 0],
  ];
  for (let i = 0; i < 25; i++) {
    const ax = axes[Math.floor(Math.random() * axes.length)];
    const sd = (Math.random() * 8 + 3).toFixed(1) + 's';
    const dl = (-Math.random() * 12).toFixed(2) + 's';
    const gp = (Math.random() * 3 + 2).toFixed(1) + 's';
    const gpd = (-Math.random() * 5).toFixed(2) + 's';
    const rxStart = Math.random() * 360;
    const ryStart = Math.random() * 360;

    const wrap = document.createElement('div');
    wrap.className = 'cube-wrap';
    wrap.style.cssText = `--gp:${gp}; --gpd:${gpd};`;

    const cube = document.createElement('div');
    cube.className = 'cube';
    cube.style.cssText = `
      --sd:${sd}; --dl:${dl};
      --rx-start:${rxStart}deg; --ry-start:${ryStart}deg;
      --rx-end:${rxStart + ax[0]}deg; --ry-end:${ryStart + ax[1]}deg; --rz-end:${ax[2]}deg;
    `;

    cube.innerHTML = '<div class="f f-front"></div><div class="f f-back"></div><div class="f f-left"></div><div class="f f-right"></div><div class="f f-top"></div><div class="f f-bottom"></div>';

    wrap.appendChild(cube);
    matrix.appendChild(wrap);
  }

  // Mouse parallax scoped to wrapper
  const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  if (prefersReduced) return;

  let tx = 22, ty = -18, cx = 22, cy = -18, rafId = null;

  root.addEventListener('mousemove', e => {
    const rect = root.getBoundingClientRect();
    tx = 22 + ((e.clientY - rect.top) / rect.height - 0.5) * 25;
    ty = -18 + ((e.clientX - rect.left) / rect.width - 0.5) * 30;
    if (!rafId) rafId = requestAnimationFrame(animate);
  });
  root.addEventListener('mouseleave', () => { tx = 22; ty = -18; });

  function animate() {
    cx += (tx - cx) * 0.055;
    cy += (ty - cy) * 0.055;
    matrix.style.transform = `rotateX(${cx}deg) rotateY(${cy}deg)`;
    if (Math.abs(tx - cx) > 0.05 || Math.abs(ty - cy) > 0.05) {
      rafId = requestAnimationFrame(animate);
    } else {
      rafId = null;
    }
  }
})();