Markers and range bands
`GanttMarker`, `GanttRangeBand`, `GanttDateRange`
GanttMarker draws a vertical line at one date. GanttRangeBand shades a span of dates behind the rows. GanttDateRange is the shape onRangeChange hands back. All three are exported from the package root.
// src/MarkerTypes.tsx
import type {
GanttMarker,
GanttRangeBand,
GanttDateRange,
} from '@jaeungkim/gantt-chart';They reach the chart through three props on GanttProps:
| Prop | Type | Default |
|---|---|---|
markers | GanttMarker[] | [] (module-level constant, stable across renders) |
rangeBands | GanttRangeBand[] | [] (module-level constant, stable across renders) |
onRangeChange | (range: GanttDateRange) => void | none |
What markers and bands are used for, and how they behave as the timeline scrolls, is in The timeline.
Date input
Every date field on these types takes the same union:
// src/types/gantt.ts
/** Anything the marker/band props accept as a date */
export type GanttDateInput = string | Date | Dayjs;GanttDateInput is not exported from the package root — do not try to import it. Write the literal union, or use GanttMarker['date'].
Each value is passed straight to the chart's UTC dayjs, so a string without a zone ('2025-06-01', '2025-06-01T09:00') is read as a UTC wall clock and lands where it reads. The same rules as task dates apply; see Task data.
GanttMarker
// src/types/gantt.ts
/** A labelled vertical line at one date - deadlines, releases, and the built-in today line */
export interface GanttMarker {
/** React key (default: the date) */
id?: string;
date: GanttDateInput;
/** Text shown at the top of the line - omitted, the line is drawn bare */
label?: string;
/** Extra class on the marker element */
className?: string;
/** Line color - any CSS color, overrides the class and the theme default */
color?: string;
/**
* Turn the marker into a warning (`data-warning="true"`) once a task ends past its date
*
* Checks every task, or only `taskIds` when that is given.
*/
warnOnOverrun?: boolean;
/** Limits `warnOnOverrun` to these tasks */
taskIds?: string[];
}| Field | Type | Required | Default | Meaning |
|---|---|---|---|---|
id | string | no | `${String(date)}-${index}` | React key only. Never written to the DOM. |
date | GanttDateInput | yes | — | Where the line is drawn. An unparseable value drops the marker. |
label | string | no | none | Text in .gantt-marker-label. Omitted, no label element is rendered. |
className | string | no | none | Appended after gantt-marker on the line element. |
color | string | no | var(--gantt-marker) | Any CSS color. Written inline as --gantt-marker-color. |
warnOnOverrun | boolean | no | false | Compared with === true. Sets data-warning="true" when a watched task ends after date. |
taskIds | string[] | no | all tasks | Restricts the warnOnOverrun check to these task ids. |
Overrun
The chart resolves warnOnOverrun against its full transformed task list, collapsed rows included. The comparison is strictly greater than:
// src/utils/timeline.ts
const overrun =
marker.warnOnOverrun === true &&
tasks.some(
(task) =>
(!marker.taskIds || marker.taskIds.includes(task.id)) &&
dayjs(task.endDate).valueOf() > time
);A task ending exactly on date is not an overrun.
DOM
<div class="gantt-marker <className>" style="left: <n>px; --gantt-marker-color: <color>"
data-warning="true" aria-hidden="true">
<span class="gantt-marker-label"><label></span>
</div>data-warning is present only when the overrun check passed; otherwise the attribute is absent, not "false". --gantt-marker-color is present only when color is set. The marker elements are siblings inside .gantt-content, with no wrapper layer.
| Selector | Declarations |
|---|---|
.gantt-marker | position:absolute; top:0; bottom:0; width:2px; margin-left:-1px; background:var(--gantt-marker-color, var(--gantt-marker)); pointer-events:none; z-index:2 |
.gantt-marker[data-warning="true"] | --gantt-marker-color: var(--gantt-marker-warning) |
.gantt-marker-label | position:absolute; top:2px; left:3px; padding:1px 5px; font-size:10px; font-weight:600; line-height:1.4; color:var(--gantt-marker-label); background:var(--gantt-marker-color, var(--gantt-marker)); border-radius:3px; white-space:nowrap |
.gantt-today-marker | position:absolute; top:0; bottom:0; width:2px; margin-left:-1px; background:var(--gantt-today-marker); opacity:0.7; pointer-events:none; z-index:2 |
The built-in today marker
The chart prepends one marker of its own to whatever markers holds:
// src/components/Gantt.tsx
{ id: "today", date: dayjs(), className: "gantt-today-marker" }Its id is "today" and its class list is gantt-marker gantt-today-marker. It carries no label, no color and no warnOnOverrun. There is no prop that hides it — .gantt-today-marker { display: none } is the only switch.
GanttRangeBand
// src/types/gantt.ts
/** A shaded band covering a date range - sprints, phases, freezes */
export interface GanttRangeBand {
/** React key (default: the start date) */
id?: string;
startDate: GanttDateInput;
endDate: GanttDateInput;
/** Text shown at the top of the band */
label?: string;
/** Extra class on the band element */
className?: string;
/** Fill color - any CSS color, overrides the class and the theme default */
color?: string;
}| Field | Type | Required | Default | Meaning |
|---|---|---|---|---|
id | string | no | `${String(startDate)}-${index}` | React key only. Never written to the DOM. |
startDate | GanttDateInput | yes | — | Left edge, measured like a bar starting on that date. |
endDate | GanttDateInput | yes | — | Right edge. Must be strictly after startDate. |
label | string | no | none | Text in .gantt-range-band-label. Omitted, no label element is rendered. |
className | string | no | none | Appended after gantt-range-band on the band element. |
color | string | no | var(--gantt-band-bg) | Any CSS color. Written inline as --gantt-band-color. |
DOM
<div class="gantt-range-band-layer" aria-hidden="true">
<div class="gantt-range-band <className>"
style="left: <n>px; width: <n>px; --gantt-band-color: <color>">
<span class="gantt-range-band-label"><label></span>
</div>
</div>Bands render no data attributes. The layer element is omitted entirely when no band survives placement.
| Selector | Declarations |
|---|---|
.gantt-range-band-layer | position:absolute; top:0; left:0; width:100%; height:100%; pointer-events:none; z-index:0 |
.gantt-range-band | position:absolute; top:0; bottom:0; background:var(--gantt-band-color, var(--gantt-band-bg)) |
.gantt-range-band-label | position:absolute; top:2px; left:4px; font-size:10px; font-weight:600; color:var(--gantt-muted-foreground); white-space:nowrap |
GanttDateRange
// src/types/gantt.ts
/** The rendered timeline range, as reported by `onRangeChange` */
export interface GanttDateRange {
start: Dayjs;
end: Dayjs;
}| Field | Type | Meaning |
|---|---|---|
start | Dayjs | Start date of the first tick on the timeline. Inclusive. |
end | Dayjs | Start date of the last tick plus one tick unit. Exclusive. |
Both values are UTC dayjs objects. The package does not re-export dayjs or the Dayjs type — import it from dayjs directly.
Placement rules
| Input | Result |
|---|---|
Marker date unparseable | Marker dropped, no error |
Marker date outside the rendered range | Marker dropped, no error |
Band startDate or endDate unparseable | Band dropped, no error |
Band endDate <= startDate | Band dropped, no error |
| Band entirely before or after the rendered range | Band dropped, no error |
| Band overlapping one end of the range | Clipped to the visible part |
| Band narrower than 1px at the current scale | Drawn 1px wide |
Constraints
- Markers and bands are decorative. Both are
aria-hidden="true"withpointer-events: none, so there is no click, hover, tooltip or focus API and no screen-reader text. - Markers render inside the scrolling content, below the sticky header. A marker line never crosses the header rows.
- The today marker's
dayjs()is read when the marker memo recomputes. No timer moves the line at midnight. - A host marker with
id: "today"collides with the built-in one and produces a duplicate React key warning. coloroutrankswarnOnOverrun. Both write--gantt-marker-color, andcolorwrites it inline, so a marker with both keeps its custom color and never turns amber.data-warningstill appears in the DOM.className: "gantt-today-marker"on a host marker overridescoloron the line but not on the label, because that rule setsbackgrounddirectly.- The rendered range is the tasks' span plus a fixed buffer unless
visibleStart,visibleEndorinfiniteScrollwidens it. A marker far past the last task is not drawn; see The timeline. GanttDateInput,PositionedMarker,PositionedBand,computeMarkerOffsetsandcomputeBandRectsare internal. OnlyGanttMarker,GanttRangeBandandGanttDateRangeare importable from the package.- The JSDoc on
idsays the key defaults to the date. The code appends the array index to it. Neither value reaches the DOM.
Related
- The timeline — when markers and bands are drawn, and what the rendered range covers.
- Theming —
--gantt-marker,--gantt-marker-warning,--gantt-marker-label,--gantt-today-marker,--gantt-band-bgand the rest of the custom-property table. - Props — the full
GanttPropsindex.