Open demo

CSS code

HTML
<div class="gantt" role="region" aria-label="Delivery plan, July to December" tabindex="0">
  <div class="sr-only"><table>
    <caption>Delivery plan, July to December 2026. Today is 1 September.</caption>
    <thead><tr><th scope="col">Phase</th><th scope="col">Work</th><th scope="col">Starts</th><th scope="col">Ends</th></tr></thead>
    <tbody>
      <tr><th scope="row">Discovery</th><td>Research & specs</td><td><time datetime="2026-07">July</time></td><td><time datetime="2026-08">August</time></td></tr>
      <tr><th scope="row">Design</th><td>Wireframes to UI</td><td><time datetime="2026-08">August</time></td><td><time datetime="2026-09">September</time></td></tr>
      <tr><th scope="row">Build</th><td>Implementation</td><td><time datetime="2026-09">September</time></td><td><time datetime="2026-11">November</time></td></tr>
      <tr><th scope="row">Launch</th><td>Beta to GA</td><td><time datetime="2026-11">November</time></td><td><time datetime="2026-12">December</time></td></tr>
    </tbody>
  </table></div>
  <div class="grid" aria-hidden="true">
    <span class="mh" style="grid-column:2">Jul</span>
    <span class="mh" style="grid-column:3">Aug</span>
    <span class="mh" style="grid-column:4">Sep</span>
    <span class="mh" style="grid-column:5">Oct</span>
    <span class="mh" style="grid-column:6">Nov</span>
    <span class="mh" style="grid-column:7">Dec</span>

    <span class="row-label" style="--r:2"><i class="c-discovery"></i>Discovery</span>
    <span class="bar c-discovery" style="--r:2;--from:2;--to:4">Research & specs</span>

    <span class="row-label" style="--r:3"><i class="c-design"></i>Design</span>
    <span class="bar c-design" style="--r:3;--from:3;--to:5">Wireframes → UI</span>

    <span class="row-label" style="--r:4"><i class="c-build"></i>Build</span>
    <span class="bar c-build" style="--r:4;--from:4;--to:7">Implementation</span>

    <span class="row-label" style="--r:5"><i class="c-launch"></i>Launch</span>
    <span class="bar c-launch" style="--r:5;--from:6;--to:8">Beta → GA</span>

    <span class="today"></span>
  </div>
  <ul class="legend" aria-hidden="true">
    <li><i class="c-discovery"></i> Discovery</li>
    <li><i class="c-design"></i> Design</li>
    <li><i class="c-build"></i> Build</li>
    <li><i class="c-launch"></i> Launch</li>
  </ul>
</div>
CSS
/* CSS grid Gantt chart: month columns, one row per phase, bars placed with --r (row), --from and --to (grid lines).
   The chart is drawn for sighted users and hidden from screen readers, which get the same plan as a visually hidden table.
   On narrow screens the chart scrolls sideways inside a focusable region. */
.gantt{--g-line:#eef1f8;--g-muted:#687390;--g-legend:#5e6a85;--g-today:#e11d48;
  box-sizing:border-box;width:100%;max-width:880px;background:#fff;border:1px solid #e3e8f2;border-radius:14px;padding:20px 22px 24px;overflow-x:auto}
.gantt:focus-visible{outline:2px solid #4f46e5;outline-offset:2px}
.gantt .grid{display:grid;grid-template-columns:150px repeat(6,1fr);align-items:center;row-gap:10px;min-width:640px;position:relative}
/* Gridlines are painted, not built from empty cells — nothing to auto-place. */
.gantt .grid::before{content:"";position:absolute;left:150px;right:0;top:26px;bottom:0;
  background:repeating-linear-gradient(90deg,var(--g-line) 0 1px,transparent 1px calc(100%/6));pointer-events:none}
.gantt .mh{grid-row:1;font-size:.72rem;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:var(--g-muted);padding-bottom:12px;text-align:center}
.gantt .row-label{grid-column:1;grid-row:var(--r);font-size:.85rem;font-weight:600;padding-right:14px;display:flex;align-items:center;gap:8px}
.gantt .row-label i,.gantt .legend i{width:8px;height:8px;border-radius:2px;flex-shrink:0}
.gantt .bar{grid-row:var(--r);grid-column:var(--from) / var(--to);height:22px;border-radius:6px;position:relative;z-index:1;margin:0 4px;
  display:flex;align-items:center;padding:0 10px;font-size:.7rem;font-weight:700;color:#fff;white-space:nowrap;overflow:hidden}
.gantt .today{grid-row:2 / 6;grid-column:4;justify-self:start;align-self:stretch;width:2px;background:var(--g-today);z-index:2;position:relative;margin-left:-1px}
.gantt .today::after{content:"Today";position:absolute;top:-22px;left:50%;transform:translateX(-50%);font-size:.64rem;font-weight:800;color:var(--g-today);white-space:nowrap}
.gantt .legend{display:flex;gap:16px;margin:18px 0 0;padding:0;list-style:none;flex-wrap:wrap;font-size:.76rem;color:var(--g-legend)}
.gantt .legend li{display:flex;align-items:center;gap:6px}
.gantt .legend i{width:9px;height:9px;border-radius:3px}
.c-discovery{background:#4f46e5}.c-design{background:#0369a1}.c-build{background:#15803d}.c-launch{background:#f59e0b}
.gantt .bar.c-launch{color:#451a03}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div class="gantt card overflow-x-auto" role="region" aria-label="Delivery plan, July to December" tabindex="0">
  <div class="visually-hidden"><table>
    <caption>Delivery plan, July to December 2026. Today is 1 September.</caption>
    <thead><tr><th scope="col">Phase</th><th scope="col">Work</th><th scope="col">Starts</th><th scope="col">Ends</th></tr></thead>
    <tbody>
      <tr><th scope="row">Discovery</th><td>Research & specs</td><td><time datetime="2026-07">July</time></td><td><time datetime="2026-08">August</time></td></tr>
      <tr><th scope="row">Design</th><td>Wireframes to UI</td><td><time datetime="2026-08">August</time></td><td><time datetime="2026-09">September</time></td></tr>
      <tr><th scope="row">Build</th><td>Implementation</td><td><time datetime="2026-09">September</time></td><td><time datetime="2026-11">November</time></td></tr>
      <tr><th scope="row">Launch</th><td>Beta to GA</td><td><time datetime="2026-11">November</time></td><td><time datetime="2026-12">December</time></td></tr>
    </tbody>
  </table></div>
  <div class="grid" aria-hidden="true">
    <span class="mh text-uppercase text-center" style="grid-column:2">Jul</span>
    <span class="mh text-uppercase text-center" style="grid-column:3">Aug</span>
    <span class="mh text-uppercase text-center" style="grid-column:4">Sep</span>
    <span class="mh text-uppercase text-center" style="grid-column:5">Oct</span>
    <span class="mh text-uppercase text-center" style="grid-column:6">Nov</span>
    <span class="mh text-uppercase text-center" style="grid-column:7">Dec</span>

    <span class="row-label d-flex align-items-center" style="--r:2"><i class="c-discovery flex-shrink-0"></i>Discovery</span>
    <span class="bar c-discovery d-flex align-items-center" style="--r:2;--from:2;--to:4">Research & specs</span>

    <span class="row-label d-flex align-items-center" style="--r:3"><i class="c-design flex-shrink-0"></i>Design</span>
    <span class="bar c-design d-flex align-items-center" style="--r:3;--from:3;--to:5">Wireframes → UI</span>

    <span class="row-label d-flex align-items-center" style="--r:4"><i class="c-build flex-shrink-0"></i>Build</span>
    <span class="bar c-build d-flex align-items-center" style="--r:4;--from:4;--to:7">Implementation</span>

    <span class="row-label d-flex align-items-center" style="--r:5"><i class="c-launch flex-shrink-0"></i>Launch</span>
    <span class="bar c-launch d-flex align-items-center" style="--r:5;--from:6;--to:8">Beta → GA</span>

    <span class="today"></span>
  </div>
  <ul class="legend list-unstyled mb-0 d-flex flex-wrap" aria-hidden="true">
    <li class="d-flex align-items-center"><i class="c-discovery"></i> Discovery</li>
    <li class="d-flex align-items-center"><i class="c-design"></i> Design</li>
    <li class="d-flex align-items-center"><i class="c-build"></i> Build</li>
    <li class="d-flex align-items-center"><i class="c-launch"></i> Launch</li>
  </ul>
</div>
CSS
/* CSS grid Gantt chart in a Bootstrap .card with .overflow-x-auto: month columns, one row per phase,
   bars placed with --r (row), --from and --to (grid lines). Screen readers get the same plan as a
   .visually-hidden table; the drawn chart is aria-hidden. */
.gantt{--g-line:#eef1f8;--g-muted:#687390;--g-legend:#5e6a85;--g-today:#e11d48;
  --bs-card-border-color:#e3e8f2;--bs-card-border-radius:14px;width:100%;max-width:880px;padding:20px 22px 24px}
.gantt:focus-visible{outline:2px solid #4f46e5;outline-offset:2px}
.gantt .grid{display:grid;grid-template-columns:150px repeat(6,1fr);align-items:center;row-gap:10px;min-width:640px;position:relative}
.gantt .grid::before{content:"";position:absolute;left:150px;right:0;top:26px;bottom:0;
  background:repeating-linear-gradient(90deg,var(--g-line) 0 1px,transparent 1px calc(100%/6));pointer-events:none}
.gantt .mh{grid-row:1;font-size:.72rem;font-weight:800;letter-spacing:.07em;color:var(--g-muted);padding-bottom:12px}
.gantt .row-label{grid-column:1;grid-row:var(--r);font-size:.85rem;font-weight:600;padding-right:14px;gap:8px}
.gantt .row-label i{width:8px;height:8px;border-radius:2px}
.gantt .bar{grid-row:var(--r);grid-column:var(--from) / var(--to);height:22px;border-radius:6px;position:relative;z-index:1;margin:0 4px;
  padding:0 10px;font-size:.7rem;font-weight:700;color:#fff;white-space:nowrap;overflow:hidden}
.gantt .today{grid-row:2 / 6;grid-column:4;justify-self:start;align-self:stretch;width:2px;background:var(--g-today);z-index:2;position:relative;margin-left:-1px}
.gantt .today::after{content:"Today";position:absolute;top:-22px;left:50%;transform:translateX(-50%);font-size:.64rem;font-weight:800;color:var(--g-today);white-space:nowrap}
.gantt .legend{margin-top:18px;gap:16px;font-size:.76rem;color:var(--g-legend)}
.gantt .legend li{gap:6px}
.gantt .legend i{width:9px;height:9px;border-radius:3px}
.c-discovery{background:#4f46e5}.c-design{background:#0369a1}.c-build{background:#15803d}.c-launch{background:#f59e0b}
.gantt .bar.c-launch{color:#451a03}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div role="region" aria-label="Delivery plan, July to December" tabindex="0" class="w-full max-w-[880px] overflow-x-auto rounded-[14px] border border-[#e3e8f2] bg-white px-[22px] pt-5 pb-6 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#4f46e5]">
  <div class="sr-only"><table>
    <caption>Delivery plan, July to December 2026. Today is 1 September.</caption>
    <thead><tr><th scope="col">Phase</th><th scope="col">Work</th><th scope="col">Starts</th><th scope="col">Ends</th></tr></thead>
    <tbody>
      <tr><th scope="row">Discovery</th><td>Research & specs</td><td><time datetime="2026-07">July</time></td><td><time datetime="2026-08">August</time></td></tr>
      <tr><th scope="row">Design</th><td>Wireframes to UI</td><td><time datetime="2026-08">August</time></td><td><time datetime="2026-09">September</time></td></tr>
      <tr><th scope="row">Build</th><td>Implementation</td><td><time datetime="2026-09">September</time></td><td><time datetime="2026-11">November</time></td></tr>
      <tr><th scope="row">Launch</th><td>Beta to GA</td><td><time datetime="2026-11">November</time></td><td><time datetime="2026-12">December</time></td></tr>
    </tbody>
  </table></div>
  <div aria-hidden="true" class="relative grid min-w-[640px] grid-cols-[150px_repeat(6,1fr)] items-center gap-y-2.5 before:pointer-events-none before:absolute before:top-[26px] before:right-0 before:bottom-0 before:left-[150px] before:bg-[repeating-linear-gradient(90deg,#eef1f8_0_1px,transparent_1px_calc(100%/6))]">
    <span class="col-start-2 row-start-1 pb-3 text-center text-[.72rem] font-extrabold tracking-[.07em] text-[#687390] uppercase">Jul</span>
    <span class="col-start-3 row-start-1 pb-3 text-center text-[.72rem] font-extrabold tracking-[.07em] text-[#687390] uppercase">Aug</span>
    <span class="col-start-4 row-start-1 pb-3 text-center text-[.72rem] font-extrabold tracking-[.07em] text-[#687390] uppercase">Sep</span>
    <span class="col-start-5 row-start-1 pb-3 text-center text-[.72rem] font-extrabold tracking-[.07em] text-[#687390] uppercase">Oct</span>
    <span class="col-start-6 row-start-1 pb-3 text-center text-[.72rem] font-extrabold tracking-[.07em] text-[#687390] uppercase">Nov</span>
    <span class="col-start-7 row-start-1 pb-3 text-center text-[.72rem] font-extrabold tracking-[.07em] text-[#687390] uppercase">Dec</span>

    <span class="col-start-1 row-start-2 flex items-center gap-2 pr-3.5 text-[.85rem] font-semibold"><i class="size-2 shrink-0 rounded-[2px] bg-[#4f46e5]"></i>Discovery</span>
    <span class="col-start-2 col-end-4 row-start-2 relative z-[1] mx-1 flex h-[22px] items-center overflow-hidden rounded-md px-2.5 text-[.7rem] font-bold whitespace-nowrap bg-[#4f46e5] text-white">Research & specs</span>

    <span class="col-start-1 row-start-3 flex items-center gap-2 pr-3.5 text-[.85rem] font-semibold"><i class="size-2 shrink-0 rounded-[2px] bg-[#0369a1]"></i>Design</span>
    <span class="col-start-3 col-end-5 row-start-3 relative z-[1] mx-1 flex h-[22px] items-center overflow-hidden rounded-md px-2.5 text-[.7rem] font-bold whitespace-nowrap bg-[#0369a1] text-white">Wireframes → UI</span>

    <span class="col-start-1 row-start-4 flex items-center gap-2 pr-3.5 text-[.85rem] font-semibold"><i class="size-2 shrink-0 rounded-[2px] bg-[#15803d]"></i>Build</span>
    <span class="col-start-4 col-end-7 row-start-4 relative z-[1] mx-1 flex h-[22px] items-center overflow-hidden rounded-md px-2.5 text-[.7rem] font-bold whitespace-nowrap bg-[#15803d] text-white">Implementation</span>

    <span class="col-start-1 row-start-5 flex items-center gap-2 pr-3.5 text-[.85rem] font-semibold"><i class="size-2 shrink-0 rounded-[2px] bg-[#f59e0b]"></i>Launch</span>
    <span class="col-start-6 col-end-8 row-start-5 relative z-[1] mx-1 flex h-[22px] items-center overflow-hidden rounded-md px-2.5 text-[.7rem] font-bold whitespace-nowrap bg-[#f59e0b] text-[#451a03]">Beta → GA</span>

    <span class="relative z-[2] col-start-4 row-start-2 row-end-6 -ml-px w-0.5 self-stretch justify-self-start bg-[#e11d48] after:absolute after:-top-[22px] after:left-1/2 after:-translate-x-1/2 after:text-[.64rem] after:font-extrabold after:whitespace-nowrap after:text-[#e11d48] after:content-['Today']"></span>
  </div>
  <ul aria-hidden="true" class="mt-[18px] flex flex-wrap gap-4 text-[.76rem] text-[#5e6a85]">
    <li class="flex items-center gap-1.5"><i class="size-[9px] rounded-[3px] bg-[#4f46e5]"></i> Discovery</li>
    <li class="flex items-center gap-1.5"><i class="size-[9px] rounded-[3px] bg-[#0369a1]"></i> Design</li>
    <li class="flex items-center gap-1.5"><i class="size-[9px] rounded-[3px] bg-[#15803d]"></i> Build</li>
    <li class="flex items-center gap-1.5"><i class="size-[9px] rounded-[3px] bg-[#f59e0b]"></i> Launch</li>
  </ul>
</div>

About this set

A Gantt chart built with CSS grid, with no canvas, SVG or script. Six month columns from July to December carry four phase rows: Discovery, Design, Build and Launch, each with a coloured bar labelled with its work, from Research & specs to Beta → GA. Faint gridlines mark the months, a red Today line crosses every row at the start of September, and a legend sits underneath. Use it for project plans, delivery schedules, release trains or event programmes where time spans overlap. Each bar is placed with three custom properties, –r for the row and –from and –to for the start and end grid lines, so rescheduling is a one-number change. Because a positioned chart means little to a screen reader, the drawn chart and legend are aria-hidden and the same plan is given as a visually hidden table with a caption, phase, work, start month and end month, with months in time elements. The chart sits in a focusable region named Delivery plan, which scrolls sideways on narrow screens instead of overflowing the page. Bar colours were darkened, or given dark text, so every label reaches 4.5:1.

What’s included

  • Bars placed with --r, --from and --to on a CSS grid
  • Today marker and painted month gridlines
  • Visually hidden table carries the same data as text
  • Focusable named region scrolls the chart on phones
  • Bar label colours adjusted to pass 4.5:1

Accessibility

Screen readers get a visually hidden table with a caption, column and row headers and month values in time elements; the drawn grid and legend are aria-hidden so the plan is not read twice. The chart region is named, keyboard focusable for scrolling and shows a 2px indigo outline on focus. Month labels, legend and bar text all reach 4.5:1.

Customise it

Move a bar by changing --r, --from and --to; move the Today line with its grid-column. Colours are the .c-* classes and --g-line, --g-muted and --g-today on .gantt. In Tailwind edit col-start-, col-end- and the bg-[#…] classes, and keep the hidden table in step with the bars.