More Scroll Snippets
Scroll Timeline Dots — Free GSAP ScrollTrigger Snippet
Scroll Timeline Dots · Scroll · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Scroll Timeline Dots — A Timeline That Fills as You Scroll

Scroll timeline dots is the animated vertical timeline where a progress line fills from top to bottom as you scroll, each milestone's dot lights up when you reach it, and the cards slide in beside them — the storytelling layout for company histories, roadmaps, and changelogs. This snippet builds it with GSAP and ScrollTrigger (from a CDN), combining a scrubbed fill with per-step toggles.
The filling spine
A track runs down the left edge with a gradient fill inside it. One scrubbed tween animates the fill's height from 0 to 100% across the timeline (start: 'top 60%' to end: 'bottom 70%'), so the line grows exactly in step with the scrollbar — scroll halfway and the spine is half full, scroll back and it recedes. This continuous fill is the backbone that ties the milestones together visually.
Cards that slide in once
Each milestone card gets a gsap.from that slides it in from the side and fades it up, triggered at start: 'top 78%' with toggleActions: 'play none none reverse' — so it animates in as it enters and reverses if you scroll it back out, replaying on re-entry. Using a discrete entrance (not scrub) here means each card has a crisp arrival rather than being half-drawn mid-scroll.
Dots that light at the right moment
Separately, each dot has a ScrollTrigger.create with an onToggle that fires when its card spans a band near the viewport center. While active, the dot tweens to a bright fill, accent border, and a slight scale-up; when the card leaves, it reverts. This makes the dots a live "you are here" indicator that tracks reading position, distinct from the one-time card entrance.
Two trigger styles, on purpose
The snippet deliberately mixes ScrollTrigger modes: a single scrub for the continuous fill, discrete toggleActions for the card entrances, and onToggle for the dot state. Each effect uses the mode that fits it — continuous value, play-once-on-enter, and active-while-in-range — which is exactly how ScrollTrigger is meant to be composed, and keeps every piece simple.
Smooth and reversible
The fill animates height within an overflow: hidden track (cheap, contained), and the cards and dots animate transforms, colors, and scale — all reversible, so scrolling up unwinds the whole timeline cleanly. will-change hints keep the motion smooth.
Customizing it
Add milestones (each is a step with a dot and card), change the fill gradient, the slide direction, the active dot color, or the trigger points. Pair it with a vertical timeline, a scroll svg path draw, or scroll pin steps.
Build with AI
Build, Understand, Optimize, and Extend It With AI
You do not need to untangle why three different ScrollTrigger modes are mixed in one snippet by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why the spine fill uses a plain scrub, the cards use toggleActions with a reverse, and the dots use a separate onToggle callback, rather than one single approach for all three. The same assistant can help optimize it — for instance checking whether creating one ScrollTrigger.create call per dot scales fine for a dozen milestones or whether it should batch triggers for a much longer timeline. It is just as useful for extending the effect: ask it to add a small percentage label that tracks the fill, make the active dot pulse with a repeating scale animation instead of a static one, or let a milestone stay permanently lit once passed instead of dimming on scroll-up. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a "scroll timeline dots" component in plain HTML, CSS, and JavaScript using GSAP and its ScrollTrigger plugin (load both from a CDN, no build step).
Requirements:
- A vertical timeline of milestone steps, each with a dot marker and a card, laid beside a track containing an inner fill element.
- Use one gsap.to tween with ease: none and scrub: true, tied to a ScrollTrigger on the whole timeline wrapper (start near the top of the viewport, end near the bottom), to animate the fill element's height from 0 to 100%, so the spine visually fills in step with scroll position and reverses when scrolling up.
- For each card, use a separate gsap.from tween (not scrubbed) that slides it in from an offset and fades it up, triggered by a ScrollTrigger with toggleActions set so the animation plays once on entering the viewport and reverses if the card scrolls back out of view, so it can replay on re-entry.
- For each dot, create an independent ScrollTrigger (via ScrollTrigger.create, not attached to a tween) whose start and end define a band near the vertical center of the viewport, and use its onToggle callback to tween the dot's background color, border color, and scale between a dim resting state and a bright active state depending on whether that trigger is currently active.
- The three behaviors (continuous scrub, one-time reversible entrance, and center-band active toggle) must each use the ScrollTrigger mode best suited to them — do not collapse them into a single timeline or a single trigger type.
- Everything must be layout-cheap: only height, transform, opacity, background-color, and border-color may be animated, nothing that forces reflow.Step by step
How to Use
- 1Add the GSAP CDNsInclude gsap and ScrollTrigger from the CDN panel.
- 2Paste HTML, CSS, and JSA vertical timeline of milestones renders.
- 3Scroll downThe spine fills and cards slide in.
- 4Watch the dotsEach dot lights as its card reaches center.
- 5Scroll back upThe fill recedes and dots dim.
- 6Add a milestoneCopy a .tl-step with its dot and card.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
A track with an inner gradient fill runs down the side, and one scrubbed tween animates the fill's height from 0 to 100% across the timeline (start: top 60% to end: bottom 70%). Because it is scrubbed, the line grows in step with the scrollbar and recedes when you scroll up — a continuous progress backbone for the milestones.
The cards need a one-time entrance, so toggleActions: play none none reverse plays them in on enter and reverses on leave. The dots need to reflect the current reading position, so onToggle lights the dot while its card is in a center band and reverts when it leaves. Each effect uses the ScrollTrigger mode that fits its behaviour.
Each dot has a ScrollTrigger whose start and end define a band near the viewport center; its onToggle fires when the card enters or leaves that band, tweening the dot to a bright fill, accent border, and slight scale while active. This makes the dots a live you-are-here indicator that tracks scroll position independently of the card entrances.
Yes. The fill is scrubbed, the cards use a reversible toggleActions, and the dots revert on toggle-off, so scrolling up unwinds the spine, slides the cards back out, and dims the dots cleanly. Everything animates transforms, colors, height, and scale, all of which GSAP can play in both directions.
In a mount effect, register ScrollTrigger and create the fill scrub plus per-card entrance and per-dot toggle triggers, scoped to refs. Return a cleanup that reverts the GSAP context so all triggers are removed on unmount. If milestones are dynamic, create the triggers after they render and call ScrollTrigger.refresh(). The CSS ports unchanged.