Bevestig met aandacht
klaar bezig… bevestigd ✓
// Mechanisme: cta-hold-to-confirm-minimal
// Press-and-hold vult een 1px lijn van 0 -> 100% in 1.2s; auto-demo loop elke 4s.
const root = document.querySelector('.hold-block');
if (root && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
const btn = root.querySelector('.hold-btn');
const fill = root.querySelector('.hold-fill');
const status = root.querySelector('.hold-status');
const DURATION = 1200;
let raf, t0, holding = false, autoTimer, autoDemo = true;
const setProgress = (p) => {
fill.style.transform = 'scaleX(' + p + ')';
status.dataset.state = p >= 1 ? 'confirmed' : (p > 0 ? 'holding' : 'idle');
};
const tick = (ts) => {
if (!holding) return;
if (!t0) t0 = ts;
const p = Math.min(1, (ts - t0) / DURATION);
setProgress(p);
if (p >= 1) { holding = false; return; }
raf = requestAnimationFrame(tick);
};
const start = () => { holding = true; t0 = 0; cancelAnimationFrame(raf); raf = requestAnimationFrame(tick); };
const cancel = () => { if (holding) { holding = false; setProgress(0); } };
const stopAuto = () => { autoDemo = false; clearTimeout(autoTimer); };
btn.addEventListener('mousedown', () => { stopAuto(); start(); });
btn.addEventListener('touchstart', (e) => { e.preventDefault(); stopAuto(); start(); }, { passive: false });
['mouseup','mouseleave','touchend','touchcancel'].forEach(ev => btn.addEventListener(ev, cancel));
const autoLoop = () => {
if (!autoDemo) return;
start();
autoTimer = setTimeout(() => {
if (!autoDemo) return;
setTimeout(() => { setProgress(0); autoTimer = setTimeout(autoLoop, 2800); }, 900);
}, DURATION + 50);
};
setTimeout(autoLoop, 1500);
} <!-- Skeleton: cta-hold-to-confirm-minimal -->
<section class="hold-block">
<p class="hold-eyebrow">Bevestig met aandacht</p>
<h2 class="hold-headline">Houd ingedrukt om<br/>te bevestigen.</h2>
<button class="hold-btn" type="button">
<span class="hold-label">houd vast</span>
<span class="hold-track"><span class="hold-fill"></span></span>
</button>
<p class="hold-status" data-state="idle">
<span class="s-idle">klaar</span>
<span class="s-hold">bezig…</span>
<span class="s-ok">bevestigd</span>
</p>
</section> /* Styling: cta-hold-to-confirm-minimal — cream/ink, Inter 300, single 1px rule */
.hold-block {
--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;
display: grid;
gap: 2.25rem;
padding: clamp(3rem, 8vw, 6rem) 0;
max-width: 56ch;
}
.hold-headline { font-size: clamp(1.6rem, 3.2vw, 2.4rem); line-height: 1.2; margin: 0; font-weight: 300; letter-spacing: -.01em; }
.hold-btn {
background: transparent;
border: 0;
border-top: 1px solid var(--ink);
border-bottom: 1px solid var(--rule);
padding: 0;
cursor: pointer;
font-family: inherit;
font-weight: 300;
display: grid;
}
.hold-label { padding: 1.4rem 0; text-align: left; font-size: 1rem; letter-spacing: .04em; }
.hold-track { height: 1px; background: transparent; position: relative; }
.hold-fill { position: absolute; inset: 0; background: var(--ink); transform: scaleX(0); transform-origin: left; }
@media (prefers-reduced-motion: reduce) { .hold-fill { transform: scaleX(1); } }