Gantt
Project-scheduling chart component (data family): a WBS row tree (parent summary bars span the union of their children's time) against a horizontal time axis, with three bar forms (task / milestone / summary) plus progress fill; dependency links (FS/SS/FF/SF, drawn on a single SVG overlay); a drag trio (drag to reschedule, edge handles to resize, in-bar handle to change progress — written back once on pointerup with events); six time scales (hour/day/week/month/quarter/year) with a two-tier header; today line and weekend/holiday column highlights; row virtualization for tens of thousands of tasks; three readonly levels plus per-task disabling. The data contract is a flat parent pointer list (nested children input is flattened automatically).
Basic Usage
WBS row tree + dependency links + milestones + progress (tasks JSON; flat parent / nested children both accepted)
- Parent tasks render summary bars (time = union of children, progress = duration-weighted average); the row toggle collapses/expands children; the expanded set is written back to the
expandedattribute (JSON array) withoas-expand-change; collapsed sub-rows hide together with their dependency links (links follow row visibility, they do not reroute to the summary bar); - Milestones render as diamonds: an explicit
type: "milestone"takes precedence (a task renders as a milestone even when it carriesend; theendis ignored with a one-timeconsole.warnabout the data conflict); tasks withoutendalso infer as milestones; a task with children always renders as a summary bar (children carry the aggregation semantics, so form and aggregation must agree; a one-timeconsole.warnabout the data conflict fires only whentype: "milestone"is also declared explicitly); astartafterend(reversed dates) is swapped automatically at the sanitize layer with a one-timeconsole.warn(no 2px ghost bar); dependenciesdeclare links (typedefaults tofs;fs/ss/ff/sfsupported) drawn as polylines with arrowheads on a single SVG canvas;- Hovering a bar shows a tooltip (name + range + progress);
template[slot="tooltip"]overrides the content (bind points[data-task-name]/[data-task-range]/[data-task-progress]).
Time Scales (scale)
Six scales: hour / day (default) / week / month / quarter / year (two-tier header: merged major tier + per-unit minor tier)
Changing scale re-lays the time axis (column width adapts per tier, with a floor that keeps labels legible) and dispatches oas-scale-change; invalid values fall back to day. When the container squeezes columns too narrow for labels, minor ticks are thinned by N (grid lines kept, some texts hidden — e.g. every 2nd day on a narrow day axis) so day/month numbers never collide. Dragging snaps to the current scale unit (snap="false" disables snapping and shifts by pixel ratio, preserving time precision).
Drag Trio
Drag body to reschedule / edge handles to resize / dot handle for progress (written back once on pointerup; Esc or pointercancel cancels with zero events)
- Drag the body = move as a whole (duration kept); drag edge handles = change start/end (cannot cross the opposite side); drag the in-bar dot = change progress;
- Written back on pointerup with
oas-task-change(detail: { id, task, oldStart, oldEnd, start, end }) /oas-progress-change(detail: { id, task, progress }) plusoas-tasks-change(full list); releasing without movement dispatches nothing; - Esc (or pointercancel) while dragging cancels → visual rollback, zero events; rewriting
tasksexternally mid-drag aborts the drag (external data wins); - With a bar focused, Shift + ←/→ reschedules by one unit (mirrored in RTL).
Readonly Levels + Per-task Disable
readonly (all) / dates-readonly (no rescheduling) / progress-readonly (no progress drag); readonly states render no drag handles (visual = semantics)
disabled: true in task data disables that single bar (others unaffected).
Today Line & Weekend/Holiday Highlights
Today line (show-today on by default) + weekend column highlights (weekends on by default) + holidays (demo range dynamically covers today)
Today line off / weekend highlights off / holiday highlights (holidays JSON)
The today line is decorative (aria-hidden) and renders only when today falls inside the axis range; weekend/holiday column tints apply on the day/hour tiers.
Row Virtualization (10k-scale tasks)
height + row-height enable viewport windowing (300 rows demoed; only the visible window + buffer renders)
Methods: scrollToTask(id) to bring a task row into view, scrollToDate(date) / scrollToToday() for horizontal positioning, updateTask(id, patch) for single-task incremental updates (sanitized, dispatches oas-tasks-change).
Controlled Expansion / Labels & Snapping / Localization
expanded as a controlled set (JSON array = expanded set; explicit [] collapses all) + label-position (default none, since the row-name column already shows names; inside / right must be opted in)
snap=false (pixel-ratio shift keeps time precision) + locale / first-day-of-week
template[slot="task"] overrides in-bar label content ([data-task-name] binding; labels are not rendered by default with label-position="none", so the template takes effect only after opting in); each rendered bar dispatches oas-task-render (detail: { task, element }) so hosts can rewrite its DOM.
API
oas-gantt
Attributes
| Attribute | Description | Type | Default |
|---|---|---|---|
dates-readonly | No rescheduling/resizing (progress still draggable) | boolean | — |
expanded | Expanded set (JSON array, controlled): defaults to fully expanded when absent; explicit [] collapses all; written back on toggle | string | — |
first-day-of-week | Week start day (0-6); defaults to locale derivation (used for week-tier alignment) | string | — |
height | Component height in px (default 320) | string | 320 |
holidays | Holiday list (JSON ["YYYY-MM-DD",…]): matching columns highlighted (takes precedence over weekend tint) | string | — |
label-position | Bar label position: inside / right / left; default none (the row-name column already shows the name, so the bar does not repeat it); invalid values fall back to none | string | — |
locale | Localization (overrides config-provider injection and the global locale) | string | — |
progress-readonly | No progress dragging (dates still draggable) | boolean | — |
readonly | Whole chart read-only (no rescheduling/progress, drag handles not rendered) | boolean | — |
row-height | Row height in px (default 36, minimum 20) | string | 36 |
scale | Time scale: hour / day (default) / week / month / quarter / year; invalid values fall back to day | string | — |
show-today | Today line (on by default; false to disable) | string | — |
snap | Snap drags to the current scale unit (on by default; false shifts by pixel ratio and keeps time precision) | string | — |
tasks | Gantt tasks (JSON): [{ id?, name, start, end?, type?, progress?, parent?, children?, dependencies?, color?, disabled?, expanded? }] — flat parent pointers are the primary contract; nested children are flattened automatically; explicit type:"milestone" takes precedence (rendered as milestone even with end, which is ignored) but a task with children is always a summary; no end also infers milestone; start/end are swapped automatically when start is after end | GanttTask[] | [] |
weekends | Weekend column highlights (on by default; applies on day/hour tiers; false to disable) | string | — |
Property (JS property; see each row for attribute reflection)
| Property | Description | Type | Default |
|---|---|---|---|
tasks | Gantt tasks (GanttTask[]): el.tasks reads (copy; nested input already flattened to parent pointers); el.tasks = [...] writes (syncs the tasks attribute + re-renders + dispatches oas-tasks-change) | GanttTask[] | [] |
Events
| Event | Description |
|---|---|
oas-expand-change | Dispatched on row tree collapse/expand, detail: |
oas-progress-change | Dispatched when a progress handle drag settles, detail: |
oas-scale-change | Dispatched when the scale attribute changes, detail: { scale }; not dispatched on initial absorption |
oas-task-change | Dispatched when a drag/resize/keyboard reschedule settles (pointerup), detail: { id, task, oldStart, oldEnd, start, end }; not dispatched on zero movement |
oas-task-click | Dispatched on task bar/milestone click, detail: |
oas-task-dblclick | Dispatched on task bar double-click, detail: { id, task } — hosts attach an edit form |
oas-task-render | Dispatched after each task bar renders, detail: { task, element } — hosts may rewrite bar DOM |
oas-tasks-change | Dispatched on task data change (programmatic tasks write / updateTask / drag write-back), detail: |
Slots
| Name | Description |
|---|---|
template[slot="empty"] | Empty-state content (defaults to "No tasks") |
template[slot="task"] | Task bar label template (cloned into the bar; [data-task-name] nodes get the task name) |
template[slot="tooltip"] | Hover tooltip template ([data-task-name] / [data-task-range] / [data-task-progress] bind points) |
CSS Variables
| CSS Variable | Default |
|---|---|
--oas-gantt-bar-bg | color-mix(in srgb, var(--oas-gantt-bar-color, var(--oas-color-primary)) 22%, transparent) |
--oas-gantt-bar-height | 22px |
--oas-gantt-bar-progress-bg | var(--oas-gantt-bar-color, var(--oas-color-primary)) |
--oas-gantt-grid-line-color | var(--oas-color-border) |
--oas-gantt-header-bg | var(--oas-color-bg) |
--oas-gantt-header-height | 44px |
--oas-gantt-height | 320px |
--oas-gantt-holiday-bg | color-mix(in srgb, var(--oas-color-warning) 12%, transparent) |
--oas-gantt-link-color | var(--oas-color-primary) |
--oas-gantt-list-width | 220px |
--oas-gantt-milestone-color | var(--oas-color-warning) |
--oas-gantt-row-height | 36px |
--oas-gantt-summary-bg | var(--oas-color-text-secondary-strong) |
--oas-gantt-today-color | var(--oas-color-danger) |
--oas-gantt-weekend-bg | color-mix(in srgb, var(--oas-color-bg-hover) 60%, transparent) |