1. Mechanisme — kopieer 1-op-1, geen styling-keuzes
// Mechanisme: gallery-marquee-scrub-brutalist
// Scrub-tied marquee met capped velocity (ease-out coupling, scrub:1.5)
import gsap from "https://esm.sh/[email protected]";
import { ScrollTrigger } from "https://esm.sh/[email protected]/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (!reduce) {
document.querySelectorAll(".bm-track").forEach((track) => {
gsap.to(track, {
xPercent: -50,
ease: "none",
scrollTrigger: {
trigger: track.closest(".bm-stage"),
start: "top bottom",
end: "bottom top",
scrub: 1.5,
}
});
});
// Velocity → cap met ease-out (max boost 1.6x, smoothed)
const tracks = document.querySelectorAll(".bm-track");
ScrollTrigger.create({
onUpdate: (self) => {
const v = Math.min(Math.abs(self.getVelocity()) / 2000, 1);
const eased = 1 + (1 - Math.pow(1 - v, 3)) * 0.6; // ease-out cubic, cap 1.6
tracks.forEach((t) => gsap.to(t, { timeScale: eased, duration: .4, overwrite: "auto" }));
}
});
} 2. Skeleton — DOM + class-namen, mag herschikken
<!-- Skeleton: gallery-marquee-scrub-brutalist -->
<section class="bm-stage">
<header class="bm-head">
<span class="bm-tag">// MARQUEE_SCRUB · v1</span>
<span class="bm-tag">VELOCITY_CAPPED</span>
</header>
<div class="bm-strip">
<div class="bm-track">
<span>STRATEGIE</span><span class="bm-dot">●</span>
<span>ONTWERP</span><span class="bm-dot">●</span>
<span class="bm-hot">MOTION</span><span class="bm-dot">●</span>
<span>GROWTH</span><span class="bm-dot">●</span>
<span>BRANDING</span><span class="bm-dot">●</span>
<!-- duplicate for seamless loop -->
<span>STRATEGIE</span><span class="bm-dot">●</span>
<span>ONTWERP</span><span class="bm-dot">●</span>
<span class="bm-hot">MOTION</span><span class="bm-dot">●</span>
<span>GROWTH</span><span class="bm-dot">●</span>
<span>BRANDING</span><span class="bm-dot">●</span>
</div>
</div>
</section> 3. Styling-template — verplicht eigen invulling per merk
/* Styling: gallery-marquee-scrub-brutalist — 2px borders, mono caps, hot-spot */
.bm-stage{
--bg:#F4F1EB; --ink:#0A0A0A; --hot:#FF4A1C;
background:var(--bg); color:var(--ink);
padding:clamp(2rem,5vw,4rem) 0;
border-top:2px solid var(--ink); border-bottom:2px solid var(--ink);
}
.bm-head{ display:flex; justify-content:space-between; padding:0 clamp(1.5rem,4vw,3rem) 1.5rem;
font-family:"JetBrains Mono",monospace; font-size:.72rem; letter-spacing:.2em; text-transform:uppercase; }
.bm-strip{ overflow:hidden; border-top:2px solid var(--ink); border-bottom:2px solid var(--ink); padding:1.2rem 0; }
.bm-track{ display:flex; gap:2.5rem; white-space:nowrap; will-change:transform;
font-family:"JetBrains Mono",monospace; font-weight:500;
font-size:clamp(2rem,6vw,4.5rem); letter-spacing:.04em; text-transform:uppercase; }
.bm-track .bm-dot{ color:var(--ink); align-self:center; font-size:.6em; }
.bm-track .bm-hot{ color:var(--hot); }