Motion Lab / FAQ / svg morph toggle / minimal
Doorgaans binnen tien werkdagen. Vaste sprints, geen ruis.
Vanaf 2.450 euro, alles inbegrepen. Hosting blijft gratis via Cloudflare.
Ja. Lichtgewicht editor, korte uitleg. Of laat ons het beheer doen.
Stuur logo, kleuren en fonts mee — wij vertalen het 1:1 naar code.
// Mechanisme: faq-svg-morph-toggle-minimal (techniek #33 — SVG morphing) // Per item morphen we het "+" path naar "×" via GSAP attr-tween (path-d interpolation). // Restrained ease 'power2.inOut', auto-loop opent items 1..4 met 1.4s tussenpoos. import gsap from 'https://esm.sh/[email protected]'; const PLUS = 'M12 5 L12 19 M5 12 L19 12'; const CROSS = 'M6 6 L18 18 M18 6 L6 18'; const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; const root = document.querySelector('.faq-morph-min'); if (root) { const items = Array.from(root.querySelectorAll('[data-faq]')); const setOpen = (item, open) => { const path = item.querySelector('.morph-icon'); const body = item.querySelector('.faq-body'); item.classList.toggle('is-open', open); if (reduce) { path.setAttribute('d', open ? CROSS : PLUS); body.style.height='auto'; body.style.opacity='1'; return; } gsap.to(path, { attr: { d: open ? CROSS : PLUS }, duration: 0.5, ease: 'power2.inOut' }); if (open) { const h = body.scrollHeight || 80; gsap.fromTo(body, { height: 0, opacity: 0 }, { height: h, opacity: 1, duration: 0.6, ease: 'power2.out', onComplete: () => { body.style.height = 'auto'; } }); } else { gsap.to(body, { height: 0, opacity: 0, duration: 0.4, ease: 'power2.in' }); } }; items.forEach((it) => it.querySelector('.faq-trigger').addEventListener('click', () => setOpen(it, !it.classList.contains('is-open')))); }
<!-- Skeleton: faq-svg-morph-toggle-minimal -->
<section class="faq-morph-min">
<h2>Vragen.</h2>
<ul class="faq-list">
<li data-faq class="faq-row">
<button class="faq-trigger" type="button">
<span class="faq-q">Vraag?</span>
<svg class="faq-icon" viewBox="0 0 24 24" width="20" height="20"><path class="morph-icon" d="M12 5 L12 19 M5 12 L19 12" stroke="currentColor" stroke-width="1" stroke-linecap="round" fill="none"/></svg>
</button>
<div class="faq-body"><p>Antwoord.</p></div>
</li>
<!-- 4 items totaal -->
</ul>
</section> /* Styling: faq-svg-morph-toggle-minimal */
:root {
--block-paper: #F4F1EB;
--block-ink: #0A0A0A;
--block-rule: rgba(10,10,10,.14);
}
.faq-morph-min { background: var(--block-paper); color: var(--block-ink); font-family: 'Inter', system-ui, sans-serif; font-weight: 300; padding: clamp(4rem,10vw,9rem) clamp(1.5rem,5vw,4rem); }
.faq-morph-min h2 { font-weight: 300; font-size: clamp(2rem,4vw,3rem); letter-spacing: -0.02em; margin: 0 0 4rem 0; }
.faq-morph-min .faq-list { list-style: none; margin: 0; padding: 0; max-width: 720px; }
.faq-morph-min .faq-row { border-top: 1px solid var(--block-rule); }
.faq-morph-min .faq-row:last-child { border-bottom: 1px solid var(--block-rule); }
.faq-morph-min .faq-trigger { width: 100%; appearance: none; background: transparent; border: 0; cursor: pointer; padding: 1.75rem 0; display: flex; justify-content: space-between; align-items: center; font: 300 1rem/1.4 'Inter', sans-serif; color: inherit; text-align: left; }
.faq-morph-min .faq-icon { flex: none; }
.faq-morph-min .faq-body { height: 0; opacity: 0; overflow: hidden; }
.faq-morph-min .faq-body p { margin: 0 0 1.75rem 0; font: 300 0.95rem/1.65 'Inter', sans-serif; color: rgba(10,10,10,.62); max-width: 56ch; }