Docs Components Advanced

Gantt

React example follows below.
ts
/**
* Gantt's pinned demo state (CSP-D3).
*
* Deliberately overlapping spans: `start`/`end` are quarter fractions on the
* default four-column axis, and every pair of adjacent rows shares part of
* its range, so the demo proves each row draws its own independent track
* rather than a single shared timeline. `today` sits inside that overlap
* (2.35 of 4 = 58.75%) so the marker rule lands mid-chart, not on a divider.
*
* `bar`, not `color`: the fixture set forbids a `color` key (it is one of the
* theme-coupled names the proof test rejects), while `GanttRow`'s own field
* is called `color`. `ganttRows()` below does that one rename, once, instead
* of five demo files each spreading their own mapping.
*/
const ganttFixture = {
id: "gantt",
labelHeading: "Workstream",
today: 2.35,
rows: [
{ name: "Ledger v2 rewrite", sub: "platform", start: 0, end: 1.9, bar: "var(--accent)" },
{ name: "Billing migration", sub: "payments", start: 1.2, end: 2.8, bar: "var(--vs-info)" },
{ name: "Fraud rules engine", sub: "risk", start: 2.4, end: 4, bar: "var(--vs-warning)" },
{ name: "Partner API GA", sub: "integrations", start: 0.6, end: 3.2, bar: "var(--vs-success)" },
],
} satisfies {
id: string;
labelHeading: string;
today: number;
rows: { name: string; sub: string; start: number; end: number; bar: string }[];
};
/** Fixture rows in the shape every adapter's `rows` prop takes. */
function ganttRows(): {
name: string;
sub: string;
start: number;
end: number;
color: string;
}[] {
return ganttFixture.rows.map(({ bar, ...row }) => ({ ...row, color: bar }));
}
import { Gantt } from "@grassroot/ui-react/advanced";
export default function GanttDemo() {
return (
<div className="w-full min-w-0">
<Gantt
rows={ganttRows()}
today={ganttFixture.today}
labelHeading={ganttFixture.labelHeading}
/>
</div>
);
}
ts
<script setup lang="ts">
/**
* Gantt's pinned demo state (CSP-D3).
*
* Deliberately overlapping spans: `start`/`end` are quarter fractions on the
* default four-column axis, and every pair of adjacent rows shares part of
* its range, so the demo proves each row draws its own independent track
* rather than a single shared timeline. `today` sits inside that overlap
* (2.35 of 4 = 58.75%) so the marker rule lands mid-chart, not on a divider.
*
* `bar`, not `color`: the fixture set forbids a `color` key (it is one of the
* theme-coupled names the proof test rejects), while `GanttRow`'s own field
* is called `color`. `ganttRows()` below does that one rename, once, instead
* of five demo files each spreading their own mapping.
*/
const ganttFixture = {
id: "gantt",
labelHeading: "Workstream",
today: 2.35,
rows: [
{ name: "Ledger v2 rewrite", sub: "platform", start: 0, end: 1.9, bar: "var(--accent)" },
{ name: "Billing migration", sub: "payments", start: 1.2, end: 2.8, bar: "var(--vs-info)" },
{ name: "Fraud rules engine", sub: "risk", start: 2.4, end: 4, bar: "var(--vs-warning)" },
{ name: "Partner API GA", sub: "integrations", start: 0.6, end: 3.2, bar: "var(--vs-success)" },
],
} satisfies {
id: string;
labelHeading: string;
today: number;
rows: { name: string; sub: string; start: number; end: number; bar: string }[];
};
/** Fixture rows in the shape every adapter's `rows` prop takes. */
function ganttRows(): {
name: string;
sub: string;
start: number;
end: number;
color: string;
}[] {
return ganttFixture.rows.map(({ bar, ...row }) => ({ ...row, color: bar }));
}
import { Gantt } from "@grassroot/ui-vue";
const rows = ganttRows();
</script>
<template>
<div class="w-full min-w-0">
<Gantt :rows="rows" :today="ganttFixture.today" :label-heading="ganttFixture.labelHeading" />
</div>
</template>
ts
import { Component } from "@angular/core";
/**
* Gantt's pinned demo state (CSP-D3).
*
* Deliberately overlapping spans: `start`/`end` are quarter fractions on the
* default four-column axis, and every pair of adjacent rows shares part of
* its range, so the demo proves each row draws its own independent track
* rather than a single shared timeline. `today` sits inside that overlap
* (2.35 of 4 = 58.75%) so the marker rule lands mid-chart, not on a divider.
*
* `bar`, not `color`: the fixture set forbids a `color` key (it is one of the
* theme-coupled names the proof test rejects), while `GanttRow`'s own field
* is called `color`. `ganttRows()` below does that one rename, once, instead
* of five demo files each spreading their own mapping.
*/
const ganttFixture = {
id: "gantt",
labelHeading: "Workstream",
today: 2.35,
rows: [
{ name: "Ledger v2 rewrite", sub: "platform", start: 0, end: 1.9, bar: "var(--accent)" },
{ name: "Billing migration", sub: "payments", start: 1.2, end: 2.8, bar: "var(--vs-info)" },
{ name: "Fraud rules engine", sub: "risk", start: 2.4, end: 4, bar: "var(--vs-warning)" },
{ name: "Partner API GA", sub: "integrations", start: 0.6, end: 3.2, bar: "var(--vs-success)" },
],
} satisfies {
id: string;
labelHeading: string;
today: number;
rows: { name: string; sub: string; start: number; end: number; bar: string }[];
};
/** Fixture rows in the shape every adapter's `rows` prop takes. */
function ganttRows(): {
name: string;
sub: string;
start: number;
end: number;
color: string;
}[] {
return ganttFixture.rows.map(({ bar, ...row }) => ({ ...row, color: bar }));
}
import { GrassrootGantt } from "@grassroot/ui-angular";
// Every input is a `[property]` binding, never a static attribute — Angular
// leaves static template attributes on the host element in the rendered DOM,
// where the other four frameworks have nothing.
@Component({
selector: "app-gantt-demo",
standalone: true,
imports: [GrassrootGantt],
host: { style: "display: contents" },
template: `
<div class="w-full min-w-0">
<grassroot-gantt [rows]="rows" [today]="today" [labelHeading]="labelHeading" />
</div>
`,
})
export class GanttDemoComponent {
protected readonly rows = ganttRows();
protected readonly today = ganttFixture.today;
protected readonly labelHeading = ganttFixture.labelHeading;
}
export default GanttDemoComponent;
ts
<script lang="ts">
/**
* Gantt's pinned demo state (CSP-D3).
*
* Deliberately overlapping spans: `start`/`end` are quarter fractions on the
* default four-column axis, and every pair of adjacent rows shares part of
* its range, so the demo proves each row draws its own independent track
* rather than a single shared timeline. `today` sits inside that overlap
* (2.35 of 4 = 58.75%) so the marker rule lands mid-chart, not on a divider.
*
* `bar`, not `color`: the fixture set forbids a `color` key (it is one of the
* theme-coupled names the proof test rejects), while `GanttRow`'s own field
* is called `color`. `ganttRows()` below does that one rename, once, instead
* of five demo files each spreading their own mapping.
*/
const ganttFixture = {
id: "gantt",
labelHeading: "Workstream",
today: 2.35,
rows: [
{ name: "Ledger v2 rewrite", sub: "platform", start: 0, end: 1.9, bar: "var(--accent)" },
{ name: "Billing migration", sub: "payments", start: 1.2, end: 2.8, bar: "var(--vs-info)" },
{ name: "Fraud rules engine", sub: "risk", start: 2.4, end: 4, bar: "var(--vs-warning)" },
{ name: "Partner API GA", sub: "integrations", start: 0.6, end: 3.2, bar: "var(--vs-success)" },
],
} satisfies {
id: string;
labelHeading: string;
today: number;
rows: { name: string; sub: string; start: number; end: number; bar: string }[];
};
/** Fixture rows in the shape every adapter's `rows` prop takes. */
function ganttRows(): {
name: string;
sub: string;
start: number;
end: number;
color: string;
}[] {
return ganttFixture.rows.map(({ bar, ...row }) => ({ ...row, color: bar }));
}
import { Gantt } from "@grassroot/ui-svelte";
</script>
<div class="w-full min-w-0">
<Gantt rows={ganttRows()} today={ganttFixture.today} labelHeading={ganttFixture.labelHeading} />
</div>
ts
/** @jsxImportSource solid-js */
/**
* Gantt's pinned demo state (CSP-D3).
*
* Deliberately overlapping spans: `start`/`end` are quarter fractions on the
* default four-column axis, and every pair of adjacent rows shares part of
* its range, so the demo proves each row draws its own independent track
* rather than a single shared timeline. `today` sits inside that overlap
* (2.35 of 4 = 58.75%) so the marker rule lands mid-chart, not on a divider.
*
* `bar`, not `color`: the fixture set forbids a `color` key (it is one of the
* theme-coupled names the proof test rejects), while `GanttRow`'s own field
* is called `color`. `ganttRows()` below does that one rename, once, instead
* of five demo files each spreading their own mapping.
*/
const ganttFixture = {
id: "gantt",
labelHeading: "Workstream",
today: 2.35,
rows: [
{ name: "Ledger v2 rewrite", sub: "platform", start: 0, end: 1.9, bar: "var(--accent)" },
{ name: "Billing migration", sub: "payments", start: 1.2, end: 2.8, bar: "var(--vs-info)" },
{ name: "Fraud rules engine", sub: "risk", start: 2.4, end: 4, bar: "var(--vs-warning)" },
{ name: "Partner API GA", sub: "integrations", start: 0.6, end: 3.2, bar: "var(--vs-success)" },
],
} satisfies {
id: string;
labelHeading: string;
today: number;
rows: { name: string; sub: string; start: number; end: number; bar: string }[];
};
/** Fixture rows in the shape every adapter's `rows` prop takes. */
function ganttRows(): {
name: string;
sub: string;
start: number;
end: number;
color: string;
}[] {
return ganttFixture.rows.map(({ bar, ...row }) => ({ ...row, color: bar }));
}
import { Gantt } from "@grassroot/ui-solid";
export default function GanttDemo() {
return (
<div class="w-full min-w-0">
<Gantt
rows={ganttRows()}
today={ganttFixture.today}
labelHeading={ganttFixture.labelHeading}
/>
</div>
);
}

Installation

API Reference

Import

import { Gantt } from "@grassroot/ui-react/advanced";

Props

Prop
Type
Default
Requirement
Description
rows
CoreGanttRow[]
required
columns
string[] | undefined
["Q1","Q2","Q3","Q4"]
optional
Column tick labels across the top.
today
number | undefined
optional
"Today" position as a fraction across the axis (0..units).
labelHeading
string | undefined
"Workstream"
optional
Heading for the first (label) column.
getRowId
((row: GanttRow, index: number) => string) | undefined
optional
Stable row id.
step
number | undefined
1
optional
Axis unit for keyboard nudges.
onRangeChange
((detail: GanttRangeChangeDetail) => void) | undefined
optional
Prop
rows
Type
CoreGanttRow[]
Default
Requirement
required
Description
Prop
columns
Type
string[] | undefined
Default
["Q1","Q2","Q3","Q4"]
Requirement
optional
Description
Column tick labels across the top.
Prop
today
Type
number | undefined
Default
Requirement
optional
Description
"Today" position as a fraction across the axis (0..units).
Prop
labelHeading
Type
string | undefined
Default
"Workstream"
Requirement
optional
Description
Heading for the first (label) column.
Prop
getRowId
Type
((row: GanttRow, index: number) => string) | undefined
Default
Requirement
optional
Description
Stable row id.
Prop
step
Type
number | undefined
Default
1
Requirement
optional
Description
Axis unit for keyboard nudges.
Prop
onRangeChange
Type
((detail: GanttRangeChangeDetail) => void) | undefined
Default
Requirement
optional
Description

Events

EventPayload
onRangeChange((detail: GanttRangeChangeDetail) => void) | undefined

Native attributes

Standard DOM and ARIA attributes not listed above spread onto the root element.

Import

import { Gantt } from "@grassroot/ui-vue";

Props

Prop
Type
Default
Requirement
rows
CoreGanttRow[]
required
columns
string[] | undefined
() => [...DEFAULT_GANTT_COLUMNS]
optional
today
number | undefined
optional
labelHeading
string | undefined
"Workstream"
optional
getRowId
((row: GanttRow, index: number) => string) | undefined
optional
step
number | undefined
optional
Prop
rows
Type
CoreGanttRow[]
Default
Requirement
required
Prop
columns
Type
string[] | undefined
Default
() => [...DEFAULT_GANTT_COLUMNS]
Requirement
optional
Prop
today
Type
number | undefined
Default
Requirement
optional
Prop
labelHeading
Type
string | undefined
Default
"Workstream"
Requirement
optional
Prop
getRowId
Type
((row: GanttRow, index: number) => string) | undefined
Default
Requirement
optional
Prop
step
Type
number | undefined
Default
Requirement
optional

Events

EventPayload
rangeChange[detail: GanttRangeChangeDetail]

Import

import { Component } from "@angular/core";
import { GrassrootGantt } from "@grassroot/ui-angular";

Props

Prop
Type
Default
Requirement
rows
GanttRow[]
required
columns
string[]
DEFAULT_COLUMNS
optional
today
number | undefined
undefined
optional
labelHeading
string
"Workstream"
optional
getRowId
(row: GanttRow, index: number) => string
optional
step
number | undefined
undefined
optional
Prop
rows
Type
GanttRow[]
Default
Requirement
required
Prop
columns
Type
string[]
Default
DEFAULT_COLUMNS
Requirement
optional
Prop
today
Type
number | undefined
Default
undefined
Requirement
optional
Prop
labelHeading
Type
string
Default
"Workstream"
Requirement
optional
Prop
getRowId
Type
(row: GanttRow, index: number) => string
Default
Requirement
optional
Prop
step
Type
number | undefined
Default
undefined
Requirement
optional

Events

EventPayload
rangeChangeGanttRangeChangeDetail

Import

import { Gantt } from "@grassroot/ui-svelte";

Props

Prop
Type
Default
Requirement
rows
GanttRow[]
required
columns
string[]
optional
today
number
optional
labelHeading
string
"Workstream"
optional
className
string
optional
getRowId
(row: GanttRow, index: number) => string
optional
step
number
optional
onRangeChange
(detail: GanttRangeChangeDetail) => void
optional
Prop
rows
Type
GanttRow[]
Default
Requirement
required
Prop
columns
Type
string[]
Default
Requirement
optional
Prop
today
Type
number
Default
Requirement
optional
Prop
labelHeading
Type
string
Default
"Workstream"
Requirement
optional
Prop
className
Type
string
Default
Requirement
optional
Prop
getRowId
Type
(row: GanttRow, index: number) => string
Default
Requirement
optional
Prop
step
Type
number
Default
Requirement
optional
Prop
onRangeChange
Type
(detail: GanttRangeChangeDetail) => void
Default
Requirement
optional

Events

EventPayload
onRangeChange(detail: GanttRangeChangeDetail) => void

Import

import { Gantt } from "@grassroot/ui-solid";

Props

Prop
Type
Default
Requirement
rows
GanttRow[]
required
columns
string[]
optional
today
number
optional
labelHeading
string
optional
className
string
optional
getRowId
(row: GanttRow, index: number) => string
optional
step
number
optional
onRangeChange
(detail: GanttRangeChangeDetail) => void
optional
Prop
rows
Type
GanttRow[]
Default
Requirement
required
Prop
columns
Type
string[]
Default
Requirement
optional
Prop
today
Type
number
Default
Requirement
optional
Prop
labelHeading
Type
string
Default
Requirement
optional
Prop
className
Type
string
Default
Requirement
optional
Prop
getRowId
Type
(row: GanttRow, index: number) => string
Default
Requirement
optional
Prop
step
Type
number
Default
Requirement
optional
Prop
onRangeChange
Type
(detail: GanttRangeChangeDetail) => void
Default
Requirement
optional

Events

EventPayload
onRangeChange(detail: GanttRangeChangeDetail) => void

Native attributes

Standard DOM and ARIA attributes not listed above spread onto the root element.