Gantt charts
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. |
Ask your agent
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.Use Plotwork to draw a two year roadmap in quarters, with Platform, Product, and Growth sections and a milestone for general availability.Example
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}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
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
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
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
milestone beta "Beta" after=web status=criticalmilestone ga "Launch" at=2026-03-02A 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.
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}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:
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, 0dIt 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):
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 | The language for flow, sequence, and schema diagrams, and the options every kind shares |
| Examples to copy | Finished diagrams with the prompt that gets an agent to write each one |
| Migrating from Mermaid | How every Mermaid type converts, and what the diagnostic codes mean |
| Git graphs | Branches, commits, merges, and tags |
| Class diagrams | Classes, members, and the relations between them |
| State diagrams | States, transitions, and regions |
| Timelines | Events in time order |
| User journeys | Tasks scored by how a person feels |
| Mind maps | A topic and its branches |
| Language reference | Every word of the language, one page for each topic |
| Diagram kinds | Every kind, its header, and what it draws |