Roadmap status columns
A public product roadmap laid out as Planned, In progress and Shipped columns that stack on phones, using semantic sections and lists.
Tailwind CSSLayoutsroadmapcolumnsgridproduct
Live preview
Code
<section class="mx-auto max-w-5xl px-4" aria-labelledby="roadmap-title">
<h2 id="roadmap-title" class="mb-4 text-xl font-semibold text-slate-900">Public roadmap</h2>
<!-- One column on phones, a row of columns on large screens -->
<div class="grid gap-4 lg:grid-cols-3">
<section aria-labelledby="col-planned" class="rounded-xl bg-slate-100 p-3">
<h3 id="col-planned" class="mb-3 flex items-center gap-2 text-sm font-semibold text-slate-700">
<span class="size-2 rounded-full bg-sky-500"></span> Planned <span class="ml-auto rounded bg-white px-1.5 text-xs">2</span>
</h3>
<ul class="space-y-2">
<li class="rounded-lg bg-white p-3 shadow-sm">
<p class="text-sm font-medium text-slate-900">Dark mode</p>
<p class="mt-1 text-xs text-slate-500">Follows the system setting.</p>
</li>
<li class="rounded-lg bg-white p-3 shadow-sm">
<p class="text-sm font-medium text-slate-900">CSV export</p>
<p class="mt-1 text-xs text-slate-500">Download any report.</p>
</li>
</ul>
</section>
<section aria-labelledby="col-progress" class="rounded-xl bg-slate-100 p-3">
<h3 id="col-progress" class="mb-3 flex items-center gap-2 text-sm font-semibold text-slate-700">
<span class="size-2 rounded-full bg-amber-500"></span> In progress <span class="ml-auto rounded bg-white px-1.5 text-xs">1</span>
</h3>
<ul class="space-y-2">
<li class="rounded-lg bg-white p-3 shadow-sm">
<p class="text-sm font-medium text-slate-900">Team workspaces</p>
<p class="mt-1 text-xs text-slate-500">Share boards with roles.</p>
</li>
</ul>
</section>
<section aria-labelledby="col-done" class="rounded-xl bg-slate-100 p-3">
<h3 id="col-done" class="mb-3 flex items-center gap-2 text-sm font-semibold text-slate-700">
<span class="size-2 rounded-full bg-emerald-500"></span> Shipped <span class="ml-auto rounded bg-white px-1.5 text-xs">1</span>
</h3>
<ul class="space-y-2">
<li class="rounded-lg bg-white p-3 shadow-sm opacity-80">
<p class="text-sm font-medium text-slate-900">Mobile layout</p>
<p class="mt-1 text-xs text-slate-500">Released this month.</p>
</li>
</ul>
</section>
</div>
</section>
How it works
What it solves
A roadmap page should answer "what is coming and what already shipped" without a drag-and-drop board library. This markup is read-only, fast to render server-side and works with no JavaScript.
How it works
A single grid switches from one column to a row of columns at the lg breakpoint. Each status is its own section with a heading connected through aria-labelledby, so screen reader users can jump between landmarks by status. Items sit in a ul, which communicates the count and structure natively. The coloured dot is decorative and the status name carries the meaning, so colour is never the only signal. Shipped cards are slightly dimmed to read as finished while staying legible.
The count badge in each header is rendered from the length of that list, which gives people a quick sense of load.
Adapting it
- Add or rename columns by copying a section; change
lg:grid-cols-3to match the count. - Link each card title to a detail page with comments or votes.
- Sort cards by vote count or planned quarter in your query, not in the template.
- Combine it with an upvote button to let visitors express priority.
Pitfalls
- Keep headings unique per section so the generated ids do not collide.
- A very long column should scroll with the page, not inside a fixed-height box, to avoid nested scrolling.
- Do not promise dates; use quarters or no time labels unless you will keep them current.
How to use it
- Add the markup to a public page that already includes Tailwind CSS 4.
- Query your items grouped by status and loop each group into its column.
- Render the count badge from each group's length.
- Keep the status labels in one config so the roadmap and admin forms agree.
Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.