← Blurr Motion faq-popover-anchor-minimal
Categorie faq Tier 3 Techniek #41 Deps gsap

Motion Lab / FAQ / popover anchor / minimal

Vragen, kort beantwoord.

Gemiddeld 2 weken van kick-off tot live. Strakke milestones, geen vertraging.
Vanaf 2.450 euro, eenmalig of in twee termijnen. Geen verborgen kosten.
Cloudflare Pages of Vercel. Gratis hosting, instant cache, globale CDN.
Optioneel. Maandelijks pakket of pay-per-change. Jij kiest.
Volledig van jou. Repo, domein, content. Wij beheren als je het wilt.
Werkdagen 9-17. WhatsApp voor urgente issues. Reactie binnen 4 uur.
1. Mechanisme — kopieer 1-op-1, geen styling-keuzes
// Mechanisme: faq-popover-anchor-minimal (techniek #41 — Anchor positioning + Popover API)
// Native [popover] + CSS anchor-name / position-anchor. Auto-loop opent elke 3s een volgende anchor.
import gsap from 'https://esm.sh/[email protected]';
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const root = document.querySelector('.faq-pa-min');
if (root) {
  const triggers = Array.from(root.querySelectorAll('[data-pop-trigger]'));
  const popoverSupport = 'popover' in HTMLElement.prototype;
  const closeAll = () => {
    triggers.forEach((b) => b.classList.remove('is-active'));
    root.querySelectorAll('.faq-pop').forEach((p) => {
      try { if (popoverSupport && p.matches(':popover-open')) p.hidePopover(); } catch (_) {}
      p.classList.remove('is-open');
    });
  };
  const open = (btn) => {
    if (!btn) return;
    const id = btn.getAttribute('data-pop-target');
    const target = document.getElementById(id);
    if (!target) return;
    closeAll();
    btn.classList.add('is-active');
    try {
      if (popoverSupport && target.showPopover) target.showPopover();
      else target.classList.add('is-open');
    } catch (_) { target.classList.add('is-open'); }
    if (!reduce) gsap.fromTo(target, { y: -4, autoAlpha: 0 }, { y: 0, autoAlpha: 1, duration: 0.3, ease: 'power2.out' });
  };
  if (!reduce) {
    let i = 0;
    const tick = () => { open(triggers[i]); setTimeout(closeAll, 2200); i = (i + 1) % triggers.length; };
    setTimeout(tick, 500);
    setInterval(tick, 3000);
  }
}
2. Skeleton — DOM + class-namen, mag herschikken
<!-- Skeleton: faq-popover-anchor-minimal -->
<section class="faq-pa-min">
  <h2>Vragen, kort beantwoord.</h2>
  <div class="anchor-row">
    <button data-pop-trigger data-pop-target="popm-0" popovertarget="popm-0" class="pop-anchor-0">01 Snelheid</button>
    <button data-pop-trigger data-pop-target="popm-1" popovertarget="popm-1" class="pop-anchor-1">02 Prijs</button>
    <!-- 4-6 anchor-knoppen -->
  </div>
  <div id="popm-0" popover class="faq-pop">Antwoord-tekst.</div>
  <div id="popm-1" popover class="faq-pop">Antwoord-tekst.</div>
</section>
3. Styling-template — verplicht eigen invulling per merk
/* Styling: faq-popover-anchor-minimal */
:root {
  --block-bg: #F4F1EB;
  --block-fg: #0A0A0A;
  --block-rule: rgba(10,10,10,.14);
}
.faq-pa-min { background: var(--block-bg); color: var(--block-fg); font-family: 'Inter', system-ui, sans-serif; padding: clamp(4rem,10vw,9rem) clamp(2rem,6vw,5rem); }
.faq-pa-min h2 { font-weight: 300; font-size: clamp(2rem,4vw,3rem); letter-spacing: -0.02em; margin: 0 0 4rem 0; }
.faq-pa-min .anchor-row { display: flex; flex-wrap: wrap; gap: 0; border-top: 1px solid var(--block-rule); }
.faq-pa-min [data-pop-trigger] {
  appearance: none; background: transparent; border: 0; border-bottom: 1px solid var(--block-rule);
  padding: 1.25rem 1.5rem 1.25rem 0; font: 400 0.95rem/1 'Inter', sans-serif;
  color: var(--block-fg); cursor: pointer; flex: 1 1 auto; text-align: left;
  transition: color .2s ease;
}
.faq-pa-min [data-pop-trigger]:hover, .faq-pa-min [data-pop-trigger].is-active { color: rgba(10,10,10,.55); }
.faq-pa-min .pop-anchor-0 { anchor-name: --pm0; }
.faq-pa-min .pop-anchor-1 { anchor-name: --pm1; }
.faq-pa-min .pop-anchor-2 { anchor-name: --pm2; }
.faq-pa-min .pop-anchor-3 { anchor-name: --pm3; }
.faq-pa-min .pop-anchor-4 { anchor-name: --pm4; }
.faq-pa-min .pop-anchor-5 { anchor-name: --pm5; }
.faq-pa-min .faq-pop {
  margin: 0; padding: 1.25rem 1.5rem; max-width: 320px; border: 1px solid var(--block-fg);
  background: var(--block-bg); color: var(--block-fg); border-radius: 0;
  font: 400 0.9rem/1.55 'Inter', sans-serif;
}
.faq-pa-min #popm-0 { position-anchor: --pm0; }
.faq-pa-min #popm-1 { position-anchor: --pm1; }
.faq-pa-min #popm-2 { position-anchor: --pm2; }
.faq-pa-min #popm-3 { position-anchor: --pm3; }
.faq-pa-min #popm-4 { position-anchor: --pm4; }
.faq-pa-min #popm-5 { position-anchor: --pm5; }
.faq-pa-min .faq-pop { top: anchor(bottom); left: anchor(left); margin-top: 0.5rem; }
@media (prefers-reduced-motion: reduce) { .faq-pa-min .faq-pop { transition: none; } }