webdevpuneet.com

Hero with Company Milestone Timeline Strip — Free HTML CSS JS Snippet

Hero with Company Milestone Timeline Strip · Heroes · Plain HTML, CSS & JS · Live preview

CategoryHeroes

Copy & paste

Source Code

<section class="mts-hero">
  <span class="mts-eyebrow">Our story so far</span>
  <h1 class="mts-h1">Six years, one obsession: making invoicing invisible</h1>
  <p class="mts-sub">From two founders in a co-working space to the billing layer behind 40,000 businesses. Here's how we got here.</p>

  <div class="mts-timeline" id="mtsTimeline">
    <div class="mts-track"><div class="mts-track-fill" id="mtsTrackFill"></div></div>

    <div class="mts-point mts-point-active" data-index="0">
      <span class="mts-dot"></span>
      <span class="mts-year">2020</span>
    </div>
    <div class="mts-point" data-index="1">
      <span class="mts-dot"></span>
      <span class="mts-year">2021</span>
    </div>
    <div class="mts-point" data-index="2">
      <span class="mts-dot"></span>
      <span class="mts-year">2023</span>
    </div>
    <div class="mts-point" data-index="3">
      <span class="mts-dot"></span>
      <span class="mts-year">2024</span>
    </div>
    <div class="mts-point" data-index="4">
      <span class="mts-dot"></span>
      <span class="mts-year">2026</span>
    </div>
  </div>

  <div class="mts-detail" id="mtsDetail">
    <h3 id="mtsDetailTitle">Founded in a co-working space</h3>
    <p id="mtsDetailText">Two engineers, tired of chasing late invoices manually, write the first line of code that would become the product.</p>
  </div>
</section>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), 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

Clickable horizontal timeline of company milestones with a proportionally filling track
Single selectMilestone() function keeps the active dot, track fill, and detail card in sync
Fill percentage computed from milestone position, so adding entries needs no manual recalculation
Detail card briefly fades between milestones instead of jump-cutting the text
Active-dot styling handled entirely by CSS transitions off one class toggle
Hover state on inactive points previews the accent color before clicking
No timeline or scrollytelling library — plain click handlers and class toggling
Works as a self-contained hero without requiring scroll-linked animation
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons

About this UI Snippet

Milestone Timeline Hero — Clickable Year Strip with a Filling Progress Track

Screenshot of the Hero with Company Milestone Timeline Strip snippet rendered live

An "About us" page timeline usually means scrolling past a wall of dates. This hero condenses the whole company story into one interactive strip — a row of clickable year points connected by a track that visually fills up to whichever year is selected, with a detail card below narrating that specific milestone.

One selectMilestone(index) function drives three moving parts

Clicking any .mts-point calls selectMilestone(index), which updates the active dot styling, the track's fill width, and the detail card's title and text — all from a single milestones[index] entry. Centralizing the update in one function is what keeps a visitor from ever seeing, say, the 2024 dot highlighted while the detail card still shows 2021's story; there is exactly one code path that can change any of the three, and it always changes all three together.

The track fill is a real percentage, not a fixed-width decoration

fillPercent = (index / (points.length - 1)) * 100 computes the fill width proportionally to the milestone's position among all milestones, not a hardcoded per-step value. This means adding a sixth milestone doesn't require recalculating any percentages by hand — the formula automatically redistributes the fill points evenly across however many milestones exist, and the last milestone always fills the track to exactly 100%.

A brief fade instead of an instant content swap

Rather than replacing detailTitle and detailText immediately, selectMilestone() first fades .mts-detail to opacity: 0, swaps the text content inside a 120ms setTimeout, then fades it back in. Without this, the old story's text would visibly flash straight to the new one with no transition, which reads as an abrupt content jump rather than a deliberate story progression.

Active state and CSS transitions do the visual heavy lifting

The dot's border color, fill color, and a slight scale transform are all handled by a single .mts-point-active class toggle plus CSS transition — no JavaScript animates the dot directly. This keeps selectMilestone()'s job purely about *which* index is active, while CSS handles *how* that state change looks, which is the cleaner separation for anything that's a simple two-state visual toggle.

Customizing it

Add a sixth milestone by adding a new object (year, title, text) to the milestones array and a matching .mts-point div with data-index="5" in the HTML — the fill-percentage formula and the click-binding loop both work generically off the array and DOM length, so no other JavaScript needs to change. Auto-advance through milestones on a timer (similar to a testimonial rotator) by wrapping selectMilestone in a setInterval call if you want the story to play itself on load.

Step by step

How to Use

  1. 1
    Click a year on the timelineThe dot activates, the track fills up to that point, and the detail card below updates.
  2. 2
    Notice the fade transitionThe detail card briefly fades before showing the new milestone's title and text.
  3. 3
    Add a new milestoneAdd an entry to the milestones array in the JS panel and a matching .mts-point div with the next data-index in the HTML panel.
  4. 4
    Change the accent colorUpdate the #b3541e color values in the CSS panel to match your brand.
  5. 5
    Adjust the fade timingChange the 120 millisecond setTimeout delay in selectMilestone() for a faster or slower transition.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.

Real-world uses

Common Use Cases

About and company story pages
Compress a multi-year company history into one scannable, interactive strip instead of a long scrolling page.
Fundraising and press kit pages
Pair with a press mentions and founder quote hero for a fuller credibility-building narrative.
Careers and culture pages
Show prospective hires how the company has grown and what stage it is at right now.
Learn centralized-state UI update patterns
Study how one selectMilestone() function avoids the three-moving-parts-out-of-sync bug common in hand-rolled interactive timelines.
Product roadmap and changelog intro sections
Reuse the same clickable-strip pattern for past release milestones instead of company history.
Related: Stats Counter Row Hero
See the Stats Counter Row Hero for a complementary numbers-first way to open a company story.

Got questions?

Frequently Asked Questions

fillPercent = (index / (points.length - 1)) * 100, where index is the selected milestone's position and points.length is the total number of milestones. This proportional formula means the fill always reaches exactly 100% at the last milestone regardless of how many milestones exist, with no hardcoded per-step percentage values to maintain.

selectMilestone() sets the card's opacity to 0, waits 120 milliseconds via setTimeout, swaps in the new title and text, then restores opacity to 1. Updating the text immediately would show an abrupt jump-cut between two unrelated stories; the brief fade signals a transition between chapters instead.

All three are updated inside the same selectMilestone(index) function call, driven from the same milestones[index] data entry. There is no separate code path that could update, say, the dot styling without also updating the detail text, so the three visual pieces cannot independently drift apart.

Add a new object with year, title, and text fields to the milestones array in the JS panel, and add a matching div with class mts-point and data-index="5" inside #mtsTimeline in the HTML panel. The fill-percentage formula and the click-handler binding loop both read from the array and DOM length dynamically, so no other code needs to change.

Not by default, but it is straightforward to add: wrap a call to selectMilestone((activeIndex + 1) % milestones.length) in a setInterval, similar to how a testimonial rotator auto-advances, and clear the interval on manual click if you want a click to pause the auto-play.

No — it is plain click event listeners, one shared JavaScript function, and CSS transitions triggered by class toggling. There is no scroll-position tracking or third-party animation dependency involved.