# Gantt charts

> Page: https://docs.plotwork.dev/gantt-charts. Index of every page: https://docs.plotwork.dev/llms.txt

A Gantt chart draws a plan on a time axis: tasks as bars, milestones as diamonds, and lines for what waits for what. Use it for a project plan, a roadmap, a release schedule, or any work where order and dates matter. It is for anyone, human or AI agent, who writes diagram source.

## TL;DR

| Question | Answer |
|---|---|
| What is it for? | A schedule. One row for each task or milestone, in bands by section, on a time axis with two rows of labels, and a line from each task to the work that waits for it. |
| What is the first line? | `gantt "Launch plan" start=2026-01-05 unit=week today=2026-02-11 weekends=skip` |
| How are dates written? | As `YYYY-MM-DD`, always a calendar day. A length is `10d`, `2w`, or `1mo`. There is no time of day. |
| Is today marked? | Only when you write `today=`. A picture must be the same every time it is drawn, so Plotwork has no clock. |
| What are the limits? | 300 rows, 60 sections, 3660 days, and an axis no wider than 6000px. Past one, the source is refused with `E140`. More than 40 rows draws a warning. |
| How is it drawn? | Bars carry their label inside when it fits and beside them when it does not. The colors come from the theme, in light and dark, and no color is ever written in the source. |
| Does Mermaid convert? | Yes. A `gantt` chart becomes a Plotwork Gantt chart. See [From Mermaid](#from-mermaid). |

## Ask your agent

```text
Use Plotwork to draw a Gantt chart of a product launch plan. Research takes two weeks, then the product brief takes one week. Design and build run side by side after the brief. The web app waits for the visual design and the API, and it is the critical task. Add a milestone for the beta and one for the public launch. Skip weekends, and mark 11 February 2026 as today.
```

```text
Use Plotwork to draw a two year roadmap in quarters, with Platform, Product, and Growth sections and a milestone for general availability.
```

## Example

**Diagram source**

```diagram
gantt "Product launch plan" start=2026-01-05 unit=week today=2026-02-11 weekends=skip holidays=2026-04-03 desc="A fourteen week plan from research to launch, with design and build running side by side."

section "Discover" {
  task research "User research"  start=2026-01-05  duration=2w  status=done
  task brief    "Product brief"  after=research    duration=1w  status=done
}

section "Design" tone=violet {
  task ux  "UX flows"       after=brief  duration=2w  status=done
  task ui  "Visual design"  after=ux     duration=3w  status=active
  task ds  "Design system"  after=brief  duration=4w  status=active
}

section "Build" tone=aqua {
  task api     "API and data model"  after=brief   duration=5w  status=active
  task web     "Web app"             after=ui,api  duration=4w  status=critical
  task mobile  "Mobile app"          after=ui,api  duration=4w
  milestone beta "Beta ready"        after=web
}

section "Launch" tone=orange {
  task docs  "Help center"        after=ui          duration=3w
  task qa    "Testing and fixes"  after=web,mobile  duration=2w  status=critical
  milestone ga "Public launch"    after=qa          status=critical
}
```

*Picture: Product launch plan: 10 tasks and 2 milestones in 4 sections, from 2026-01-05 to 2026-04-14. A fourteen week plan from research to launch, with design and build running side by side.*

**Text description**

**Section Discover (2026-01-05 to 2026-01-23):**

- User research: 2026-01-05 to 2026-01-16, 12 days, done
- Product brief: 2026-01-19 to 2026-01-23, 5 days, done, after User research

**Section Design (2026-01-26 to 2026-02-27):**

- UX flows: 2026-01-26 to 2026-02-06, 12 days, done, after Product brief
- Visual design: 2026-02-09 to 2026-02-27, 19 days, active, after UX flows
- Design system: 2026-01-26 to 2026-02-20, 26 days, active, after Product brief

**Section Build (2026-01-26 to 2026-03-30):**

- API and data model: 2026-01-26 to 2026-02-27, 33 days, active, after Product brief
- Web app: 2026-03-02 to 2026-03-27, 26 days, critical, after Visual design and API and data model
- Mobile app: 2026-03-02 to 2026-03-27, 26 days, after Visual design and API and data model
- Beta ready: milestone on 2026-03-30, after Web app

**Section Launch (2026-03-02 to 2026-04-14):**

- Help center: 2026-03-02 to 2026-03-20, 19 days, after Visual design
- Testing and fixes: 2026-03-30 to 2026-04-13, 15 days, critical, after Web app and Mobile app
- Public launch: milestone on 2026-04-14, critical, after Testing and fixes

**Today is 2026-02-11.**

**Weekends are shaded and not counted in durations.**

**Days off: 2026-04-03.**

## The language

Like the other kinds, a Gantt chart has one statement on each line. Everything is `key=value`, labels are in double quotes, ids are words, and a line that starts with `#` is a comment.

### The header

```plotwork
gantt "Launch plan" start=2026-01-05 unit=week today=2026-02-11 weekends=skip holidays=2026-04-03 desc="A fourteen week plan."
```

| Option | Value | Meaning |
|---|---|---|
| Title | `"Text"` after `gantt` | The name of the chart. It is not painted. It is the accessible name of the picture. |
| `start=` | A date | The first day the axis must show. It can only widen the axis, never cut a task. |
| `end=` | A date | The last day the axis must show. It must not be before `start=` (`E329`). |
| `unit=` | `auto`, `day`, `week`, `month`, `quarter`, `year` | The step of the axis. The default is `auto`, which follows the span of the plan. |
| `today=` | A date | Draws the today marker on that day. A day off the axis draws nothing and warns (`W310`). |
| `weekends=` | `show`, `shade`, `skip` | `show`, the default, treats weekends like any other day. `shade` tints them. `skip` tints them and leaves them out when counting `d` and `w` lengths. |
| `holidays=` | Dates, separated by commas | Days off. They are tinted, and left out when counting lengths, whatever `weekends=` says. At most 400. |
| `desc=` | `"Text"` | The plain-language description of the chart. `desc "Text"` on a line of its own does the same. |

### Sections

```plotwork
section "Design" tone=violet {
  task ux "UX flows" start=2026-01-05 duration=2w
}
```

A section is a band of rows with its name in a tab on the left. Close it with `}` on a line of its own. Sections do not nest (`E330`). `tone=` colors the band and the bars inside it, unless a bar chooses its own tone. Rows written outside every section go in a band with no tab. A chart that has both kinds of row warns (`W314`).

### Tasks

```plotwork
task ui "Visual design" after=ux,api duration=3w status=active tone=violet
```

| Option | Value | Meaning |
|---|---|---|
| Id | A word | Required, and unique across tasks and milestones. Letters, digits, `_` and `-`, starting with a letter or `_`. |
| Label | `"Text"` | Required. |
| `start=` | A date | The first day of the work. Use this or `after=`, never both (`E324`). One of them is required (`E322`). |
| `after=` | Ids, separated by commas | The task starts the day after the last of these ends. A milestone counts as ending on its day. An id may be written further down the source. |
| `duration=` | `10d`, `2w`, `1mo` | How long the work lasts, the first day included. Use this or `end=`, never both (`E324`). One of them is required (`E323`). |
| `end=` | A date | The last day of the work, included. It must not be before the start (`E326`). |
| `status=` | `planned`, `active`, `done`, `critical` | `planned` is the default. `critical` can be added to one of the others: `status=critical,done`. |
| `tone=` | A tone | The color family. A critical task is red unless it chooses a tone. |

### Milestones

```plotwork
milestone beta "Beta" after=web status=critical
milestone ga "Launch" at=2026-03-02
```

A milestone is a point in time, drawn as a diamond with its label and its date. It takes `at=` (a day) or `after=` (it sits where the last of these ends), `status=` (`planned`, `done`, or `critical`), and `tone=`. A `start=`, a `duration=`, or an `end=` is an error (`E327`), because a milestone takes no time.

### How dates work

| Rule | Detail |
|---|---|
| A date is a calendar day | It is a day and never an instant, so no clock, time zone, or locale can change a picture. Years run from 1900 to 2200. |
| A task covers whole days, the first and the last included | `start=2026-01-05 duration=10d` covers the 5th to the 14th. `end=2026-01-14` says the same. |
| A task that follows another starts the next day | `after=a` starts the day after the last day of `a`. With several ids, it follows the last to finish. |
| A milestone sits on the boundary of its day | A milestone after a task is drawn where the task's bar ends. Its date is the day after the last day of work. |
| `d` counts days, `w` counts seven days, `mo` counts calendar months | `1mo` from 2026-01-31 ends on 2026-02-28. A length is never less than a day. |
| `weekends=skip` and `holidays=` count working days | `5d` is five working days and `1w` is five. A week of work that crosses a weekend is drawn across it. Months stay calendar months. |
| A task never starts on a day off | With days skipped, a task that follows another starts on the next working day. A written `start=` on a day off moves the same way and warns (`W311`). A milestone with `at=` stays where you put it. |
| Dependencies are checked | A name that is not a task is `E131`, with the nearest id in the hint. A circle is `E325`, reported once with the ids around it. |

### A longer plan

A plan of several years reads better in quarters. This one sets `start=` and `end=` so the axis shows both years, uses `at=` for a milestone with a fixed day, and gives each section its own tone.

**Diagram source**

```diagram
gantt "Two year roadmap" unit=quarter start=2026-01-01 end=2027-12-31 today=2026-08-15 desc="A roadmap drawn in quarters."

section "Platform" tone=blue {
  task core "Core services rewrite" start=2026-01-12 duration=6mo status=done
  task scale "Horizontal scaling" after=core duration=4mo status=active
  task multi "Multi-region" after=scale duration=6mo
}

section "Product" tone=green {
  task mobile "Mobile app" start=2026-04-01 duration=5mo status=done
  task ai "Assistant features" start=2026-09-01 duration=8mo
  milestone ga "General availability" at=2027-06-30 status=critical
}

section "Growth" tone=amber {
  task partner "Partner programme" start=2026-07-01 duration=12mo
  task intl "International launch" after=multi duration=3mo
}
```

*Picture: Two year roadmap: 7 tasks and 1 milestone in 3 sections, from 2026-01-12 to 2027-08-11. A roadmap drawn in quarters.*

**Text description**

**Section Platform (2026-01-12 to 2027-05-11):**

- Core services rewrite: 2026-01-12 to 2026-07-11, 181 days, done
- Horizontal scaling: 2026-07-12 to 2026-11-11, 123 days, active, after Core services rewrite
- Multi-region: 2026-11-12 to 2027-05-11, 181 days, after Horizontal scaling

**Section Product (2026-04-01 to 2027-06-30):**

- Mobile app: 2026-04-01 to 2026-08-31, 153 days, done
- Assistant features: 2026-09-01 to 2027-04-30, 242 days
- General availability: milestone on 2027-06-30, critical

**Section Growth (2026-07-01 to 2027-08-11):**

- Partner programme: 2026-07-01 to 2027-06-30, 365 days
- International launch: 2027-05-12 to 2027-08-11, 92 days, after Multi-region

**Today is 2026-08-15.**

### What is drawn

| Part | How |
|---|---|
| Axis | Two rows above the bands. The upper row names months (over days and weeks) or years (over months and quarters), and the lower row holds day numbers, week starts, months, quarters, or years. A label that does not fit its cell is left out. |
| Bands | A tinted band for each section, with its name in a tab on the left. A long name wraps onto up to three lines and is cut with an ellipsis past the widest tab (`W203`). |
| Rows | One row for each task or milestone, in the order written. The order you write is the order a reader sees. |
| Planned work | A tinted bar with a tone outline. |
| Work in progress | A solid bar in the tone, with its label in the surface color. |
| Finished work | A card with a check mark and a quiet label. |
| Critical work | Red, unless the task chooses a tone. |
| Labels | Inside the bar when they fit, and to the right of it when they do not, with the picture widened to make room. A label past 360px is cut and warns (`W203`). |
| Milestones | A diamond, hollow until it is done, then the label and the date in short form, such as `Mar 2`. |
| Dependencies | One rounded line with an arrowhead, from the earlier bar to the start of the later one, drawn under the bars. |
| Today | A pill named Today above the first band and a thin line from it through every band. |
| Days off | Weekends, when shaded or skipped, and holidays are tinted in the bands. Nothing is tinted when a day is narrower than 5px. |

## Limits and warnings

| Limit | Value | Over it |
|---|---|---|
| Tasks and milestones | 300 | `E140` before any layout, with a hint to split by phase or team |
| Sections | 60 | `E140` |
| Span of the plan | 3660 days | `E140` |
| Width of the axis | 6000px at the step asked for | `E140`, with the coarser steps in the hint |
| Days off | 400 | `E140` |
| Tasks and milestones, for a warning | 40 | `W200` |
| Width of the picture | About 1100px | `W202` |

A chart of 300 rows is read, laid out, and drawn in about 100ms, and a chart of twenty rows takes a few milliseconds.

| Code | When it fires | Fix |
|---|---|---|
| `E320` | A date that is not a real `YYYY-MM-DD` day, or is outside 1900 to 2200. | Write a real day, such as `2026-03-02`. |
| `E321` | A length that is not a whole number and a unit. `10`, `0d`, `1y`, `2M`, and hours are the usual causes. | Write `10d`, `2w`, or `1mo`. The hint names the right form. |
| `E322` | A task or milestone with no `start=`, `at=`, or `after=`. | Say when it starts. |
| `E323` | A task with no `duration=` or `end=`. | Say how long it lasts or when it ends. |
| `E324` | Options that cannot be used together: `start=` and `after=`, `duration=` and `end=`, `at=` and `after=`, or two progress words. | Keep one of the pair. |
| `E325` | A circle of dependencies. The message names the ids around it. | Remove one `after=` in the circle. |
| `E326` | A task that ends before it starts. The hint gives the day it starts on. | Move `end=` or `start=`. |
| `E327` | A milestone with a `start=`, `duration=`, or `end=`. | Use `at=` or `after=`. |
| `E328` | A chart with no tasks or milestones. | Add a task. |
| `E329` | An `end=` before the `start=` of the header. | Swap or correct the two. |
| `E330` | A section inside a section. | Close the first section before the second. |
| `E131` | A name in `after=` that is not a task or milestone. | Fix the id. The hint names the nearest one. |
| `E140` | The chart is over a hard limit. | Split the plan by phase or team. |
| `W310` | `today=` is off the axis, so no marker is drawn. | Move `today=` or widen the axis with `start=` and `end=`. |
| `W311` | A written `start=` fell on a day off and moved to the next working day. | Write the working day, or accept the move. |
| `W312` | A task ended before today and is not done. | Mark it `done`, or move the dates. |
| `W313` | A task is marked done and starts after today. | Check the status or `today=`. |
| `W314` | Rows outside a section next to rows inside one. | Put every row in a section, or none. |
| `W200` | More than 40 rows. | Split the plan. |
| `W202` | The picture is wider than about 1100px. | Use a coarser `unit=`, or shorten labels. |
| `W203` | Text was cut with an ellipsis. | Shorten the label or the section name. |

The shared syntax codes also apply: `E100`, `E101`, `E110`, `E121`, `E130`, `E133`, `E134`, `E135`, and `E136` cover syntax, an unterminated string, an unknown statement or tone, a repeated id, an unclosed block, an unknown option, and a bad value.

## From Mermaid

The `gantt` type of Mermaid is read by `convert_mermaid` and by `sync_markdown` with `convertMermaid`. The converter first reads the settings that apply to the whole chart, so every date is read in its `dateFormat`, then reads the sections and tasks in order. It works out each task's dates the way Mermaid does before it writes anything.

This chart has a week-long task, and excludes weekends:

```mermaid-source
gantt
  title Website refresh
  dateFormat YYYY-MM-DD
  excludes weekends
  section Build
  Design :done, design, 2026-03-02, 2w
  Build :active, build, after design, 10d
  Ship :crit, ship, after build, 3d
  Live :milestone, live, after ship, 0d
```

It converts to this Plotwork. The two weeks became fourteen working days, because Mermaid counts a week as seven working days when weekends are excluded, and the converter reports that (`M235`):

```plotwork
gantt "Website refresh" weekends=skip

section "Build" {
  task design "Design" start=2026-03-02 duration=14d status=done
  task build "Build" after=design duration=10d status=active
  task ship "Ship" after=build duration=3d status=critical
  milestone live "Live" after=ship
}
```

### What is kept

| Mermaid | Plotwork |
|---|---|
| `title`, front matter `title:`, `accTitle`, `accDescr` | The title and `desc=`. A `title` line wins over the front matter. |
| `dateFormat` with a year, a month, and a day in numbers (`YYYY`, `YY`, `MM`, `M`, `DD`, `D`, `X`, `x`) | Read, and written as `YYYY-MM-DD`. |
| `section Name` | `section "Name" {` and `}`. |
| `Task :tags, id, start, end` | A `task`. The three shapes Mermaid reads are kept: end only, start and end, and id with start and end. |
| `after a b c` | `after=a,b,c` |
| A length such as `3d` or `2w` | `duration=3d` or `duration=2w` |
| `1M`, `1y` | `duration=1mo`, `duration=12mo` |
| An end date | `end=` on the day before it, because Mermaid leaves the end day out. With `inclusiveEndDates` it is the same day. |
| `milestone` tag | A `milestone` |
| `done`, `active`, `crit` | `status=done`, `status=active`, `status=critical`, which combine |
| `excludes weekends`, or Saturday and Sunday | `weekends=skip` |
| `excludes 2024-03-29` | `holidays=2024-03-29` |
| `tickInterval 1day`, `1week`, `1month` | `unit=day`, `unit=week`, `unit=month` |
| A task with no id | An id made from its name |

### What changes or is dropped

| Mermaid | What happens | Code |
|---|---|---|
| A task with no start | It follows the task before it (`after=`). When that is a milestone, the day the milestone sits on is written as `start=`. A first task with no start stops the conversion. | `M131` |
| A length with a fraction, `0d` on a task, or hours, minutes, or seconds | Rounded to whole days, and at least one. A week with weekends excluded is written in days. | `M235` |
| `until id` | `end=` on the day before that task starts. The task no longer moves with the other one. | `M237` |
| A milestone with a length | A `milestone` on the middle day of that length, or `after=` for a length of zero. | `M234` |
| `vert` | Dropped, and the task kept. | `M236` |
| `excludes` another weekday, or `includes` | Dropped. | `M231` |
| A `tickInterval` that is not a day, a week, or a month | Dropped. | `M232` |
| `axisFormat` | Dropped. Plotwork writes its own labels. | `M230` |
| `todayMarker` with a style | Dropped. Plotwork has no clock, so write `today=` on the header. | `M233` |
| `topAxis`, `weekday`, `weekend`, `displayMode` | Dropped. Plotwork chooses the layout. | `M238` |
| `click` | Dropped. | `M201` |
| `%%{init}%%` and front matter `config:` | Dropped. | `M200` |
| HTML and line breaks in a name | Turned into plain text. | `M205` |
| An id that is not valid, such as `1st` | Renamed. | `M206` |
| An empty section | Dropped. | `M216` |

### What stops the conversion

| Mermaid | Why | Code |
|---|---|---|
| A `dateFormat` with a time of day, or hours and minutes in the lengths | Plotwork schedules in whole days. | `M130` |
| A task after an id that is not in the chart, or a circle | Its dates cannot be worked out. | `M131` |
| A `dateFormat` with a month name, a weekday, or no year | The dates cannot be read. | `M132` |
| More than 300 tasks | Over the limit. | `M102` |
| A line that is not a task, or a date not in the `dateFormat` | The line cannot be read. | `M110` |

With `lenient` set, the converter skips an unreadable line, or a task that cannot be placed and the tasks that depend on it, and reports it (`M218`).

## See also

| Doc | Why |
|---|---|
| [Writing diagrams](https://docs.plotwork.dev/writing-diagrams.md) | The language for flow, sequence, and schema diagrams, and the options every kind shares |
| [Examples to copy](https://docs.plotwork.dev/examples.md) | Finished diagrams with the prompt that gets an agent to write each one |
| [Migrating from Mermaid](https://docs.plotwork.dev/migrating-from-mermaid.md) | How every Mermaid type converts, and what the diagnostic codes mean |
| [Git graphs](https://docs.plotwork.dev/git-graphs.md) | Branches, commits, merges, and tags |
| [Class diagrams](https://docs.plotwork.dev/class-diagrams.md) | Classes, members, and the relations between them |
| [State diagrams](https://docs.plotwork.dev/state-diagrams.md) | States, transitions, and regions |
| [Timelines](https://docs.plotwork.dev/timelines.md) | Events in time order |
| [User journeys](https://docs.plotwork.dev/user-journeys.md) | Tasks scored by how a person feels |
| [Mind maps](https://docs.plotwork.dev/mind-maps.md) | A topic and its branches |
| [Language reference](https://docs.plotwork.dev/reference/language.md) | Every word of the language, one page for each topic |
| [Diagram kinds](https://docs.plotwork.dev/reference/language/kinds.md) | Every kind, its header, and what it draws |
