Skip to content

Gantt Chart

Data visualizationSince 0.7.0
import { Gantt, type GanttTask, type GanttDependency } from '@absoluteui/components/gantt';
import '@absoluteui/components/gantt/style.css';

Using Angular? See Gantt Chart for Angular.

A project schedule. A task tree grid (name, start, end, duration, and optionally progress, resources, float) sits beside a timeline of bars with summary tasks, milestones, progress, baselines, dependency connectors, the critical path, non-working days, and today. An optional histogram shows each resource’s daily load against its capacity.

Tasks are scheduled in working days of a Business Calendar. Dependencies (finish-to-start, start-to-start, finish-to-finish, start-to-finish, with lag or lead in working days) push successors later but never before their requested start (forward scheduling). Manual tasks keep their start and report violated dependencies. A backward pass gives each task’s total float; tasks without float form the critical path.

Editing (with editable): drag a bar to move it, its end to resize it, or its link handle onto another bar to link them. Keyboard: Ctrl/Cmd+Left/Right moves the focused task by a working day, Shift+Left/Right changes its duration. The toolbar offers the same actions plus Link tasks (then Enter on the successor). Every change is dispatched first as the cancelable aui-task-change with the tasks, dependencies, and the new schedule, and is undoable. The engine (scheduleGantt, resourceLoad, createsCycle, assertGantt) is pure and runs without DOM.

  • Planning and tracking projects with dependent tasks, milestones, and deadlines.
  • Finding the critical path and the float of each task after a change.
  • Comparing the current schedule with a baseline, and spotting over-allocated resources.
  • Calendars of meetings and appointments with times of day: use Scheduler.
  • Workflow boards without dates: use Kanban.
  • Simple date ranges on a few rows: use a timeline view of Scheduler or a table.

A project plan with dependencies and a milestone

Section titled “A project plan with dependencies and a milestone”
import { plainDate } from '@absoluteui/core';
import { Gantt, type GanttDependency, type GanttTask } from '@absoluteui/components/gantt';
const tasks: GanttTask[] = [
{ id: 'design', parentId: null, label: 'Design', start: plainDate(2026, 9, 28), duration: 5 },
{ id: 'build', parentId: null, label: 'Build', start: plainDate(2026, 9, 28), duration: 10, progress: 20 },
{ id: 'launch', parentId: null, label: 'Launch', start: plainDate(2026, 9, 28), duration: 0 },
];
const dependencies: GanttDependency[] = [
{ id: 'd1', from: 'design', to: 'build' },
{ id: 'd2', from: 'build', to: 'launch', lag: 2 },
];
const host = document.createElement('div');
document.body.append(host);
const gantt = Gantt.create(host, { label: 'Website relaunch', tasks, dependencies, columns: ['name', 'start', 'end', 'duration', 'progress'] });
document.body.dataset.finish = gantt.schedule.finish ? `${String(gantt.schedule.finish.month)}/${String(gantt.schedule.finish.day)}` : '';

Editing with a holiday calendar, saving every change

Section titled “Editing with a holiday calendar, saving every change”
import { plainDate } from '@absoluteui/core';
import { Gantt, type GanttTask, type GanttTaskChangeDetail } from '@absoluteui/components/gantt';
let saved: GanttTask[] = [
{ id: 'phase', parentId: null, label: 'Phase 1', start: plainDate(2026, 10, 1), duration: 0 },
{ id: 'a', parentId: 'phase', label: 'Research', start: plainDate(2026, 10, 1), duration: 3 },
{ id: 'b', parentId: 'phase', label: 'Prototype', start: plainDate(2026, 10, 1), duration: 4, manual: true },
];
const host = document.querySelector<HTMLElement>('#plan');
if (host) {
Gantt.create(host, {
label: 'Phase plan',
tasks: saved,
dependencies: [{ id: 'ab', from: 'a', to: 'b' }],
editable: true,
scale: 'day',
calendar: { workingDays: [1, 2, 3, 4, 5], holidays: [{ date: plainDate(2026, 10, 5), name: 'Company day' }] },
// Prototype dates are owned by another team.
canEdit: (task) => task.id !== 'b',
});
host.addEventListener('aui-task-change', (event) => {
const detail = (event as CustomEvent<GanttTaskChangeDetail>).detail;
if (detail.schedule.finish && detail.schedule.finish.month > 11) event.preventDefault();
else saved = detail.tasks;
});
}

Baselines, resources, and the load histogram

Section titled “Baselines, resources, and the load histogram”
import { plainDate } from '@absoluteui/core';
import { Gantt } from '@absoluteui/components/gantt';
const host = document.createElement('div');
document.body.append(host);
const gantt = Gantt.create(host, {
label: 'Migration',
tasks: [
{ id: 'plan', parentId: null, label: 'Plan', start: plainDate(2026, 10, 5), duration: 3, assignments: [{ resourceId: 'ana' }], baseline: { start: plainDate(2026, 10, 5), end: plainDate(2026, 10, 6) } },
{ id: 'move', parentId: null, label: 'Move data', start: plainDate(2026, 10, 6), duration: 4, assignments: [{ resourceId: 'ana', units: 50 }, { resourceId: 'ben' }] },
],
resources: [{ id: 'ana', label: 'Ana' }, { id: 'ben', label: 'Ben', capacity: 80 }],
columns: ['name', 'start', 'end', 'resources', 'float'],
showHistogram: true,
});
const overloaded = gantt.resourceLoad().filter((r) => r.overDays > 0).map((r) => r.resourceId);
host.dataset.overloaded = overloaded.join(',');

Schedule on a server or in a worker (no DOM)

Section titled “Schedule on a server or in a worker (no DOM)”
import { plainDate } from '@absoluteui/core';
import { createBusinessCalendar } from '@absoluteui/components/business-calendar';
import { assertGantt, scheduleGantt, type GanttDependency, type GanttTask } from '@absoluteui/components/gantt';
const tasks: GanttTask[] = [
{ id: 'a', parentId: null, label: 'Pour foundation', start: plainDate(2026, 10, 1), duration: 2 },
{ id: 'b', parentId: null, label: 'Cure', start: plainDate(2026, 10, 1), duration: 5 },
];
const dependencies: GanttDependency[] = [{ id: 'ab', from: 'a', to: 'b', type: 'FS', lag: 1 }];
assertGantt(tasks, dependencies, undefined);
const schedule = scheduleGantt(tasks, dependencies, createBusinessCalendar({ workingDays: [1, 2, 3, 4, 5, 6] }));
const cure = schedule.tasks.get('b');
document.body.dataset.cure = cure ? `${String(cure.start.day)}-${String(cure.end.day)}, critical ${String(cure.critical)}` : '';
import { configureAbsoluteUI } from '@absoluteui/core';
configureAbsoluteUI({ components: { gantt: { scale: 'day', rowHeight: 44, historyLimit: 100 } } });
import { plainDate } from '@absoluteui/core';
import { Gantt, type GanttTask, type GanttTaskChangeDetail } from '@absoluteui/components/gantt';
import '@absoluteui/components/gantt/style.css';
let saved: GanttTask[] = [
{ id: 'design', parentId: null, label: 'Design', start: plainDate(2026, 10, 5), duration: 5 },
{ id: 'build', parentId: null, label: 'Build', start: plainDate(2026, 10, 5), duration: 10 },
{ id: 'launch', parentId: null, label: 'Launch', start: plainDate(2026, 10, 5), duration: 0 },
];
const host = document.querySelector<HTMLElement>('#plan');
if (host) {
Gantt.create(host, {
label: 'Website relaunch',
tasks: saved,
dependencies: [{ id: 'd1', from: 'design', to: 'build' }, { id: 'd2', from: 'build', to: 'launch' }],
editable: true,
calendar: { holidays: [{ date: plainDate(2026, 12, 25), name: 'Christmas Day', yearly: true }] },
});
host.addEventListener('aui-task-change', (event) => {
saved = (event as CustomEvent<GanttTaskChangeDetail>).detail.tasks;
});
}

See section 33 and metadata/gantt/examples.json: a plan with dependencies and a milestone, editing with a holiday calendar, baselines and resources with the histogram, the engine without DOM, global configuration.

<div class="aui-gantt [--editable] [--linking] [--day|--week|--month] [--rtl]"> (consumer host)
<div class="aui-gantt__toolbar" role="group" aria-label="{label} controls">
<button class="aui-gantt__tool" data-aui-action="zoomOut|zoomIn|today|earlier|later|shorten|lengthen|link|undo|redo">
<div class="aui-gantt__body"> (scrolls both ways)
<div class="aui-gantt__table" role="treegrid" aria-label="{label}" aria-rowcount aria-colcount> (sticky at inline start)
<div class="aui-gantt__head-group" role="rowgroup"><div class="aui-gantt__head" role="row"> columnheaders
<div class="aui-gantt__rows" role="rowgroup"> (virtual rows)
<div class="aui-gantt__row [--summary] [--critical]" role="row" data-aui-task="build"
aria-rowindex="5" aria-level="1" aria-setsize="4" aria-posinset="2" [aria-expanded] tabindex="0">
<div class="aui-gantt__cell aui-gantt__cell--name" role="gridcell">
<span class="aui-gantt__toggle" aria-hidden="true"> <span class="aui-gantt__name">Build</span>
<span class="aui-gantt__badge aui-gantt__badge--critical">Critical</span>
<span class="aui-gantt__hidden"> Depends on Design (finish to start).</span>
<div class="aui-gantt__cell aui-gantt__cell--start" role="gridcell">Oct 5, 2026</div> ...
<div class="aui-gantt__chart" aria-hidden="true">
<div class="aui-gantt__scale"><div class="aui-gantt__tier"><span class="aui-gantt__tick">...
<div class="aui-gantt__canvas">
<div class="aui-gantt__shade"><div class="aui-gantt__off"> (non-working days)
<div class="aui-gantt__today">
<svg class="aui-gantt__links"><path class="aui-gantt__dependency [--critical] [--violated]">
<div class="aui-gantt__bars">
<div class="aui-gantt__bar aui-gantt__bar--c1 [--summary|--milestone|--critical|--manual|--focus]" data-aui-task="build">
<span class="aui-gantt__fill"><span class="aui-gantt__progress"></span></span>
<span class="aui-gantt__bar-label">Build</span> <span class="aui-gantt__resize"> <span class="aui-gantt__link-handle">
<span class="aui-gantt__baseline">
<div class="aui-gantt__histogram"> (showHistogram)
<div class="aui-gantt__histogram-title" role="heading" aria-level="3">Resource load</div>
<div class="aui-gantt__load-row"><div class="aui-gantt__load-label">Ana <span>Peak 150%, 2 days over capacity</span></div>
<div class="aui-gantt__load-track" aria-hidden="true"><span class="aui-gantt__load [--over]">
<p class="aui-gantt__instructions">...</p> (visually hidden)
Gantt.create(host: HTMLElement, options: GanttOptions): Gantt; scheduleGantt(tasks, dependencies, calendar: BusinessCalendarEngine, index?: WorkIndex): GanttSchedule; resourceLoad(tasks, schedule, resources, calendar, range: { start: PlainDate; end: PlainDate }): GanttResourceLoad[]; createsCycle(dependencies, from, to): boolean; assertGantt(tasks, dependencies, resources): void

Global configuration key: gantt (see Configuration).

Option Type Default Description
label string Accessible name of the tree grid; the toolbar is named ‘{label} controls’. Non-empty. Required.
tasks readonly GanttTask[] Tasks: { id, parentId (summary task id or null), label, start (requested start, PlainDate), duration (working days, 0 = milestone), progress? (0..100), manual?, assignments? ({ resourceId, units? percent }), baseline? ({ start, end } inclusive), color? (1..8), readOnly?, collapsed? }. Tasks with children are summary tasks (their start and duration are ignored). Order within a parent is the array order. Validated (GANTT_OPTIONS). Replacing it clears the undo history. Required.
dependencies readonly GanttDependency[] { id, from (predecessor), to (successor), type? (‘FS’ default, ‘SS’, ‘FF’, ‘SF’), lag? (working days, negative = lead) }. Leaf tasks only, one per pair, no cycles (the error names the cycle).
resources readonly GanttResource[] { id, label, capacity? (percent per working day, default 100) }; assignments must reference them.
calendar BusinessCalendarDefinition Working days, holidays, and exceptions (default Monday to Friday). Durations and lags count its working days; non-working days are shaded.
editable boolean false Pointer, keyboard, and toolbar editing (move, resize, link).
columns readonly GanttColumnKey[] "['name', 'start', 'end', 'duration']" Grid columns: ‘name’ first, then any of ‘start’, ‘end’, ‘duration’, ‘progress’, ‘resources’, ‘float’ (unique).
showCritical boolean true Mark critical tasks (badge, hatched bar, red connectors).
showBaselines boolean true Draw baselines and describe baseline variance in the grid.
showHistogram boolean false Show the resource load histogram below the chart (needs resources): daily load per resource with peak and over-capacity days as text.
today PlainDate Date of the today line and the Go to today button (default: the device’s local date).
canEdit (task: Readonly<GanttTask>) => boolean Veto edits of a task (also as a link successor). Summary and read-only tasks are never edited.
scale 'day' | 'week' | 'month' "week" Timeline scale (32, 16, or 5 CSS px per day); also changed by the zoom buttons and setScale(). Globally configurable.
rowHeight number 36 Row height in CSS px (24..96). Rows have a fixed height (virtual rows). Globally configurable.
historyLimit number 50 Undo depth (0..500). Globally configurable.
locale string "en-US" Dates, numbers, the first day of the week (week scale), plural forms, and messages. Globally configurable. Inherited from common configuration.
direction 'ltr' | 'rtl' "ltr" Mirrors the timeline and swaps ArrowLeft/ArrowRight. Globally configurable. Inherited from common configuration.
Property Type Description
host HTMLElement Consumer host.
element HTMLDivElement The tree grid (role=treegrid).
tasks GanttTask[] Every task (copies) with requested starts.
dependencies GanttDependency[] Every dependency (copies).
schedule GanttSchedule Scheduled dates, float, critical flags, violations, and variance per task; project start and finish; critical ids.
businessCalendar BusinessCalendarEngine The working-day engine in use.
scale GanttScale Current scale.
editable boolean Whether editing is on.
expanded string[] Expanded summary task ids.
canUndo boolean A change can be undone.
canRedo boolean An undone change can be redone.
Method Description
update(options: Partial<GanttOptions>): void Merge and apply options; new tasks or dependencies clear the history (expansion of known summaries is kept).
setScale(scale: GanttScale): void Change the scale, keeping the first visible date; emits aui-scale-change.
setExpanded(id: string, expanded: boolean): void Expand or collapse a summary task (emits aui-expand-change).
expandAll(): void Expand every summary task.
collapseAll(): void Collapse every summary task.
scrollToTask(id: string, options?: { focus?: boolean }): void Expand the ancestors, scroll the row and bar into view, and focus the row (unless focus is false).
scrollToDate(date: PlainDate): void Center the timeline on a date.
focusTask(id: string): void scrollToTask with focus.
moveTask(id: string, start: PlainDate): boolean Change the requested start (next working day). Emits aui-task-change; false when refused or cancelled.
setDuration(id: string, duration: number): boolean Change the duration in working days (0 = milestone).
addDependency(dependency: GanttDependency): boolean Add a dependency (validated; throws GANTT_OPTIONS for cycles or summary tasks); false when the event is cancelled.
removeDependency(id: string): boolean Remove a dependency.
resourceLoad(range?: { start: PlainDate; end: PlainDate }): GanttResourceLoad[] Daily load per resource (default: the timeline range).
undo(): boolean Undo the last change (emits aui-task-change with reason ‘undo’).
redo(): boolean Redo the last undone change.
destroy(): void Remove listeners and the rendered chart.
Event Detail Bubbles Cancelable Description
aui-task-change { reason: 'move' | 'resize' | 'link' | 'unlink' | 'undo' | 'redo'; changes: GanttTaskChange[]; dependency: GanttDependency | null; tasks: GanttTask[]; dependencies: GanttDependency[]; schedule: GanttSchedule } Yes Yes Before a change is applied; preventDefault() keeps the previous state.
aui-task-activate { id: string; task: GanttTask } Yes No Enter on a row.
aui-expand-change { id: string; expanded: boolean } Yes No A summary task was expanded or collapsed.
aui-scale-change { scale: GanttScale } Yes No The scale changed (zoom buttons or setScale).
Area Description
none Rows and bars render task data; no custom content slots.

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-gantt | --editable | --linking | --day | --week | --month | --rtl Host and modifiers; –aui-gantt-table-width, –aui-gantt-height, –aui-gantt-tier size the chart (–aui-gantt-row and –aui-gantt-day are set by the component).
aui-gantt__toolbar | __tool Toolbar.
aui-gantt__body | __table | __head-group | __head | __rows | __row (--summary, --critical, --link-source) | __cell (--name, --start, --end, --duration, --progress, --resources, --float) | __toggle | __name | __badge (--milestone, --critical) Tree grid.
aui-gantt__chart | __scale | __tier | __tick (--off) | __canvas | __shade | __off | __today | __links | __dependency (--critical, --violated) | __arrow Timeline.
aui-gantt__bars | __bar (--c1..--c8, --summary, --milestone, --critical, --manual, --editable, --focus, --link-source, --dragging, --drop, --no-drop) | __fill | __progress | __bar-label | __baseline | __resize | __link-handle Bars.
aui-gantt__histogram | __histogram-title | __load-row | __load-label | __load-name | __load-summary | __load-track | __load (--over) Resource histogram.
Slot Default icon
zoom out / zoom in zoom-out / zoom-in
today locate
earlier / later chevron-left / chevron-right
shorten / lengthen minus / plus
link link
undo / redo undo / redo
expand chevron-right / chevron-down

Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.

--aui-border-width | --aui-border-width-strong | --aui-color-border | --aui-color-border-strong | --aui-color-chart-1 | --aui-color-chart-2 | --aui-color-chart-3 | --aui-color-chart-4 | --aui-color-chart-5 | --aui-color-chart-6 | --aui-color-chart-7 | --aui-color-chart-8 | --aui-color-chart-axis | --aui-color-error-fg | --aui-color-focus-ring | --aui-color-hover | --aui-color-primary | --aui-color-selected | --aui-color-selected-text | --aui-color-surface | --aui-color-surface-sunken | --aui-color-text | --aui-color-text-disabled | --aui-color-text-muted | --aui-color-warning-fg | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-sm | --aui-font-size-xs | --aui-font-weight-medium | --aui-font-weight-semibold | --aui-icon-size-sm | --aui-line-height-tight | --aui-radius-md | --aui-radius-pill | --aui-radius-sm | --aui-shadow-md | --aui-space-1 | --aui-space-2 | --aui-touch-target-min

type GanttDependencyType = 'FS' | 'SS' | 'FF' | 'SF';
interface GanttAssignment { resourceId: string; units?: number }
interface GanttBaseline { start: PlainDate; end: PlainDate }
interface GanttTask { id: string; parentId: string | null; label: string; start: PlainDate; duration: number; progress?: number; manual?: boolean; assignments?: readonly GanttAssignment[]; baseline?: GanttBaseline; color?: GanttColor; readOnly?: boolean; collapsed?: boolean }
interface GanttDependency { id: string; from: string; to: string; type?: GanttDependencyType; lag?: number }
interface GanttResource { id: string; label: string; capacity?: number }
interface GanttScheduledTask { id: string; start: PlainDate; end: PlainDate; duration: number; summary: boolean; milestone: boolean; totalFloat: number; critical: boolean; driven: boolean; violations: readonly string[]; variance: number | null }
interface GanttSchedule { tasks: ReadonlyMap<string, GanttScheduledTask>; start: PlainDate | null; finish: PlainDate | null; critical: readonly string[] }
interface GanttResourceLoad { resourceId: string; days: readonly { date: PlainDate; load: number; capacity: number; over: boolean }[]; overDays: number; peak: number }
type GanttScale = 'day' | 'week' | 'month';
type GanttColumnKey = 'name' | 'start' | 'end' | 'duration' | 'progress' | 'resources' | 'float';
interface GanttTaskChange { id: string; start?: PlainDate; duration?: number }
interface GanttTaskChangeDetail { reason: 'move' | 'resize' | 'link' | 'unlink' | 'undo' | 'redo'; changes: GanttTaskChange[]; dependency: GanttDependency | null; tasks: GanttTask[]; dependencies: GanttDependency[]; schedule: GanttSchedule }

Pattern: WAI-ARIA APG Treegrid with row focus: rows carry aria-level, aria-setsize, aria-posinset, aria-expanded (summary tasks), and aria-rowindex (virtual rows); one roving tab stop. The grid is the accessible surface: every value a bar shows (dates, duration, milestone, critical, dependencies, violations, baseline variance) is text in its row. The timeline and histogram tracks are aria-hidden pointer surfaces with keyboard and toolbar equivalents for every action (WCAG 2.5.7).

Key Context Action
Tab / Shift+Tab Chart Toolbar buttons, then the grid (one tab stop: the focused row).
ArrowDown / ArrowUp Row focused Next / previous visible task (the grid scrolls; virtual rows render).
PageDown / PageUp, Home / End Row focused One page down / up; first / last task.
ArrowRight (ArrowLeft in RTL) Row focused Expand a collapsed summary; on an expanded one, move to its first child.
ArrowLeft (ArrowRight in RTL) Row focused Collapse an expanded summary; otherwise move to the parent.
Enter Row focused Activate (aui-task-activate); while linking, make this task the successor (or remove the existing link between the two).
Ctrl/Cmd+ArrowRight / Ctrl/Cmd+ArrowLeft Row focused (editable) Move the task one working day later / earlier (reading direction in RTL). Announces the new dates, or that dependencies prevent an earlier start.
Shift+ArrowRight / Shift+ArrowLeft Row focused (editable) Lengthen / shorten by one working day (at least one day).
Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z or Ctrl/Cmd+Y Row focused (editable) Undo / redo.
Escape Linking Cancel linking.
Element Role
toolbar <div> group, named ‘{label} controls’
toolbar <button>s button (native); Link is a toggle (aria-pressed)
grid <div> treegrid, named by aria-label, described by the instructions
header and body groups rowgroup
header row / cells row / columnheader
task row / cells row / gridcell
timeline, connectors, bars none (aria-hidden=true)
histogram title / summaries heading (level 3) / text; tracks are aria-hidden
Attribute Element Value
aria-label / aria-describedby treegrid label / visually hidden instructions
aria-rowcount / aria-colcount treegrid Visible rows + 1 (header) / column count
aria-rowindex row 1-based index including the header (rows are virtual)
aria-level / aria-setsize / aria-posinset task row Depth, siblings, position
aria-expanded summary row true / false (absent on leaf tasks)
aria-colindex cells 1-based column
tabindex task row 0 on the focused row, -1 on the others
aria-pressed / aria-disabled toolbar buttons Link while picking a successor / true when an action is not possible (buttons stay focusable)
live region shared live regions Polite: new dates after a change, scale, undone, redone, linking cancelled. Assertive: linking instructions, linked, unlinked, refused, cannot be changed, limited by dependencies.

One roving tab stop over the rows; the focused row stays rendered while the grid scrolls (virtual rows). Collapsing a summary moves focus to it from a hidden child. Changes keep focus on the edited row; toolbar actions apply to the focused row and leave focus on the button. Clicking a bar focuses its row.

Criterion Level Note
1.3.1 Info and Relationships A
1.4.1 Use of Color A
1.4.10 Reflow AA two-dimensional chart scrolls in its own container
1.4.11 Non-text Contrast AA
2.1.1 Keyboard A
2.1.4 Character Key Shortcuts A only modifier shortcuts
2.4.3 Focus Order A
2.4.7 Focus Visible AA
2.5.7 Dragging Movements AA
2.5.8 Target Size (Minimum) AA
4.1.2 Name, Role, Value A
4.1.3 Status Messages AA

Automated test results: Accessibility and compliance.

  • Critical tasks are marked with the text ‘Critical’ in the grid and a hatched bar; milestones with ‘Milestone’.
  • Dependencies and baseline variance are visually hidden text in the name cell, read with the row.
  • The histogram’s peak load and over-capacity days are visible text next to each resource.
  • Pointer drags are not announced until the change is applied.
  • Manual screen-reader verification (NVDA, JAWS, VoiceOver) is pending.

Row focused; summary expanded or collapsed; critical, milestone, manual, read-only task; violated dependency; baseline; linking; dragging (move, resize, link) over a valid or invalid target; day, week, or month scale; histogram shown.

From Trigger To
row focused Ctrl/Cmd+Arrow, toolbar Earlier/Later, bar drag aui-task-change (move) -> rescheduled; announced
row focused Shift+Arrow, toolbar Shorten/Lengthen, end drag aui-task-change (resize) -> rescheduled
row focused toolbar Link linking (source marked)
linking Enter or click on another task linked (link), or unlinked when the pair is linked, or refused (cycle, summary)
linking Escape, Link again, Enter on the source cancelled
any change preventDefault() on aui-task-change unchanged
changed Ctrl/Cmd+Z / Undo previous tasks and dependencies (undo)
  • Every pointer action has a keyboard and toolbar equivalent (WCAG 2.5.7); shortcuts use modifiers (2.1.4).
  • Critical tasks and milestones are labelled with text; dependencies (“Depends on Design (finish to start).”) and baseline variance (“2 working days behind baseline.”) are visually hidden text read with the row.
  • Critical bars use a hatch pattern and a thicker border, not color alone.
  • Manual screen-reader verification (NVDA, JAWS, VoiceOver) is pending.

The body has a fixed block size (--aui-gantt-height) and scrolls in two dimensions (WCAG 1.4.10 exception) while the page reflows at 320 px. The grid stays at the inline start while the timeline scrolls; in containers narrower than 40rem it scrolls away with the timeline. Headers are sticky. Toolbar buttons meet touch-target-min; bars and handles use touch-action: none.

direction: 'rtl' mirrors the timeline (later dates to the left, mirrored connectors) and swaps ArrowLeft / ArrowRight for expand, collapse, moving, and resizing (the arrow toward later dates moves later).

Bars are tints of color-chart-1..8 with solid borders and progress fills; critical bars add a color-error-fg border and hatch; manual tasks have dashed borders; summaries are color-text brackets; milestones are diamonds; baselines are thin color-text-muted bars. Forced colors: Canvas/CanvasText bars, Mark for critical, Highlight for focus and the link source, hatched non-working days.

configureAbsoluteUI({ components: { gantt: { scale: 'day', rowHeight: 44, historyLimit: 100 } } });

Global options: scale, rowHeight, historyLimit, plus inherited locale and direction.

Instance options override global defaults. All options are dynamic through update(); tasks and dependencies clear the undo history; calendar reschedules.

Dates are PlainDate values without a timezone; durations and lags count working days of the calendar. The default today is the device’s local date; pass today for a fixed date (for example the server’s date).

Toolbar: zoom-out, zoom-in, locate (today), chevron-left / chevron-right (earlier / later), minus / plus (shorten / lengthen), link, undo, redo. Expand: chevron-right (chevron-left in RTL) / chevron-down.

Text renders through textContent. Options are validated (GANTT_OPTIONS) before use: unique ids, known parents and resources, no parent cycles, dependencies between leaf tasks, one per pair, and no dependency cycles (the message names the cycle). Positions and sizes are written through the CSSOM; connectors are SVG paths built from numbers (CSP-safe).

No persistence, network access, or telemetry. Changes are reported through aui-task-change for the application to save: tasks (requested starts and durations), dependencies, and schedule (computed dates). Store the requested starts; do not write scheduled dates back into start (see anti-patterns).

Scheduling is linear in tasks plus dependencies (topological order, forward and backward passes) on a working-day index that maps dates to consecutive integers in constant time. Rows have a fixed height and only the rows in view (plus an overscan and the focused row) are rendered, with their bars and the connectors that touch them. Budgets (e2e): create with 2,000 tasks and 1,500 dependencies < 150 ms; a keyboard move with 2,000 tasks < 60 ms; scheduleGantt for 10,000 tasks < 80 ms.

Tested with 2,000 tasks in the chart and 10,000 in the engine. Timeline columns are not virtualized: several years on the day scale render long scales (use the week or month scale).

  • A start on a non-working day moves to the next working day.
  • Moving an auto-scheduled task earlier than its dependencies allow keeps it where it is and announces why.
  • Manual tasks keep their start; unmet dependencies are dashed connectors and “Dependency not met” text.
  • A summary task spans its children, ending on a trailing milestone’s date; summaries cannot be edited or linked.
  • Linking two tasks that are already linked (either direction) removes the link.
  • Negative lags (leads) can start a successor before its predecessor ends.
  • Collapsing a summary moves focus from a hidden child to the summary.
Code Category Cause
GANTT_OPTIONS configuration Empty label; invalid tasks (ids, labels, dates, durations, progress, colors, baselines, assignments), parent cycles, unknown parents or resources; invalid dependencies (unknown or summary tasks, self links, duplicate pairs, invalid types or lags, cycles); invalid columns or flags; methods called with unknown ids.
BUSINESS_CALENDAR_DEFINITION configuration Invalid calendar.
BUSINESS_CALENDAR_NO_WORKING_DAYS runtime The calendar has no working day at all.

A failed construction leaves the host untouched.

  • Whole working days only (no hours).
  • Dependencies between leaf tasks only.
  • One constraint type: start no earlier than the requested start.
  • Resource load is shown, not leveled.
  • Progress is not edited in the chart; pointer drags are announced only when applied.
  • Manual screen-reader verification is pending.
  • Adding, deleting, renaming, indenting, or reordering tasks inside the chart.
  • Several calendars (per resource or task).
  • Printing and export; split, recurring, or cost-tracked tasks.