Scale and theme types

The scale keys, the format overrides, the theme values and the holiday shape

Five types describe how the timeline is measured and how the chart is painted. GanttScaleKey names one of the five scales. GanttScaleFormat and GanttFormatOverrides replace the labels of a scale, GanttTheme selects the palette, and Holiday marks a day off beyond the weekend. All five are exported from the package root.

import type {
  GanttScaleKey,
  GanttScaleFormat,
  GanttFormatOverrides,
  GanttTheme,
  Holiday,
} from '@jaeungkim/gantt-chart';

GanttScaleKey

export type GanttScaleKey = 'day' | 'week' | 'month' | 'quarter' | 'year';

The declaration order is the scale order, finest first. Ctrl/Cmd + ArrowUp moves one scale finer and Ctrl/Cmd + ArrowDown one scale coarser, clamped at both ends. Ctrl/Cmd + wheel moves through the same order when zoomOnWheel is on. defaultScale takes one of these values and defaults to 'month'.

The chart renders no scale picker of its own and does not keep the scale across a remount. Render your own picker and drive it through setScale on the ref. onScaleChange reports every change, including the wheel and keyboard ones. See GanttHandle.

formats is keyed by this type, and GanttDetailRenderProps.scale carries it. See Detail renderer.

Per-scale configuration

Each key maps to one row of a fixed table. The row sets the header cell labels, the time one tick covers, and the pixels one drag step is worth. The timeline lists what each scale shows. This page describes the shape of one row:

export interface GanttScaleConfig {
  labelUnit: GanttLabelUnit;
  tickUnit: 'minute' | 'hour' | 'day' | 'week' | 'month';
  unitPerTick: number;
  /** Boundary the first cell snaps to, when a cell holds more than one tickUnit */
  tickAlign?: GanttLabelUnit;

  dragStepUnit: 'minute' | 'hour' | 'day' | 'week';
  dragStepAmount: number;

  basePxPerDragStep: number;

  formatTickLabel: (date: Dayjs) => string;
  formatHeaderLabel: (date: Dayjs) => string;
}
/** Unit the top header row groups by ('quarter' has no dayjs equivalent - see core/dates) */
export type GanttLabelUnit =
  | 'hour'
  | 'day'
  | 'week'
  | 'month'
  | 'quarter'
  | 'year';

GanttScaleConfig, GanttLabelUnit and the GANTT_SCALE_CONFIG object itself are module-internal and not exported from the package. The table cannot be imported, extended or replaced, and no prop adds a sixth scale. Tick widths are derived from basePxPerDragStep / dragStepAmount. The timeline lists the resulting pixels per day, and Editing tasks has the drag grid the same two fields define.

Built-in labels

formatTickLabel and formatHeaderLabel are the last layer of the label chain. They produce the labels when neither a formats override nor a locale applies.

scaleformatTickLabelformatHeaderLabeltooltip format stringreadout format string
dayd.format('HH')d.format('MMM D, YYYY')'MMM D, YYYY HH:mm [UTC]''HH:mm [UTC]'
weekd.format('D')d.format('MMM YYYY')'MMM D, YYYY''MMM D'
monthd.format('MMM D')d.format('MMM YYYY')'MMM D, YYYY''MMM D'
quarterd.format('MMM')`Q${quarterOfYear(d)} ${d.format('YYYY')}`'MMM YYYY''MMM YYYY'
yeard.format('MMM')d.format('YYYY')'MMM YYYY''MMM YYYY'

With firstDayOfWeek set, the week scale takes its header from the day row.

The tooltip and readout strings are in separate DATE_FORMATS and RANGE_FORMATS constants, neither exported. [UTC] in the two day strings is a dayjs escaped literal, not a resolved zone name.

The tooltip string is the chart's general-purpose date label. The hover tooltip, the bar's ARIA label, the drag and keyboard announcements and the built-in detail panel's Start and End fields all print it. The panel prints it only where those two fields are not editable. An editable field is a native <input type="date"> showing YYYY-MM-DD. See Detail panel.

The readout string is the compact per-scale variant used by the header drag readout. Where the chart writes the readout and why the shorter strings drop the year are in Locale and date formats.

GanttScaleFormat

/**
 * Replaces the generated labels of one scale
 * Every entry is optional - whatever is left out keeps the built-in (or locale) label
 */
export interface GanttScaleFormat {
  /** Bottom header row - one label per tick */
  tick?: (date: Dayjs) => string;
  /** Top header row - one label per group */
  header?: (date: Dayjs) => string;
  /**
   * Every date the chart writes as a single date: the bar's hover tooltip, its live drag readout
   * and aria-label, the header's drag guide labels (through `edge` and `range`, which follow this
   * slot), and the detail panel's Start and End where they are not editable - an editable date is
   * a native `<input type="date">` and never reaches a formatter.
   */
  tooltip?: (date: Dayjs) => string;
}
FieldTypeRequiredApplies to
tick(date: Dayjs) => stringnoone label per tick, bottom header row
header(date: Dayjs) => stringnoone label per group, top header row
tooltip(date: Dayjs) => stringnoevery date off the header rows: the hover tooltip, the bar's ARIA label, the drag and keyboard announcements, and the built-in detail panel's dates where they are not editable

The three slots are resolved independently. Supplying header alone leaves tick and tooltip on the locale or built-in labels.

A tooltip override also formats each end of the header drag readout. Without one, the readout keeps the compact per-scale strings in the readout column under Built-in labels.

GanttFormatOverrides

/** Per-scale label overrides, e.g. `{ quarter: { header: (d) => ... } }` */
export type GanttFormatOverrides = Partial<
  Record<GanttScaleKey, GanttScaleFormat>
>;

This is the type of the formats prop. A scale left out of the record keeps its resolved labels.

const formats: GanttFormatOverrides = {
  quarter: {
    header: (d) => `${d.year()} Q${Math.floor(d.month() / 3) + 1}`,
  },
  month: {
    tooltip: (d) => d.format('YYYY-MM-DD'),
  },
};

formats is declared at module scope so the object keeps the same identity across renders. Locale and date formats covers which layer wins for each of the three labels and why identity matters.

GanttTheme

/** Chart theme - 'system' follows the OS setting; omit the prop to follow the host page's `color-scheme`. */
export type GanttTheme = 'light' | 'dark' | 'system';

This is the type of the theme prop, which has no default. The resolved value is written to data-theme on the container, and the stylesheet reads that attribute. How each value resolves and what the palette holds are in Theming.

GanttTheme is a three-value union, and every color is a CSS custom property. There is no theme object or token prop.

Holiday

/** A day off beyond the weekend. A bare `YYYY-MM-DD` string is the same thing with no label. */
export interface Holiday {
  /** UTC `YYYY-MM-DD` */
  date: string;
  /** Inclusive last day - omit for a single day */
  endDate?: string;
  /** Written in the tick row over the holiday's band, when the band is wide enough to hold it */
  label?: string;
  /** Any CSS colour. Tinted onto the grid at the weekend shade's weight, never painted over it. */
  color?: string;
}
FieldTypeRequiredMeaning
datestringyesthe first day off, as a UTC YYYY-MM-DD string
endDatestringnoinclusive last day of the span; omit for a single day
labelstringnotext written in the tick row over the holiday's band
colorstringnoany CSS color, tinted onto the grid at the weekend shade's weight

What the chart draws for a holiday, when the label appears and how the entries are supplied are in The timeline.

Notes

How the three slots resolve, what each formatter receives, and the identity rule are in Locale and date formats.

  • GanttScaleFormat functions must return a string. There is no ReactNode escape hatch, and no prop replaces a header cell. A header cell is text. Style one with CSS on .gantt-top-group and .gantt-bottom-cell.
  • The chart's UTC dayjs instance is not exported from the package. An override that needs the same instance imports dayjs and its utc plugin directly.