Responsive Horizontal Timeline using HTML and CSS

A horizontal timeline is a great way to show events in chronological order while keeping the UI compact and visually appealing. Below you’ll find a complete, responsive, accessible timeline built with pure HTML and CSS. On wide screens it shows a horizontal timeline with events positioned above and below a center line; on small screens it switches to a vertical layout for better readability.


Live features

  • Pure HTML & CSS (no JS required)
  • Horizontal layout with alternating items
  • Responsive: becomes vertical on narrow screens
  • Accessible use of <time> and ARIA roles
  • Optional: scroll snapping for better horizontal navigation

HTML

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>Responsive Horizontal Timeline</title>
  <link rel="stylesheet" href="styles.css" />
</head>
<body>

<section class="timeline" aria-label="Project timeline">
  <div class="timeline__container" role="list">
    <article class="timeline__item" role="listitem">
      <time datetime="2023-01-01">Jan 1, 2023</time>
      <h3>Project Kickoff</h3>
      <p>Initial planning, scope, and team alignment.</p>
    </article>

    <article class="timeline__item" role="listitem">
      <time datetime="2023-03-15">Mar 15, 2023</time>
      <h3>Design Phase</h3>
      <p>Wireframes and visual design completed.</p>
    </article>

    <article class="timeline__item" role="listitem">
      <time datetime="2023-06-01">Jun 1, 2023</time>
      <h3>Development Start</h3>
      <p>Backend and frontend scaffolding implemented.</p>
    </article>

    <article class="timeline__item" role="listitem">
      <time datetime="2023-09-05">Sep 5, 2023</time>
      <h3>Beta Release</h3>
      <p>Beta released to internal testers for feedback.</p>
    </article>

    <article class="timeline__item" role="listitem">
      <time datetime="2023-12-10">Dec 10, 2023</time>
      <h3>Public Launch</h3>
      <p>Product launched to the public.</p>
    </article>
  </div>
</section>

</body>
</html>

CSS (styles.css)

:root{
  --bg: #f7f8fb;
  --card: #ffffff;
  --muted: #6b7280;
  --accent: #6c5ce7;
  --line: #e6e9ef;
  --shadow: 0 6px 18px rgba(30,40,60,0.06);
  --radius: 12px;
}

/* Page basics */
*{box-sizing:border-box}
body{
  font-family: Inter, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
  margin:0;
  background:var(--bg);
  color:#111827;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  padding:2rem;
}

/* Timeline wrapper */
.timeline{
  max-width:1100px;
  margin:0 auto;
}

/* Horizontal container */
.timeline__container{
  position:relative;
  display:flex;
  gap:1.5rem;
  align-items:center;        /* center items vertically relative to timeline line */
  padding:2rem;
  height:260px;             /* fixed height for horizontal view */
  overflow-x:auto;          /* allow scrolling when narrow */
  -webkit-overflow-scrolling:touch;
  scroll-behavior:smooth;
  border-radius:14px;
  background:linear-gradient(180deg, rgba(255,255,255,0.6), rgba(255,255,255,0.4));
  box-shadow:var(--shadow);
}

/* Center timeline line (horizontal on wide screens) */
.timeline__container::before{
  content:"";
  position:absolute;
  left:1.5rem;
  right:1.5rem;
  top:50%;
  height:4px;
  background:var(--line);
  transform:translateY(-50%);
  border-radius:4px;
  z-index:0;
}

/* Individual timeline event (card) */
.timeline__item{
  position:relative;
  z-index:1;                /* above the center line */
  flex:0 0 300px;           /* fixed card width, not shrinking */
  background:var(--card);
  padding:1rem;
  border-radius:var(--radius);
  box-shadow:var(--shadow);
  min-width:260px;
  text-align:left;
  transition:transform .2s ease, box-shadow .2s ease;
  scroll-snap-align:center; /* for scroll snapping */
}

/* hover and focus */
.timeline__item:focus,
.timeline__item:hover{
  transform:translateY(-6px);
  box-shadow:0 10px 30px rgba(30,40,60,0.12);
  outline: none;
}

/* Date/time styling */
.timeline__item time{
  display:block;
  font-size:0.85rem;
  color:var(--muted);
  margin-bottom:0.45rem;
}

/* Dot that connects card to center line */
.timeline__item::before{
  content:"";
  position:absolute;
  left:50%;
  top:50%;
  transform:translate(-50%,-50%);
  width:14px;
  height:14px;
  border-radius:50%;
  background:var(--card);
  border:4px solid var(--accent);
  box-shadow:0 4px 10px rgba(0,0,0,0.06);
}

/* Alternate items above/below the line */
.timeline__item:nth-child(odd){
  align-self:flex-start;    /* push to top half */
  margin-top:-40px;         /* slight lift */
}
.timeline__item:nth-child(even){
  align-self:flex-end;      /* push to bottom half */
  margin-bottom:-40px;      /* slight lift */
}

/* Small accessible focus style */
.timeline__item:focus{
  box-shadow:0 8px 28px rgba(108,92,231,0.18);
  transform:translateY(-8px);
}

/* Make it prettier on large screens */
.timeline__item h3{
  margin:0 0 .4rem 0;
  font-size:1.05rem;
}
.timeline__item p{
  margin:0;
  color:#374151;
  line-height:1.45;
}

/* Optional: enable horizontal snap behavior */
.timeline__container{
  scroll-snap-type: x proximity;
}

/* Responsive — switch to vertical timeline on narrow screens */
@media (max-width: 700px){
  .timeline__container{
    flex-direction:column;
    align-items:stretch;
    height:auto;
    padding:1rem;
  }

  /* vertical line on the left instead of center horizontal line */
  .timeline__container::before{
    left:48px;
    top:1rem;
    bottom:1rem;
    right:auto;
    width:4px;
    height:auto;
    transform:none;
  }

  .timeline__item{
    flex: none;
    min-width: auto;
    margin-left:76px;      /* leave space for the vertical line and dot */
    margin-bottom:1.25rem;
  }

  /* move the dot to the left side */
  .timeline__item::before{
    left:40px;
    top:1rem;
    transform:translate(-50%,0);
  }

  /* reset alternating offsets */
  .timeline__item:nth-child(odd),
  .timeline__item:nth-child(even){
    align-self:stretch;
    margin-top:0;
    margin-bottom:0;
  }
}

How it works — short explanation

  1. Layout strategy
    • The .timeline__container is a horizontal flex container. Each .timeline__item is a flex child with flex: 0 0 300px so cards keep a fixed width and don’t shrink.
    • A pseudo-element (::before) on the container draws a horizontal center line.
  2. Alternating placement
    • We use align-self: flex-start and flex-end on odd/even children to place items above and below the center line so the timeline alternates visually.
  3. Responsive behavior
    • At smaller width (@media (max-width: 700px)), container switches to flex-direction: column. The center line changes into a left vertical line (::before repositioned), cards stack vertically, and the dot (card ::before) shifts to the left.
  4. Accessibility
    • Each event uses a <time datetime="YYYY-MM-DD"> which helps machines and screen-readers understand dates.
    • role="list" and role="listitem" improve navigation semantics for some assistive tech.
    • Focus styles ensure keyboard users can interact.

Optional improvements & variations

  • Add icons: include an <svg> or icon inside each .timeline__item::before or inside the card.
  • Animation: add CSS animations (fade/translate) for cards when they come into view (use @keyframes or intersection observer + classes if JS allowed).
  • Vertical-only alternate: remove the alternating offsets if you want a simple horizontal row without up/down alternation.
  • Dynamic content: generate the <article> items via JS when events are stored in JSON.
  • Accessibility: add aria-describedby linking to details panels, or make each item a <button> or <a> to open more details.

Quick tips & best practices

  • Use min-width and overflow-x:auto to ensure the horizontal timeline remains usable on narrower screens: users can scroll horizontally.
  • Use scroll-snap-type for a pleasant scrolling experience on touch devices (already included above).
  • Keep each card width reasonable (260–340px) for readability.
  • Make sure color contrast (text vs background) meets WCAG standards.

FAQ

Q: Can I make the timeline purely vertical on desktop?
Yes. Remove the center line rules and the alternating offsets, then set .timeline__container{flex-direction:column;} — the mobile styles already do that.

Q: How do I add more data (time, location, images)?
Extend the .timeline__item HTML with additional elements, e.g., <p class="location">Remote</p> or <img>; then style accordingly.

Q: Is JavaScript required?
No — the provided example is purely HTML/CSS. JS is only needed for advanced behaviors (lazy loading, fetching events dynamically, animated reveal on scroll).


How to Remove Filter in CSS – With Real Example

More

Share via
Copy link