Open demo

CSS code

HTML
<ol class="qtl">
  <li style="--i:0"><span class="dot" aria-hidden="true"></span><p class="label">Q1</p><div class="card"><h2>Foundation</h2><p>New billing core and workspace-level permissions.</p></div></li>
  <li style="--i:1"><span class="dot" aria-hidden="true"></span><p class="label">Q2</p><div class="card"><h2>Speed</h2><p>Sub-second loads everywhere; edge caching for APIs.</p></div></li>
  <li style="--i:2"><span class="dot" aria-hidden="true"></span><p class="label">Q3</p><div class="card"><h2>Collaboration</h2><p>Live presence, comments, and shared views.</p></div></li>
  <li style="--i:3"><span class="dot" aria-hidden="true"></span><p class="label">Q4</p><div class="card"><h2>Mobile</h2><p>Native apps with full offline support.</p></div></li>
</ol>
CSS
/* Quarterly roadmap: cards alternate above and below a gradient road. Each item's horizontal slot is --i.
   Under 760px it becomes a vertical list with the road on the left. */
.qtl{--qtl-from:#ea580c;--qtl-to:#f59e0b;--qtl-label:#c2410c;--qtl-muted:#7a6f5f;
  position:relative;box-sizing:border-box;width:100%;max-width:900px;height:280px;margin:0;padding:0;list-style:none}
.qtl::before{content:"";position:absolute;top:50%;left:0;right:0;height:4px;border-radius:99px;background:linear-gradient(90deg,var(--qtl-from),var(--qtl-to))}
.qtl>li{position:absolute;width:22%;left:calc(var(--i) * 26%)}
.qtl .dot{position:absolute;left:0;box-sizing:border-box;width:18px;height:18px;border-radius:50%;background:#fff;border:4px solid var(--qtl-from)}
.qtl>li:nth-child(odd){bottom:calc(50% + 20px)}
.qtl>li:nth-child(odd) .dot{bottom:-29px}
.qtl>li:nth-child(even){top:calc(50% + 20px)}
.qtl>li:nth-child(even) .dot{top:-29px}
.qtl .label{margin:0 0 6px;font-size:.72rem;font-weight:900;letter-spacing:.08em;color:var(--qtl-label)}
.qtl .card{background:#fff;border:1px solid #eee6d9;border-radius:12px;padding:14px 16px;box-shadow:0 12px 26px -18px rgba(90,60,20,.25)}
.qtl h2{margin:0 0 4px;font-size:.9rem}
.qtl .card p{margin:0;font-size:.78rem;line-height:1.55;color:var(--qtl-muted)}
@media (max-width:760px){
  .qtl{height:auto;display:flex;flex-direction:column;gap:22px;padding-left:26px}
  .qtl::before{left:8px;right:auto;top:0;bottom:0;width:4px;height:auto;background:linear-gradient(180deg,var(--qtl-from),var(--qtl-to))}
  .qtl>li,.qtl>li:nth-child(odd),.qtl>li:nth-child(even){position:relative;width:100%;left:auto;top:auto;bottom:auto}
  .qtl>li:nth-child(odd) .dot,.qtl>li:nth-child(even) .dot{left:-26px;top:14px;bottom:auto}
}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<ol class="qtl list-unstyled mb-0">
  <li style="--i:0"><span class="dot rounded-circle" aria-hidden="true"></span><p class="label">Q1</p><div class="card"><div class="card-body"><h2 class="card-title fw-bold">Foundation</h2><p class="card-text">New billing core and workspace-level permissions.</p></div></div></li>
  <li style="--i:1"><span class="dot rounded-circle" aria-hidden="true"></span><p class="label">Q2</p><div class="card"><div class="card-body"><h2 class="card-title fw-bold">Speed</h2><p class="card-text">Sub-second loads everywhere; edge caching for APIs.</p></div></div></li>
  <li style="--i:2"><span class="dot rounded-circle" aria-hidden="true"></span><p class="label">Q3</p><div class="card"><div class="card-body"><h2 class="card-title fw-bold">Collaboration</h2><p class="card-text">Live presence, comments, and shared views.</p></div></div></li>
  <li style="--i:3"><span class="dot rounded-circle" aria-hidden="true"></span><p class="label">Q4</p><div class="card"><div class="card-body"><h2 class="card-title fw-bold">Mobile</h2><p class="card-text">Native apps with full offline support.</p></div></div></li>
</ol>
CSS
/* Quarterly roadmap: Bootstrap .card items alternate above and below a gradient road; each item's slot is --i.
   Under 760px it becomes a vertical list with the road on the left. */
.qtl{--qtl-from:#ea580c;--qtl-to:#f59e0b;--qtl-label:#c2410c;--qtl-muted:#7a6f5f;position:relative;width:100%;max-width:900px;height:280px}
.qtl::before{content:"";position:absolute;top:50%;left:0;right:0;height:4px;border-radius:99px;background:linear-gradient(90deg,var(--qtl-from),var(--qtl-to))}
.qtl>li{position:absolute;width:22%;left:calc(var(--i) * 26%)}
.qtl .dot{position:absolute;left:0;width:18px;height:18px;background:#fff;border:4px solid var(--qtl-from)}
.qtl>li:nth-child(odd){bottom:calc(50% + 20px)}
.qtl>li:nth-child(odd) .dot{bottom:-29px}
.qtl>li:nth-child(even){top:calc(50% + 20px)}
.qtl>li:nth-child(even) .dot{top:-29px}
.qtl .label{margin-bottom:6px;font-size:.72rem;font-weight:900;letter-spacing:.08em;color:var(--qtl-label)}
.qtl .card{--bs-card-border-color:#eee6d9;--bs-card-border-radius:12px;--bs-card-spacer-y:14px;--bs-card-spacer-x:16px;--bs-card-title-spacer-y:4px;
  box-shadow:0 12px 26px -18px rgba(90,60,20,.25)}
.qtl .card-title{font-size:.9rem;line-height:normal}
.qtl .card-text{font-size:.78rem;line-height:1.55;color:var(--qtl-muted)}
@media (max-width:760px){
  .qtl{height:auto;display:flex;flex-direction:column;gap:22px;padding-left:26px}
  .qtl::before{left:8px;right:auto;top:0;bottom:0;width:4px;height:auto;background:linear-gradient(180deg,var(--qtl-from),var(--qtl-to))}
  .qtl>li,.qtl>li:nth-child(odd),.qtl>li:nth-child(even){position:relative;width:100%;left:auto;top:auto;bottom:auto}
  .qtl>li:nth-child(odd) .dot,.qtl>li:nth-child(even) .dot{left:-26px;top:14px;bottom:auto}
}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<ol class="relative flex w-full max-w-[900px] flex-col gap-[22px] pl-[26px] before:absolute before:inset-y-0 before:left-2 before:w-1 before:rounded-full before:bg-[linear-gradient(180deg,#ea580c,#f59e0b)] min-[761px]:block min-[761px]:h-[280px] min-[761px]:pl-0 min-[761px]:before:inset-x-0 min-[761px]:before:top-1/2 min-[761px]:before:bottom-auto min-[761px]:before:h-1 min-[761px]:before:w-auto min-[761px]:before:bg-[linear-gradient(90deg,#ea580c,#f59e0b)]">
  <li class="group relative min-[761px]:absolute min-[761px]:left-[0%] min-[761px]:w-[22%] min-[761px]:odd:bottom-[calc(50%+20px)] min-[761px]:even:top-[calc(50%+20px)]"><span aria-hidden="true" class="absolute top-3.5 -left-[26px] size-[18px] rounded-full border-4 border-[#ea580c] bg-white min-[761px]:left-0 min-[761px]:group-odd:top-auto min-[761px]:group-odd:-bottom-[29px] min-[761px]:group-even:-top-[29px]"></span><p class="mb-1.5 text-[.72rem] font-black tracking-[.08em] text-[#c2410c]">Q1</p><div class="rounded-xl border border-[#eee6d9] bg-white px-4 py-3.5 shadow-[0_12px_26px_-18px_rgba(90,60,20,.25)]"><h2 class="mb-1 text-[.9rem] font-bold">Foundation</h2><p class="text-[.78rem] leading-[1.55] text-[#7a6f5f]">New billing core and workspace-level permissions.</p></div></li>
  <li class="group relative min-[761px]:absolute min-[761px]:left-[26%] min-[761px]:w-[22%] min-[761px]:odd:bottom-[calc(50%+20px)] min-[761px]:even:top-[calc(50%+20px)]"><span aria-hidden="true" class="absolute top-3.5 -left-[26px] size-[18px] rounded-full border-4 border-[#ea580c] bg-white min-[761px]:left-0 min-[761px]:group-odd:top-auto min-[761px]:group-odd:-bottom-[29px] min-[761px]:group-even:-top-[29px]"></span><p class="mb-1.5 text-[.72rem] font-black tracking-[.08em] text-[#c2410c]">Q2</p><div class="rounded-xl border border-[#eee6d9] bg-white px-4 py-3.5 shadow-[0_12px_26px_-18px_rgba(90,60,20,.25)]"><h2 class="mb-1 text-[.9rem] font-bold">Speed</h2><p class="text-[.78rem] leading-[1.55] text-[#7a6f5f]">Sub-second loads everywhere; edge caching for APIs.</p></div></li>
  <li class="group relative min-[761px]:absolute min-[761px]:left-[52%] min-[761px]:w-[22%] min-[761px]:odd:bottom-[calc(50%+20px)] min-[761px]:even:top-[calc(50%+20px)]"><span aria-hidden="true" class="absolute top-3.5 -left-[26px] size-[18px] rounded-full border-4 border-[#ea580c] bg-white min-[761px]:left-0 min-[761px]:group-odd:top-auto min-[761px]:group-odd:-bottom-[29px] min-[761px]:group-even:-top-[29px]"></span><p class="mb-1.5 text-[.72rem] font-black tracking-[.08em] text-[#c2410c]">Q3</p><div class="rounded-xl border border-[#eee6d9] bg-white px-4 py-3.5 shadow-[0_12px_26px_-18px_rgba(90,60,20,.25)]"><h2 class="mb-1 text-[.9rem] font-bold">Collaboration</h2><p class="text-[.78rem] leading-[1.55] text-[#7a6f5f]">Live presence, comments, and shared views.</p></div></li>
  <li class="group relative min-[761px]:absolute min-[761px]:left-[78%] min-[761px]:w-[22%] min-[761px]:odd:bottom-[calc(50%+20px)] min-[761px]:even:top-[calc(50%+20px)]"><span aria-hidden="true" class="absolute top-3.5 -left-[26px] size-[18px] rounded-full border-4 border-[#ea580c] bg-white min-[761px]:left-0 min-[761px]:group-odd:top-auto min-[761px]:group-odd:-bottom-[29px] min-[761px]:group-even:-top-[29px]"></span><p class="mb-1.5 text-[.72rem] font-black tracking-[.08em] text-[#c2410c]">Q4</p><div class="rounded-xl border border-[#eee6d9] bg-white px-4 py-3.5 shadow-[0_12px_26px_-18px_rgba(90,60,20,.25)]"><h2 class="mb-1 text-[.9rem] font-bold">Mobile</h2><p class="text-[.78rem] leading-[1.55] text-[#7a6f5f]">Native apps with full offline support.</p></div></li>
</ol>

About this set

A year roadmap on a horizontal orange gradient road, with one card per quarter alternating above and below it: Q1 Foundation, Q2 Speed, Q3 Collaboration and Q4 Mobile. Each card has a small orange quarter label, a heading and a short note, and a ring on the road marks where it belongs. The warm off-white page and soft card shadows suit a pitch page, an investor update or a public product plan. Use it when there are four to six roughly equal phases and the overview matters more than detail. Each item’s horizontal slot is set by one custom property, –i, and odd and even items go above and below the road through nth-child, so the markup stays a plain ordered list. Under 760 pixels the road turns vertical on the left and the cards stack in order, so the layout never overflows a phone. The quarter labels were darkened from #ea580c to #c2410c to reach 4.5:1. The Bootstrap version uses .card and .card-body restyled through –bs-card-* variables, and the Tailwind version positions the cards with left-[…] and odd: and even: variants.

What’s included

  • Cards alternate above and below a gradient road
  • Horizontal slot set by a single --i custom property
  • Turns into a vertical list under 760px
  • Quarter labels darkened to #c2410c for contrast
  • Rings on the road are aria-hidden decoration

Accessibility

The quarters are an ordered list read in time order, whatever side of the road a card sits on, and each card has a heading. The rings are aria-hidden. Labels and card text reach 4.5:1. There are no controls or animation.

Customise it

Change --qtl-from and --qtl-to for the road gradient, --qtl-label and --qtl-muted on .qtl, and the 26% multiplier with the 22% item width to fit more items. In Tailwind edit the gradient colours and the min-[761px]:left-[…] values.