CSS & front-end
Scroll-driven animations in CSS: parallax zonder JavaScript
Met animation-timeline koppel je een animatie direct aan de scrollpositie. Geen scroll-listeners, geen libraries, en de browser doet het zware werk.
In het kort
- Scroll-driven animations koppelen een CSS-animatie aan de scrollpositie in plaats van aan de tijd.
- scroll() volgt de scrollpositie van een container of pagina, view() volgt een element dat door het beeld beweegt.
- Zet animation-timeline na de animation-shorthand, omdat de shorthand de tijdlijn anders terugzet.
- Animeer alleen transform en opacity voor soepele scroll-effecten.
- Gebruik @supports en prefers-reduced-motion voor een veilige fallback en toegankelijkheid.
Met scroll-driven animations in CSS koppel je een animatie aan de scrollpositie in plaats van aan de tijd. Je gebruikt gewone @keyframes en zet daarna animation-timeline: scroll() of animation-timeline: view() op het element. Zo bouw je parallax, voortgangsbalken en reveal-effecten zonder één regel JavaScript.
Dat is meer dan een leuke truc. Scroll-effecten met JavaScript luisteren naar scroll-events en rekenen op de hoofdthread, wat bij zware pagina’s tot haperingen leidt. Met CSS laat je de browser het werk doen, en die kan veel van deze animaties buiten de hoofdthread afhandelen. In dit artikel lees je hoe het werkt, met voorbeelden die je direct kunt gebruiken.
Hoe scroll-driven animations in CSS werken
Een normale CSS-animatie loopt over een tijdlijn in seconden. Bij scroll-driven animations vervang je die tijdlijn door een scrolltijdlijn. Er zijn twee soorten:
| Tijdlijn | Functie | Voortgang is gekoppeld aan | Typisch gebruik |
|---|---|---|---|
| Scroll progress timeline | scroll() |
Hoe ver een scrollcontainer of de pagina is gescrold | Leesvoortgangsbalk, header die verandert |
| View progress timeline | view() |
Hoe ver een element door het zichtbare deel beweegt | Fade-in bij binnenkomen, parallax per sectie |
Bij 0% van de scrolltijdlijn staat de animatie aan het begin, bij 100% aan het eind. Scrol je terug, dan loopt de animatie mee terug. Dat maakt het gedrag voorspelbaar en goed te testen.
Voorbeeld 1: een leesvoortgangsbalk
Het eenvoudigste voorbeeld: een balk bovenin die meegroeit terwijl je door een artikel scrolt.
.voortgang {
position: fixed;
inset: 0 0 auto 0;
height: 4px;
background: currentColor;
transform-origin: 0 50%;
animation: groei linear both;
animation-timeline: scroll(root block);
}
@keyframes groei {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
scroll(root block) betekent: gebruik de scrollpositie van het hele document, in de blokrichting (verticaal in een gewone layout). Let op de volgorde: zet animation-timeline na de animation-shorthand. De shorthand zet de tijdlijn namelijk terug naar de standaardwaarde.
Voorbeeld 2: elementen laten verschijnen met view()
Een klassiek effect: blokken die infaden en een stukje omhoog schuiven zodra ze in beeld komen. Vroeger deed je dat met een Intersection Observer en een class. Nu is het puur CSS:
.reveal {
animation: verschijn linear both;
animation-timeline: view();
animation-range: entry 0% cover 30%;
}
@keyframes verschijn {
from { opacity: 0; transform: translateY(2rem); }
to { opacity: 1; transform: none; }
}
Met animation-range bepaal je welk deel van de tijdlijn de animatie gebruikt. entry 0% is het moment dat het element net de onderkant van het scherm raakt, cover 30% is wanneer het een stuk verder in beeld is. Daarna blijft het element in de eindstand staan door both.
De belangrijkste ranges op een rij:
- entry: het element komt het scherm binnen;
- exit: het element verlaat het scherm;
- contain: het element is volledig zichtbaar, of vult het scherm volledig;
- cover: de hele reis, van eerste tot laatste zichtbare pixel.
Voorbeeld 3: parallax zonder JavaScript
Parallax betekent dat lagen met verschillende snelheden bewegen. Met view() laat je een achtergrondafbeelding binnen een sectie langzamer bewegen dan de content:
.parallax {
overflow: clip;
}
.parallax__beeld {
animation: parallax linear both;
animation-timeline: view();
animation-range: cover;
}
@keyframes parallax {
from { transform: translateY(-15%) scale(1.2); }
to { transform: translateY(15%) scale(1.2); }
}
De scale(1.2) zorgt dat er geen lege randen zichtbaar worden terwijl het beeld verschuift. Omdat je alleen transform animeert, blijft het effect soepel: de browser hoeft niets opnieuw in te delen.
Kleine bewegingen bij binnenkomen sluiten mooi aan op wat we in microinteracties: kleine animaties met grote impact beschrijven. Meer kant-en-klare voorbeelden vind je in ons CSS-lab met gratis snippets.
Browserondersteuning, fallback en toegankelijkheid
Scroll-driven animations worden ondersteund in Chromium-browsers zoals Chrome en Edge, en steeds meer andere browsers volgen. Controleer de actuele status op MDN voordat je erop bouwt. Het goede nieuws: dit is een typisch geval van progressive enhancement. Zonder ondersteuning blijft de content gewoon staan, alleen zonder animatie. Zorg daarom dat de eindstand ook de standaardstand is.
@supports (animation-timeline: view()) {
.reveal {
animation: verschijn linear both;
animation-timeline: view();
animation-range: entry 0% cover 30%;
}
}
@media (prefers-reduced-motion: reduce) {
.reveal,
.parallax__beeld {
animation: none;
}
}
Door de animatie in @supports te zetten, voorkom je dat browsers zonder ondersteuning een gewone, tijdgebaseerde animatie afspelen of elementen onzichtbaar laten. En respecteer altijd prefers-reduced-motion: voor sommige mensen veroorzaakt beweging bij scrollen echte klachten.
Let op prestaties
Animeer bij voorkeur alleen transform en opacity. Eigenschappen als width, top of margin dwingen de browser om de layout opnieuw te berekenen, en dan verlies je het voordeel. Overdrijf het ook niet: tien parallaxlagen op één pagina zijn zelden mooi en kosten op goedkopere telefoons merkbaar rekenkracht. Hoe zwaar scripts en animaties op de responsiviteit drukken, lees je in INP verbeteren.
Wanneer je scroll-effecten wel en niet inzet
Dat iets kan, betekent niet dat het overal moet. Scroll-animaties werken het best als ze de lezer helpen om de pagina te begrijpen. Denk aan een voortgangsbalk bij een lang artikel, een productafbeelding die stap voor stap onderdelen toont terwijl je scrolt, of een tijdlijn die zich opbouwt naarmate je verder leest. Daar ondersteunt de beweging het verhaal.
Minder geschikt zijn effecten die content vertragen of verbergen. Een tekstblok dat pas leesbaar wordt als je een bepaald punt bereikt, frustreert snelle lezers. Effecten die belangrijke knoppen laten verschuiven, maken een pagina onrustig. En op pagina’s waar bezoekers vooral iets willen regelen, zoals een contactformulier of een checkout, voegt scrollbeweging zelden iets toe.
Een paar vuistregels die wij aanhouden:
- Belangrijke tekst is altijd leesbaar, ook halverwege een animatie.
- Beweging is subtiel: een paar rem verschuiving is vaak genoeg.
- Elk effect heeft een functie: richting geven, samenhang tonen of voortgang laten zien.
- Knoppen en formulieren bewegen niet mee.
- Het ontwerp werkt ook als je alle animaties uitzet.
Daarnaast zijn scroll-driven animations goed te combineren met andere moderne CSS. Een kaartcomponent die zijn layout aanpast aan de beschikbare ruimte met container queries kan bijvoorbeeld prima een eigen view()-animatie krijgen, zonder dat de component weet waar hij op de pagina staat.
Zo pak je het aan
- Begin met de eindstand. Ontwerp de pagina zo dat alles zonder animatie klopt.
- Kies de juiste tijdlijn:
scroll()voor effecten die aan de hele pagina hangen,view()voor effecten per element. - Schrijf gewone keyframes met alleen
transformenopacity. - Zet de tijdlijn na de shorthand en stel met
animation-rangehet juiste moment in. - Verpak het in
@supportsen zet eenprefers-reduced-motion-uitzondering. - Test op een echte telefoon, niet alleen op je snelle laptop, en controleer of het effect iets toevoegt aan het verhaal van de pagina.
Conclusie
Scroll-driven animations in CSS maken een hoop JavaScript overbodig. Met animation-timeline, scroll(), view() en animation-range bouw je voortgangsbalken, reveals en parallax die soepel lopen en netjes terugvallen in browsers zonder ondersteuning. Gebruik het doelbewust: een paar goed getimede effecten maken een pagina levendig, te veel beweging leidt af.
Wil je een site waarin beweging de boodschap versterkt in plaats van afleidt? Bekijk wat we doen met interactief webdesign en animaties.
Veelgestelde vragen
Werken scroll-driven animations in alle browsers?
Niet overal. Chromium-browsers ondersteunen ze, andere browsers volgen. Gebruik @supports zodat de pagina zonder ondersteuning gewoon werkt.
Wat is het verschil tussen scroll() en view()?
scroll() koppelt de animatie aan hoe ver een container of de hele pagina is gescrold. view() koppelt hem aan de positie van een element in het zichtbare deel van het scherm.
Is parallax met CSS beter voor de snelheid dan met JavaScript?
Meestal wel, omdat de browser transform-animaties efficiënt kan afhandelen zonder scroll-listeners op de hoofdthread.
Moet ik rekening houden met bezoekers die beweging willen beperken?
Ja. Zet animaties uit binnen een prefers-reduced-motion-mediaquery, want scrollbeweging kan voor sommige mensen klachten geven.
