Jaartal loopt mee met de scroll New
A large year that scrubs with your scroll from 1958 to now, like a time machine: at each milestone year the story below changes and the timeline fills. For the about or anniversary page of a family business, bakery or installer. Own scroll box with keyboard support; screen readers hear only the milestones, not every year.
1958: Opa Kees opent de bakkerij
Aan de Lange Tiendeweg in Gouda, met één oven en één knecht.
How to use it
- Paste the HTML where the scroll effects should appear.
- Add the CSS to your stylesheet. All classes are prefixed, so nothing clashes with your own styles.
- Add the JavaScript and call init() with the element that contains the HTML.
<!-- Jaartal loopt mee met de scroll · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie -->
<div class="jouw-container">
<div class="jt">
<div class="jt__vak" tabindex="0" role="region" aria-label="Geschiedenis van Bakkerij Verhoeven in Gouda, van 1958 tot nu. Scroll in dit vak.">
<div class="jt__baan">
<div class="jt__beeld">
<div class="jt__jaar" aria-hidden="true">1958</div>
<div class="jt__mijl" aria-live="polite"><p class="jt__titel"><span class="jt__sr">1958: </span>Opa Kees opent de bakkerij</p><p class="jt__tekst">Aan de Lange Tiendeweg in Gouda, met één oven en één knecht.</p></div>
<div class="jt__lijn" aria-hidden="true"><i class="jt__vul"></i></div>
</div>
</div>
</div>
<p class="jt__hint" aria-hidden="true">Scroll door de jaren</p>
</div>
</div>/* Jaartal loopt mee met de scroll · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie */
.jt{position:absolute;inset:0;container-type:size;background:#0a0a0a;color:#fff;font-family:'Switzer',system-ui,sans-serif}
.jt__vak{position:absolute;inset:0;overflow-y:auto;scrollbar-width:none}
.jt__vak::-webkit-scrollbar{display:none}
.jt__vak:focus-visible{outline:2px solid #F5480F;outline-offset:-2px}
.jt__baan{height:600cqh}
.jt__beeld{position:sticky;top:0;height:100cqh;display:flex;flex-direction:column;justify-content:center;padding:0 7cqw;box-sizing:border-box}
.jt__jaar{font-size:clamp(48px,22cqw,96px);font-weight:700;line-height:.9;letter-spacing:-.02em;font-variant-numeric:tabular-nums;color:#F5480F}
.jt__mijl{margin-top:10px;min-height:52px;transition:opacity .25s}
.jt__mijl.is-wissel{opacity:0}
.jt__titel{font-size:clamp(14px,4.2cqw,18px);font-weight:600;margin:0 0 3px}
.jt__tekst{font-size:clamp(12px,3.4cqw,14px);line-height:1.4;margin:0;color:#cfcfcf;max-width:40ch}
.jt__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.jt__lijn{position:relative;height:2px;background:rgba(255,255,255,.2);margin-top:14px}
.jt__vul{position:absolute;left:0;top:0;height:100%;width:100%;background:#F5480F;transform-origin:left;transform:scaleX(0)}
.jt__tik{position:absolute;top:-4px;width:2px;height:10px;background:rgba(255,255,255,.45)}
.jt__tik.is-aan{background:#F5480F}
.jt__hint{position:absolute;right:10px;bottom:8px;margin:0;font-size:12px;background:#fff;color:#0a0a0a;padding:3px 8px;border-radius:4px;pointer-events:none;transition:opacity .3s}
.jt.is-weg .jt__hint{opacity:0}
@media (prefers-reduced-motion:reduce){.jt__mijl,.jt__hint{transition:none}}// Jaartal loopt mee met de scroll · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie
// root = het element waarin de HTML staat
function init(root){
const w = root.querySelector('.jt'); if (!w) return;
const vak = w.querySelector('.jt__vak'), jaar = w.querySelector('.jt__jaar'), mijl = w.querySelector('.jt__mijl');
const titel = w.querySelector('.jt__titel'), tekst = w.querySelector('.jt__tekst'), lijn = w.querySelector('.jt__lijn'), vul = w.querySelector('.jt__vul');
const rm = matchMedia('(prefers-reduced-motion: reduce)').matches;
const A = 1958, B = 2026;
const M = [[1958, 'Opa Kees opent de bakkerij', 'Aan de Lange Tiendeweg in Gouda, met één oven en één knecht.'], [1972, 'De eerste stroopwafels', 'Met de hand gebakken en op zaterdag verkocht op de kaasmarkt.'], [1989, 'Tweede winkel in Waddinxveen', 'Oma Riet zet daar de eerste koffiehoek neer.'], [2004, 'Bart neemt het over', 'De kleinzoon van Kees, bakker sinds zijn zestiende.'], [2017, 'Oven op groene stroom', 'Het gas eruit, zonnepanelen op het dak van de bakkerij.'], [2026, 'Nu: 14 mensen en 2 winkels', 'Elke ochtend om vier uur gaat het licht nog steeds aan.']];
const tikken = M.map(m => { const t = document.createElement('span'); t.className = 'jt__tik'; t.style.left = ((m[0] - A) / (B - A) * 100) + '%'; lijn.appendChild(t); return t; });
let raf = 0, nu = 0, timer = 0;
function zetMijl(i) {
const doe = () => { titel.innerHTML = '<span class="jt__sr">' + M[i][0] + ': </span>' + M[i][1]; tekst.textContent = M[i][2]; mijl.classList.remove('is-wissel'); };
clearTimeout(timer);
if (rm) doe(); else { mijl.classList.add('is-wissel'); timer = setTimeout(doe, 180); }
}
function teken() {
raf = 0; const m = vak.scrollHeight - vak.clientHeight, p = m > 0 ? vak.scrollTop / m : 0;
const j = Math.round(A + p * (B - A)); jaar.textContent = j;
vul.style.transform = 'scaleX(' + p.toFixed(3) + ')';
let i = 0; M.forEach((x, k) => { if (j >= x[0]) i = k; });
tikken.forEach((t, k) => t.classList.toggle('is-aan', k <= i));
if (i !== nu) { nu = i; zetMijl(i); }
w.classList.toggle('is-weg', vak.scrollTop > 8);
}
vak.addEventListener('scroll', () => { if (!raf) raf = requestAnimationFrame(teken); }, { passive: true });
teken();
}
init(document.querySelector('.jouw-container'));
More scroll effects
Seen something you like?
Add it to your selection and send it over. We build your site around it, in your brand.
Get in touch →