v1.0.0

<bmx-gantt>

A project plan: the task list on the left, the timeline on the right, and the links between the tasks drawn across it.

21 properties · 6 events · 12 methods · 30 parts

Example

The dates are worked out, not typed. Each task has a duration and links; the chart schedules them against the working calendar — weekends and the two holidays here — and draws the tasks with no room to slip as the critical path. Drag a bar, its end or its progress grip, or draw a link from the dot at a bar's end to another bar, and everything that depends on it moves with it.
Every edit is a cancelable event. bmxTaskChange and bmxLinkChange carry the whole list as it would be; call preventDefault() to refuse one, or set apply-changes="false" and assign the lists yourself. canChange refuses a drag while it is still in the reader's hand.
Tab into the task list once, then use the keyboard. The arrows move between rows, → and ← open and close a summary, F2 edits a cell, Shift+→ moves a task a working day and Ctrl+Shift+→ lengthens it. Links are typed in the Predecessors column — 2, 3SS+1 — so nothing the pointer can do needs a pointer.
Show markup
<bmx-gantt
  label="Office move"
  project-start="2026-03-02"
  holidays="2026-04-03,2026-04-06"
  editable
  show-baselines
  tasks='[
    {"id":"p","name":"Planning"},
    {"id":"p1","parentId":"p","name":"Survey the new site","duration":3,"progress":100},
    {"id":"p2","parentId":"p","name":"Agree the floor plan","duration":4,"progress":60,"baselineStart":"2026-03-04","baselineFinish":"2026-03-09"},
    {"id":"p3","parentId":"p","name":"Plan signed off","milestone":true},
    {"id":"f","name":"Fit-out"},
    {"id":"f1","parentId":"f","name":"Network cabling","duration":6,"progress":20},
    {"id":"f2","parentId":"f","name":"Furniture delivery","duration":2},
    {"id":"f3","parentId":"f","name":"Meeting rooms AV","duration":5},
    {"id":"m","name":"Move"},
    {"id":"m1","parentId":"m","name":"Pack","duration":3},
    {"id":"m2","parentId":"m","name":"Move day","duration":1},
    {"id":"go","name":"Open for business","milestone":true}
  ]'
  links='[
    {"from":"p1","to":"p2"},{"from":"p2","to":"p3"},{"from":"p3","to":"f1"},
    {"from":"p3","to":"f2","lag":2},{"from":"f1","to":"f3","type":"SS","lag":2},
    {"from":"f","to":"m"},{"from":"m1","to":"m2"},{"from":"m2","to":"go"}
  ]'
></bmx-gantt>

<div class="row" style="margin-block-start: 1rem">
  <span class="note">
    <strong>The dates are worked out, not typed.</strong> Each task has a duration and links; the chart schedules
    them against the working calendar &mdash; weekends and the two holidays here &mdash; and draws the tasks with no
    room to slip as the critical path. Drag a bar, its end or its progress grip, or draw a link from the dot at a
    bar's end to another bar, and everything that depends on it moves with it.
  </span>
</div>

<div class="row" style="margin-block-start: 1rem">
  <span class="note">
    <strong>Every edit is a cancelable event.</strong> <code>bmxTaskChange</code> and <code>bmxLinkChange</code>
    carry the whole list as it would be; call <code>preventDefault()</code> to refuse one, or set
    <code>apply-changes="false"</code> and assign the lists yourself. <code>canChange</code> refuses a drag while it
    is still in the reader's hand.
  </span>
</div>

<div class="row" style="margin-block-start: 1rem">
  <span class="note">
    <strong>Tab into the task list once, then use the keyboard.</strong> The arrows move between rows,
    <kbd>&rarr;</kbd> and <kbd>&larr;</kbd> open and close a summary, <kbd>F2</kbd> edits a cell,
    <kbd>Shift</kbd>+<kbd>&rarr;</kbd> moves a task a working day and <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>&rarr;</kbd>
    lengthens it. Links are typed in the Predecessors column &mdash; <code>2, 3SS+1</code> &mdash; so nothing the
    pointer can do needs a pointer.
  </span>
</div>
<bmx-gantt
  label="Office move"
  project-start="2026-03-02"
  tasks='[{"id":"1","name":"Survey","duration":3},
          {"id":"2","name":"Fit-out","duration":10}]'
  links='[{"from":"1","to":"2"}]'
></bmx-gantt>

THE DATES ARE WORKED OUT, NOT TYPED

A task has a duration and links; where it falls follows from those and from the working calendar - weekends, holidays - by the critical-path method. Move one task and everything that depends on it moves with it; the tasks with no room to slip are drawn as the critical path. A start on a task is the earliest it may begin, not a date it is stuck on, so a plan stays consistent however it is edited. The arithmetic is shared with bmx-pert-chart and bmx-task-board, and with the WPF and Windows Forms editions of this chart, so the same plan gives the same dates everywhere.

A TREEGRID, WITH THE TIMELINE BESIDE IT

The task list is a real treegrid: one Tab stop, arrow keys between rows, Right and Left to open and close a summary, F2 to edit a cell. Everything the timeline shows is also said in the row - dates, length, progress, float and whether the task is critical - because a bar is a picture and a picture cannot be read aloud. With editable set, a focused task moves a working day at a time with Shift and the arrows and changes length with Ctrl and Shift, and its links are typed in the Predecessors column (2, 3SS+1), so nothing the pointer can do needs a pointer.

THE PAGE OWNS THE PLAN

Every edit is announced with bmxTaskChange or bmxLinkChange before it is applied, carrying the whole list as it would be. Call preventDefault() to refuse it, or set apply-changes="false" to apply nothing and assign the lists yourself; canChange lets a rule refuse a drag while it is still in the reader's hand.

LARGE PLANS

Only the rows and the header ticks in view are rendered, so ten thousand tasks scroll as smoothly as ten.

Properties

PropertyAttributeTypeDefaultDescription
applyChanges apply-changes boolean true Whether an edit is applied by the chart itself. On by default, so a plain page works with no script; turn it off to own the lists entirely.
canChange property only (detail: BmxGanttTaskChangeDetail) => boolean — Whether a change may be made, asked while a drag moves and before any edit is applied. A function, so it is set from script. js gantt.canChange = ({ edits }) => edits.every(e => !e.edit.start?.endsWith('-25'));
columns columns (BmxGanttColumn | BmxGanttColumnKey)[] | string [...DEFAULT_COLUMNS] The task-list columns, in order: columns="wbs,name,start,duration", or objects with a label and width from script.
editable editable boolean false Lets the reader drag bars, draw links and edit cells.
firstDayOfWeek first-day-of-week number 1 The first day of the week in the headers, 0 for Sunday.
gridWidth grid-width number 460 Width of the task list in pixels. The reader can drag the divider.
holidays holidays string[] | string [] Dates that are not worked, YYYY-MM-DD: holidays="2026-12-25,2026-12-28".
label label string — What the plan is called. Without one it is announced as "Project plan".
links links BmxTaskLink[] | string [] The links between tasks.
locale locale string — The locale dates are written in. Defaults to the page's.
projectStart project-start string — The first day work can happen, YYYY-MM-DD. Defaults to the earliest task start.
rowHeight row-height number 36 Row height in pixels. A layout input: the timeline and the list are placed from it.
showBaselines show-baselines boolean false Draws each task's baseline under its bar.
showCriticalPath show-critical-path boolean true Draws the critical path in the critical colour.
showLinks show-links boolean true Draws the links.
showToday show-today boolean true Draws a line at today.
tasks tasks BmxTask[] | string [] The tasks, as a flat list with parent references.
today today string — Which day is today, YYYY-MM-DD. Defaults to the reader's date.
toolbar toolbar boolean true Shows the zoom and "today" buttons above the chart.
workingDays working-days number[] | string [1, 2, 3, 4, 5] Days of the week that are worked, 0 for Sunday: working-days="1,2,3,4,5".
zoom zoom BmxGanttZoom 'week' How much time the timeline shows: day, week, month, quarter or year.

Events

EventDetailDescription
bmxLinkChange BmxGanttLinkChangeDetail Links are about to change. Cancelable.
bmxSchedule BmxGanttScheduleDetail The plan was scheduled, with anything in it that could not be used.
bmxSelect BmxGanttSelectDetail A task was chosen.
bmxTaskChange BmxGanttTaskChangeDetail Tasks are about to change. Cancelable.
bmxToggle BmxGanttToggleDetail A summary task was opened or closed.
bmxZoomChange { readonly zoom: BmxGanttZoom; } The zoom changed.

Methods

MethodSignatureDescription
collapseAll collapseAll() => Promise<void> Closes every summary.
expandAll expandAll() => Promise<void> Opens every summary.
exportPng exportPng() => Promise<Blob | null> The whole plan as a PNG image, at twice the pixel density for print.
exportSvg exportSvg() => Promise<string> The whole plan - every row, open or not - as a standalone SVG document.
getSchedule getSchedule() => Promise<BmxSchedule> The plan as scheduled: dates, float, the critical path and any problems.
print print() => Promise<void> Prints the whole plan. The chart on screen shows only what is in view, so printing the page would print a window of it; this prints every row.
scrollToTask scrollToTask(id: string) => Promise<void> Scrolls a task into view in both panes, opening its summaries, and focuses its row.
scrollToToday scrollToToday() => Promise<void> Scrolls the timeline to today.
toggle toggle(id: string, expanded?: boolean) => Promise<void> Opens or closes one summary.
zoomIn zoomIn() => Promise<void> Shows less time, in more detail.
zoomOut zoomOut() => Promise<void> Shows more time, in less detail.
zoomToFit zoomToFit() => Promise<void> Picks the finest zoom that shows the whole plan without scrolling sideways.

Slots

SlotDescription
empty what to draw when there are no tasks.
toolbar-end extra controls after the built-in zoom buttons.

CSS shadow parts

PartDescription
bar-label
baseline
cell-editor
chart
empty
frame
grid
grid-body
grid-header
link-draft
link-handle
link-remove
links
non-working
plot
progress
progress-handle
resize-handle
row-band
side-label
splitter
tick
tick-major
timeline-body
timeline-header
today
toggle
toolbar
toolbar-button
zoom-name

CSS custom properties

PropertyDescription
--bmx-gantt-bar-background A task bar's unfinished part.
--bmx-gantt-bar-color Text on a bar.
--bmx-gantt-bar-height How much of the row a bar fills, as a length.
--bmx-gantt-bar-progress A task bar's finished part.
--bmx-gantt-bar-radius A task bar's corners.
--bmx-gantt-baseline-color The baseline under a bar.
--bmx-gantt-border The lines between rows, columns and header ticks.
--bmx-gantt-critical-background A critical bar's unfinished part.
--bmx-gantt-critical-progress A critical bar's finished part, and critical links.
--bmx-gantt-grid-width Width of the task list. Written by the component from grid-width.
--bmx-gantt-header-background The header rows' fill.
--bmx-gantt-header-color The header text.
--bmx-gantt-height How tall the chart is, header included.
--bmx-gantt-link-color The links between bars.
--bmx-gantt-non-working-background Weekends and holidays behind the bars.
--bmx-gantt-row-height Height of a row. Written by the component from row-height.
--bmx-gantt-row-hover A row under the pointer.
--bmx-gantt-row-selected The chosen row, in the list and across the timeline.
--bmx-gantt-summary-color A summary bar and a milestone.
--bmx-gantt-today-color The line at today. @part toolbar - the zoom and "today" buttons. @part toolbar-button - one of them. @part zoom-name - the current zoom's name. @part frame - the task list, the divider and the timeline together. @part grid - the task list (role="treegrid"). @part grid-header - the list's header row. @part grid-body - the list's scrolling body. @part column-header - one heading. Also column-header-<key>. @part row - one task's row. Also row-summary, row-selected. @part cell - one cell. Also cell-<key>. @part cell-editor - the input while a cell is edited. @part toggle - the arrow that opens a summary. @part name - a task's name in the list. Also name-milestone. @part splitter - the divider between the list and the timeline. @part chart - the timeline side. @part timeline-header - the two rows of dates. @part tick - one date in them. Also tick-major, tick-minor, tick-non-working, tick-today. @part timeline-body - the scrolling timeline. @part plot - the drawing surface inside it. @part non-working - a shaded weekend or holiday. @part row-band - a row's stripe across the timeline. @part bar - one bar. Also bar-task, bar-summary, bar-milestone, bar-critical, bar-<category>. @part track - a task bar's unfinished part. @part summary-track - a summary bar. @part progress - a task bar's finished part. @part bar-label - a task's name on its bar. @part side-label - a task's name beside a bar too short to hold it. @part baseline - the baseline under a bar. @part resize-handle - the grip on a bar's end, when editable. @part progress-handle - the grip at the end of the finished part. @part link-handle - the dot a link is drawn from. @part links - the SVG layer of links. @part link - one link. Also link-critical. @part link-draft - the link being drawn. @part link-remove - the button that removes the chosen link. @part today - the line at today. @part empty - what is drawn when there are no tasks.