Temporeum Timelinev1.0.2

Gantt charts

Dependency arrows between tasks, how far each task has got, the plan it is measured against, milestones — and the table of tasks beside the chart. All of it is a plugin on its own entry point, @temporeum/timeline-element/gantt (and, under it, the core's @temporeum/timeline-core/gantt): a page that shows a plain timeline does not download it, and a timeline without gantt draws exactly what it drew before.

The plugin

import { createTimeline, registerPlugin } from "@temporeum/timeline-element";
import { ganttPlugin, mountTaskGrid } from "@temporeum/timeline-element/gantt";

// The Gantt chart is a plugin: registered once, before the first timeline.
registerPlugin(ganttPlugin);

From the running example example/gantt

registerPlugin is the page's, not one timeline's: every instance, every <temporeum-timeline> and the server renderer draw with what is registered. Once is enough. A configuration with gantt and no plugin throws where the timeline is created, naming the import that is missing. The <script>-tag build (timeline-element.umd.js) registers every plugin on load.

Dependencies

const dependencies = [
  { from: "spec", to: "design" },
  { from: "spec", to: "api", type: "SS", lagMs: 2 * day },
  { from: "design", to: "ui" },
  { from: "api", to: "test" },
  { from: "ui", to: "test", type: "FF", lagMs: 3 * day },
  { from: "test", to: "launch" },
];

From the running example example/gantt

A dependency joins two items, from the predecessor to the successor. Its type names the two edges — the first letter the predecessor's, the second the successor's:

typeLeavesLands on
FS (default)the predecessor's finishthe successor's start
SSthe predecessor's startthe successor's start
FFthe predecessor's finishthe successor's finish
SFthe predecessor's startthe successor's finish

Live example: A Gantt chart: dependencies, progress, baseline, milestone, task table

Loading…

Run “A Gantt chart: dependencies, progress, baseline, milestone, task table” on your own machine
  1. Make an empty folder and put this example’s two files in it:

    index.html — the page the code runs in, its markup and styles:

    <div class="gantt" style="display: grid; grid-template-columns: 260px 1fr; height: 360px">
      <div id="grid"></div>
      <div id="timeline" style="height: 360px"></div>
    </div>
    <p class="log" id="log" aria-live="polite">Tab to the timeline and press ] and [ to follow the arrows — or walk the table with the arrows and press Enter.</p>
    

    main.js — the code, as it is in the editor now, your edits included:

  2. In a terminal in that folder, install the library and Vite (it lets the browser follow the imports), then start it:

    npm init -y
    npm install @temporeum/timeline-element
    npm install --save-dev vite
    npx vite
  3. Open the address it prints, http://localhost:5173/.

  • Arrows are routes. Each one runs straight out of its edge, across, and straight into the other edge, turning back through the gap after the predecessor's row when the successor's edge is behind it. The routes are the same in a vertical timeline, turned.
  • Under the items. The arrows are a layer of their own, right under the items, and they move with them: a pan or a zoom does not redraw them.
  • Only what can be seen is drawn. At 10 000 dependencies a frame draws the arrows that cross the view, and no others. An arrow is kept whenever any part of it could be on screen.
  • A lag is said, not drawn. lagMs is part of the item's accessible name ("lag 2 d", "lead 1 h"); the arrow still joins the two edges. The timeline does not move a task to honour a dependency: that is the scheduler's job, and saving is yours.
  • An arrow is not an item. It takes no pointer and has no focus stop: everything it says is in the names of the two items it joins.

Progress, baseline and milestones

  { id: "design", label: "Design", start: at(5), end: at(12), progress: 0.6, baseline: { start: at(4), end: at(10) } },

From the running example example/gantt

FieldDrawn as
progress (0 to 1)The done part of the bar, filled.
baseline ({ start, end? })A thin bar under the item over the planned span, so a slip is a visible gap.
kind: "milestone"A diamond on the item's start. Arrows end on the diamond.

Each item's accessible name says it too: "Design, Apr 11, 2026 – Apr 18, 2026, Design, 60% done, 2 d behind plan, after Spec (finish-to-start); before UI (finish-to-start)".

The keyboard

KeyDoes
]Moves the focus to a successor of the focused item, bringing it into view. Pressed again, to the next successor of the same item, round the list.
[The same, for predecessors.

The brackets are read as the keyboard types them: on most layouts outside the US that is with AltGr, or Option on a Mac, and that works. Ctrl or Cmd with a bracket is left to the browser. Every other key of the timeline works as before — see accessibility.

The task table

mountTaskGrid(document.querySelector("#grid"), timeline, { ariaLabel: "Release tasks", focusTarget: element });

From the running example example/gantt

mountTaskGrid(container, timeline, options) draws the table in an element of its own, which you place beside the timeline — here a CSS grid of two columns. It is not drawn over the timeline: it would cover the items.

  • One row per lane, on the lane's line. The rows are placed from the timeline's own layout and its scroll, so they stay with their lanes through every scroll, collapse and reorder. One task per row is one item per lane.
  • Virtualized. Only the rows in view, and a few beyond, are in the document. A table of 10 000 rows holds about as many nodes as the view has rows; aria-rowcount and aria-rowindex still tell a screen reader where it is.
  • Columns. columns: [{ id, header, width?, value(row) }], where row is { lane, items }. The default is defaultTaskColumns(): name, start, end and progress.
  • Keys and pointer. Tab into the table; the arrows walk the rows (up and down beside a horizontal timeline, left and right beside a vertical one), Home, End, Page Up and Page Down jump, and Enter — or a click on a row — moves the timeline's focus to the row's first item and the keyboard to focusTarget. The wheel over the table scrolls the lanes.
  • Your own table. The rows are taskGridRows(layout, { crossInset, crossOffset, crossSize }) from the entry point, from timeline.layout and the snapshot's crossOffset; timeline.scrollLanes(delta), revealLane(id) and focusItem(id) are what the table calls.

What it does not do yet

  • Scheduling on its own. Nothing moves a task when its predecessor moves; the arrows follow the items wherever you put them. The scheduler (/schedule) puts them back in order — dependencies, constraints, working calendars, the critical path — when you call it.
  • Editing dependencies with the pointer. Dependencies are your data, given in gantt.dependencies; dragging an arrow from one task to another is not built in.