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

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

Share & Support

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.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Rather than reasoning through the sync logic alone, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how selectMilestone() keeps the active dot, the proportional track fill, and the detail card's fade transition all driven from one function call and one shared milestones array, and why the fill percentage is computed as a ratio rather than a fixed per-step value. The same assistant can help you extend it — ask it to add auto-play that advances through milestones on a timer and pauses on manual interaction, animate the track fill with a slight overshoot easing instead of a linear transition, or make the timeline keyboard-navigable with arrow keys for accessibility. It's also useful for a content review: ask whether five milestones is the right number for your company's actual history, or whether grouping into "eras" with a milestone sub-list per era would read better for a longer story. 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:

text
Build a hero section in plain HTML, CSS, and vanilla JavaScript centered on a clickable horizontal company-milestone timeline — no timeline or scrollytelling library.

Requirements:
- A row of at least five clickable "point" elements, each showing a year label and a small dot, evenly spaced along a horizontal track line that sits behind them.
- Store each milestone's year, title, and descriptive text in a single JavaScript array. Below the timeline, render a detail card showing the currently selected milestone's title and text.
- Clicking any point must, through one single shared function: mark that point's dot as visually active (distinct border/fill color and a slight scale-up), update the detail card below to that milestone's title and text, and update a progress track fill behind the points so it visually fills from the start up to the clicked point's position.
- Calculate the track fill as a percentage proportional to the clicked point's index divided by the total number of points minus one — not a fixed width per step — so the fill formula automatically adapts if more milestone points are added later without needing per-step values recalculated by hand.
- When switching between milestones, fade the detail card's opacity down, swap its text content, then fade it back up, rather than replacing the text instantly with no transition.
- The first milestone should be selected and its detail shown by default when the page loads.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

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.