← Blurr Motion cta-audio-cue-minimal
Categorie cta Tier 2 Techniek #32 Deps

Audio cue

luister


Een fluisterend signaal. Geen geluid voor de show.

1. Mechanisme — kopieer 1-op-1, geen styling-keuzes
// Mechanisme: cta-audio-cue-minimal
// Auto-loop ripple-rings vanuit het centrum van een play-button.
// Reduced-motion respecterend; geen stijlkeuzes, alleen mechaniek.
const root = document.querySelector('.audio-cue');
if (root && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
  const stage = root.querySelector('.cue-stage');
  let n = 0;
  const spawn = () => {
    const ring = document.createElement('span');
    ring.className = 'cue-ring';
    ring.style.setProperty('--i', String(n++));
    stage.appendChild(ring);
    setTimeout(() => ring.remove(), 2400);
  };
  spawn();
  setInterval(spawn, 3000);
  // Pulserende dots
  const dots = root.querySelectorAll('.cue-dot');
  let k = 0;
  setInterval(() => {
    dots.forEach((d, i) => d.classList.toggle('on', i === k % dots.length));
    k++;
  }, 280);
}
2. Skeleton — DOM + class-namen, mag herschikken
<!-- Skeleton: cta-audio-cue-minimal -->
<section class="audio-cue">
  <p class="cue-eyebrow">Audio cue</p>
  <div class="cue-stage">
    <button class="cue-button" type="button" aria-label="Speel audio voorbeeld">
      <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg>
    </button>
    <!-- ripple-rings worden via JS in .cue-stage gespawnd -->
  </div>
  <p class="cue-meta">
    <span class="cue-dot"></span>
    <span class="cue-dot"></span>
    <span class="cue-dot"></span>
    <span class="cue-label">luister</span>
  </p>
  <hr class="cue-rule" />
  <p class="cue-caption">Een fluisterend signaal. Geen geluid voor de show.</p>
</section>
3. Styling-template — verplicht eigen invulling per merk
/* Styling: cta-audio-cue-minimal — cream/ink, Inter 300, single rule */
.audio-cue {
  --paper: #F4F1EB;
  --ink: #0A0A0A;
  --rule: rgba(10,10,10,.18);
  background: var(--paper);
  color: var(--ink);
  font-family: 'Inter', system-ui, sans-serif;
  font-weight: 300;
  padding: clamp(3rem, 8vw, 7rem) clamp(2rem, 6vw, 6rem);
  min-height: 70vh;
  display: grid;
  gap: 2.5rem;
  align-content: center;
  justify-items: start;
  max-width: 64ch;
}
.cue-eyebrow {
  font-size: .72rem;
  letter-spacing: .28em;
  text-transform: uppercase;
  margin: 0;
  opacity: .55;
}
.cue-stage {
  position: relative;
  width: 96px;
  height: 96px;
}
.cue-button {
  position: relative;
  z-index: 2;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  border: 1px solid var(--ink);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: background .4s ease, color .4s ease;
}
.cue-button:hover { background: var(--ink); color: var(--paper); }
.cue-button svg { width: 26px; height: 26px; fill: currentColor; }
.cue-ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1px solid var(--ink);
  pointer-events: none;
  animation: cueRipple 2.4s ease-out forwards;
}
@keyframes cueRipple {
  0%   { transform: scale(1);   opacity: .4; }
  100% { transform: scale(2.5); opacity: 0;  }
}
.cue-meta {
  display: flex;
  align-items: center;
  gap: .55rem;
  font-size: .8rem;
  letter-spacing: .14em;
  text-transform: lowercase;
  margin: 0;
  opacity: .7;
}
.cue-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: rgba(10,10,10,.22);
  transition: background .2s ease;
}
.cue-dot.on { background: var(--ink); }
.cue-label { margin-left: .35rem; }
.cue-rule {
  border: 0;
  border-top: 1px solid var(--rule);
  width: 100%;
  margin: 0;
}
.cue-caption {
  font-size: 1rem;
  line-height: 1.5;
  margin: 0;
  max-width: 32ch;
  opacity: .75;
}
@media (prefers-reduced-motion: reduce) {
  .cue-ring { display: none; }
  .cue-dot.on { background: rgba(10,10,10,.22); }
}