webdevpuneet.com

Horizontal Timeline — Free HTML CSS JS Snippet

Horizontal Timeline · Layouts · Plain HTML, CSS & JS · Live preview

CategoryLayouts

Copy & paste

Source Code

<div class="wrap">
  <div class="timeline-head">
    <h2 class="tl-title">Product roadmap</h2>
    <div class="tl-controls">
      <button class="tl-nav" onclick="scrollTl(-1)" aria-label="Previous">‹</button>
      <button class="tl-nav" onclick="scrollTl(1)"  aria-label="Next">›</button>
    </div>
  </div>

  <div class="tl-scroll" id="tl-scroll">
    <div class="tl-track" id="tl-track">
      <!-- Connecting line -->
      <div class="tl-line"></div>

      <div class="tl-item done" data-date="Q1 2025">
        <div class="tl-dot"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><polyline points="20 6 9 17 4 12"/></svg></div>
        <div class="tl-card">
          <div class="tl-date">Q1 2025</div>
          <div class="tl-event">Foundation</div>
          <div class="tl-desc">Core infrastructure, auth system, and basic dashboard.</div>
        </div>
      </div>

      <div class="tl-item done" data-date="Q2 2025">
        <div class="tl-dot"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><polyline points="20 6 9 17 4 12"/></svg></div>
        <div class="tl-card">
          <div class="tl-date">Q2 2025</div>
          <div class="tl-event">Collaboration</div>
          <div class="tl-desc">Team workspaces, real-time editing, and comment threads.</div>
        </div>
      </div>

      <div class="tl-item active" data-date="Q3 2025">
        <div class="tl-dot pulse"></div>
        <div class="tl-card current">
          <div class="tl-badge">Current</div>
          <div class="tl-date">Q3 2025</div>
          <div class="tl-event">Analytics</div>
          <div class="tl-desc">Usage dashboards, custom reports, and data export.</div>
        </div>
      </div>

      <div class="tl-item upcoming" data-date="Q4 2025">
        <div class="tl-dot"></div>
        <div class="tl-card">
          <div class="tl-date">Q4 2025</div>
          <div class="tl-event">Integrations</div>
          <div class="tl-desc">Slack, GitHub, Jira, and 30+ third-party connectors.</div>
        </div>
      </div>

      <div class="tl-item upcoming" data-date="Q1 2026">
        <div class="tl-dot"></div>
        <div class="tl-card">
          <div class="tl-date">Q1 2026</div>
          <div class="tl-event">Mobile Apps</div>
          <div class="tl-desc">Native iOS and Android apps with offline support.</div>
        </div>
      </div>

      <div class="tl-item upcoming" data-date="Q2 2026">
        <div class="tl-dot"></div>
        <div class="tl-card">
          <div class="tl-date">Q2 2026</div>
          <div class="tl-event">AI Features</div>
          <div class="tl-desc">Smart suggestions, auto-tagging, and workflow automation.</div>
        </div>
      </div>

    </div>
  </div>

  <div class="tl-progress">
    <div class="tl-prog-fill" id="tl-prog-fill"></div>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Three CSS states: .done (filled checkmark) / .active (pulse ring) / .upcoming (grey empty)
Pulse ring animation: ::after with scale + opacity keyframe on active dot
Connecting line: absolute position top:53px left/right:40px — spans full track
Horizontal scroll: overflow-x:auto, scrollbar hidden via CSS in Chrome and Firefox
Scroll progress bar below track: scrollLeft/(scrollWidth-clientWidth)×100%
Auto-scroll to active item on load: offsetLeft centring with smooth behaviour
Prev/next buttons: scrollBy with smooth behaviour, 200px per click
Each item min-width: 160px — uniform spacing regardless of label length

About this UI Snippet

Horizontal Timeline — Done/Active/Upcoming States, Pulse Ring, Scroll Progress & Auto-Scroll

Screenshot of the Horizontal Timeline snippet rendered live

A horizontal timeline visualises a sequence of events, milestones, or phases laid out left-to-right — ideal for product roadmaps, company histories, project phases, and onboarding step indicators. Unlike vertical timelines (which work with unlimited height), horizontal timelines must handle overflow — this snippet uses horizontal scroll with hidden scrollbars, a scroll progress indicator, and arrow navigation buttons.

Three item states

Each .tl-item has one of three classes: .done (past milestones — filled indigo dot with checkmark), .active (current milestone — pulsing ring animation), or .upcoming (future milestones — empty grey dot, muted card background). The CSS responds to these classes automatically — no JavaScript state management needed for the visual differentiation.

The pulsing ring animation

The active dot has a .pulse class. Its ::after pseudo-element uses a @keyframes that oscillates scale (0.9 to 1.1) and opacity (0.4 to 1) — creating a breathing glow ring effect. This immediately draws the eye to the current timeline position.

The connecting line

A single .tl-line div is absolutely positioned at top: 53px (the dot centre) with left: 40px and right: 40px. It spans the full track width behind all dots. No JavaScript sizing needed — the absolute positioning with right: 40px automatically extends as the track grows with more items.

Overflow scroll without scrollbar

The .tl-scroll container has overflow-x: auto with scrollbar-width: none (Firefox) and ::-webkit-scrollbar { display: none } (Chrome/Safari). This allows natural scroll gestures on touch devices while hiding the scrollbar for a cleaner desktop appearance. The arrow buttons provide explicit navigation.

Auto-scroll to active item

On page load, scrollTo() centres the active item in the viewport. The timeout ensures the DOM is rendered before measuring offsetLeft. The calculation: item.offsetLeft - containerWidth/2 + itemWidth/2 centres the item precisely.

Connecting to real roadmap data

Replace the static HTML with dynamically generated items: fetch("/api/roadmap").then(r=>r.json()).then(data => { data.milestones.forEach(m => { const item = document.createElement("div"); item.className = "tl-item " + m.state; item.innerHTML = buildItemHTML(m); track.appendChild(item); }); autoScrollToActive(); }). Define buildItemHTML(m) to return the dot and card HTML from the milestone object. The auto-scroll and progress bar work identically with dynamic content.

Accessibility considerations

The horizontal timeline uses a scrollable region. Add role="region" and aria-label="Product roadmap timeline" to .tl-scroll for screen readers. Each .tl-item can include an aria-label with the date and event name. The prev/next buttons already have aria-label attributes. For keyboard users, the scroll container is focusable by default and arrow keys scroll horizontally when focused.

Step by step

How to Use

  1. 1
    Scroll horizontally or use the ‹ › arrowsThe timeline scrolls horizontally. Use arrow buttons or swipe on mobile. The thin progress bar below shows scroll position within the timeline.
  2. 2
    Update the timeline items and datesEdit each .tl-item div: change .tl-date, .tl-event, and .tl-desc text. The state classes (done/active/upcoming) control the visual appearance automatically.
  3. 3
    Change the current active itemMove class="tl-item active" to the item you want highlighted as current. Remove the .pulse class from the old dot div and add it to the new one. Move the .tl-badge "Current" label similarly.
  4. 4
    Add more timeline itemsDuplicate any .tl-item div and place it at the correct position. The connecting line automatically extends. Each item has min-width: 160px so they space consistently.
  5. 5
    Change the connecting line positionUpdate top: 53px on .tl-line to match the vertical centre of your dots. If you change the dot size or top padding, adjust this value to keep the line at dot height.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component mapping a milestones array to timeline items, or "Tailwind" for a React + Tailwind CSS version.

Real-world uses

Common Use Cases

SaaS product roadmap and release schedule
Display your product roadmap with completed features, current quarter work, and planned future releases. The done/active/upcoming states communicate timeline progress at a glance. Link each card to a detailed changelog or milestone page.
Onboarding progress and setup wizard steps
Show users where they are in an onboarding sequence. Each step is a timeline item — completed steps use the done state, the current step uses active with the pulse ring, and remaining steps use upcoming. Users can see the full sequence and their progress.
Order tracking and shipping status timeline
Show order status from placed → confirmed → packed → shipped → delivered as a horizontal timeline. Each state transition updates the done/active classes. The horizontal layout matches user expectations from major e-commerce tracking pages.
Company history and milestone pages
Showcase company history with founding year, key milestones, product launches, and expansion events. All items use the done state (all past). Add a Current badge to the most recent milestone. The horizontal scroll handles long histories gracefully.
Study horizontal scroll with hidden scrollbar technique
The snippet demonstrates the standard hidden scrollbar pattern for custom horizontal scrollers: overflow-x: auto combined with scrollbar-width: none and ::-webkit-scrollbar { display: none }. This is used in carousels, timelines, and chip rows across the web.
Development sprint and project phase tracking
Display development sprints or project phases on a shared team timeline. The current sprint uses the active state. Team members can see upcoming phases at a glance. Link each card to a sprint board or project doc.
Related: Mega Menu Panel
See the Mega Menu Panel for a related navigation pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Three CSS declarations together hide the scrollbar in all browsers while keeping scrolling functional. For Firefox: scrollbar-width: none on the container. For Chrome, Safari, Edge: .container::-webkit-scrollbar { display: none }. For Internet Explorer: -ms-overflow-style: none. The container still scrolls on touch swipe and mouse wheel horizontally — only the visual scrollbar track is hidden. This is the standard approach for all carousels and custom horizontal scroll areas.

The auto-scroll calculation: const offset = active.offsetLeft - scroll.clientWidth / 2 + active.offsetWidth / 2. This centres the active item in the visible scroll viewport. scroll.scrollTo({ left: offset, behavior: "smooth" }) animates to that position. The setTimeout(fn, 300) delay ensures the layout has rendered and offsetLeft has been computed before the scroll fires. Without the timeout, offsetLeft may be 0 during the synchronous render phase.

Define a milestones array: const milestones = [{ date: "Q1 2025", event: "Foundation", desc: "...", state: "done" }, ...]. Map to HTML: milestones.forEach(m => { const item = document.createElement("div"); item.className = "tl-item " + m.state; item.innerHTML = getItemHTML(m); track.appendChild(item); }). Where getItemHTML(m) returns the inner HTML with dot and card. Run this before the auto-scroll initialisation.

Click "JSX" to download. Define a milestones prop array with date, event, desc, and state fields. Map to timeline item divs with className derived from state. Use useRef for the scroll container. The auto-scroll useEffect depends on the milestones array. The progress bar state uses useState(0) updated by a scroll event listener attached in useEffect with cleanup.