Transitions✨ Premium

Timeline Choreography

Six bars that fill then empty while a cursor runs along them, each 0.3 s after the previous one: two animations per bar (width and position) sharing the same 3 s duration and the same delay, read as a schedule of overlapping steps — pure CSS, zero JavaScript, bars that light up instead when the user reduces motion.

CSSJSTimeline

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Transitions has 52 effects, including 9 free. Explore the category →

3 usage examples

Progress of the work packages — a home builder's customer area — Timeline Choreography example 1

① Progress of the work packages — a home builder's customer area

WhenThe client opens their area: the six work packages of the site run as offset bars, and the header switches from "Work in progress: foundations" to "Starting this week: roof frame", the matching row lighting up in orange.
WhyThe offset bars show overlapping steps, like a real building site where masonry starts before the foundations are finished.
Settings150 px label column (package + weeks); rails thickened to 6 px, 30 px apart, 12 px cursor; amber to brown gradient; original keyframes, duration and delays (3 s, 0.3 s).
Importing a song's tracks — rehearsal software for a band — Timeline Choreography example 2

② Importing a song's tracks — rehearsal software for a band

WhenThe band opens today's song: the six tracks import as offset bars, then all turn full and the "Play" button goes green.
WhyOne bar per track, offset: the musician sees what is ready and what is coming, then the full lines say everything is loaded.
SettingsSame layout as example 1, pink to violet gradient; state B: animation: none on fill and cursor, 100% width, cursor at the end; going back to A restarts the cascade.
A swimming day's program — poolside screen — Timeline Choreography example 3

③ A swimming day's program — poolside screen

WhenOn the poolside screen, the day's events run as offset bars; in the morning the heats are highlighted; in the afternoon, the header switches to the finals and the clock to 16:05.
WhyReading in offset rows gives the day's flow at a glance, and the highlighted rows tell where the competition stands.
SettingsPool-blue background, cyan fill, 12 px yellow cursor; current rows in white, the others at 55% opacity; original animation.

How it works

The .md-tl-stage frame (at least 300 px tall, background #0a0a0f) holds .md-timeline (full width, 20 px padding) and six .md-tl-bars written in the HTML: 4 px tall rails, 2 px corners, indigo at 20% (rgba(99, 102, 241, .2)), 12 px apart. Each rail carries two children. .md-tl-progress, the fill in an indigo to violet gradient (#6366f1 → #a855f7), plays mdTlFill: width from 0 to 100% halfway, then back to 0. .md-tl-dot, the cursor, an 8 px indigo disc set 2 px above the rail, plays mdTlDot: left from 0 to calc(100% - 8px), then back. Both animations last 3 s, ease-in-out, looping.

The whole choreography lives in the delays written in the HTML: 0 s, 0.3 s, 0.6 s… 1.5 s, the same for a bar's fill and cursor. The cursor therefore stays stuck to the end of the fill, and each bar starts when the previous one is 10% into its cycle: you read overlapping steps, like an animated Gantt chart. Measured in Chromium 151, Firefox 153 and WebKit 26 on a 760 px bar, 600 ms after load: fills of 251, 61, then 0 px for the next four; six bars, a round 8 × 8 px cursor fully visible, the rail not clipping it.

Both animations work on width and left, which force the browser to recompute layout every frame: for six bars, the cost is negligible; beyond a few dozen, prefer transform: scaleX() (see the FAQ). Everything is written in the HTML, with no script: what shows is what you paste. The frame has its own class, prefixed like the rest of the effect: two effects from the library pasted on the same page do not overwrite each other.

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce, no more running fill or cursor: each bar is full and lights up then dims (mdTlFade, opacity from 0.25 to 1), with the same 0.3 s offset from one bar to the next. Measured in Chromium 151, Firefox 153 and WebKit 26: six full-width fills, cursors hidden, changing opacity.
  • Meaning: the animation is a decorative loop; it does not give a task's real progress. If the bars stand for real steps, write their state as text ("Foundations: done", "Roof frame: starts this week") and, for a real percentage, use a progress element or role="progressbar" with aria-valuenow.
  • Stopping: the loop runs endlessly. Motion lasting more than 5 seconds must be stoppable: freeze the bars when the operation is over (animation: none; width: 100%, as in example 2), or offer a pause button.
  • Contrast: the 20% rail is deliberately discreet; the indigo fill and cursor measure 4.4:1 on the #0a0a0f background.

Browser compatibility

Pure CSS (@keyframes on width and left, calc()), 1.6 KB of CSS and 1.1 KB of HTML, zero JavaScript. Measured in Chromium 151, Firefox 153 and WebKit 26: six bars, same offsets, same opacity replacement under reduced motion.

Chrome 43+✓ Full
Firefox 16+✓ Full
Safari 9+✓ Full
Edge 12+✓ Full
Mobile iOS 9+✓ Full
Android Chrome 43+✓ Full

Without CSS animations, the six rails stay empty with their cursor at the start: the structure shows, not the progress. If the display without animation matters, give the fill a starting width.

The code

HTML structure to paste into your page (the CSS + JS come with Premium):

index.html — structure
<div class="md-tl-stage">
  <div class="md-timeline">
    <div class="md-tl-bar">
      <div class="md-tl-progress" style="animation-delay: 0s;"></div>
      <div class="md-tl-dot" style="animation-delay: 0s;"></div>
    </div>
    … (6 bars, delay + 0.3 s each time)
  </div>
</div>
Unlock the full code — $9.90/mo incl. tax

The full HTML + CSS + JS of Timeline Choreography, ready to paste — plus all 840 effects. Or $299 once, lifetime access.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Offset between bars (HTML — animation-delay) 0.3 s (0 → 1.5 s), the same for fill and cursor 0.15 s tightens the steps (a quick wave); 0.6 s almost separates them. Keep the same delay on a bar's fill and cursor, or the cursor drifts off the end.
Duration (CSS — 3s on both animations) 3 s: 1.5 s to fill, 1.5 s to empty Change both together. 5 s gives a calm schedule; for a bar that stays full for a while, add a 60% { width: 100% } step (and left at the end) in both keyframes.
Number of bars (HTML — .md-tl-bar) 6 Add one rail + fill + cursor block per step, continuing the delays 0.3 s at a time. Beyond 10, reduce the offset so the last bar does not start too late.
Thickness and cursor (CSS — .md-tl-bar, .md-tl-dot) 4 px rail, 8 px cursor at top: -2px 6 px rail: 12 px cursor at top: -3px, and calc(100% - 12px) instead of calc(100% - 8px) in the mdTlDot keyframe (and in the end state), or it overshoots the end of the rail by 4 px. The cursor overflows the rail by design: do not put overflow: hidden back on .md-tl-bar.
Colors (CSS — .md-tl-progress gradient) #6366f1 → #a855f7 One color per bar with .md-tl-bar:nth-child(2) .md-tl-progress { background: … }, useful to tell teams or tracks apart.

FAQ

The animation is a demo loop. For a real value, remove the animation from that bar and give it its width: animation: none; width: 62%; on .md-tl-progress, and animation: none; left: calc(62% - 8px); on the cursor. Add transition: width .6s, left .6s so the bar glides when the value changes. Keep the animation on steps that have no value yet.
width and left read simply: the bar really has the width shown, and the cursor is placed with a calc(). The price is a layout recalculation every frame; for six bars, it does not show. For a schedule of several dozen rows, give the fill width: 100%; transform-origin: left and animate transform: scaleX(0) → scaleX(1); the cursor then moves inside a full-width container carrying transform: translateX().
Add a state class on .md-timeline and the rule .is-done .md-tl-progress { animation: none; width: 100%; } .is-done .md-tl-dot { animation: none; left: calc(100% - 8px); }: all bars turn full, cursor at the end. Remove the class and the animations start again from the beginning, with their delays — that is what example 2 does.