Theming
The look of the chart is a stylesheet made of custom properties. This guide shows how to switch
between the light and dark themes, how to re-theme the chart by overriding tokens, how to colour
single bars, how to change the size of rows and bars, and how to replace the stylesheet altogether
by following the unstyled contract: the class names and data-* attributes the renderer writes.
The same DOM and the same stylesheet draw the chart in vanilla code and in React, so everything
here applies to both. In React the stylesheet is imported from @ganttlane/react/styles.css (a
byte-for-byte copy of @ganttlane/dom/styles.css); import one of the two, never both.
The short version
Section titled “The short version”import '@ganttlane/dom/styles.css';import { createGantt } from '@ganttlane/dom';
const gantt = createGantt(container, { tasks, theme: 'auto' });/* Your own stylesheet, loaded after the library's. No !important needed. */.gantt { --gantt-bar-color: #0f766e; --gantt-today-color: #b45309;}theme is 'auto' by default. The stylesheet is in @layer gantt, so a style of your own that is
not in a layer wins whatever its specificity. The theming example shows
the pieces live, and the release plan colours its bars by status
with one custom property per class.
Themes
Section titled “Themes”Choosing a theme
Section titled “Choosing a theme”The theme option sets data-theme on the root element div.gantt. setOptions changes it at
any time, and the change costs no redraw: the stylesheet does the work.
| Value | Look |
|---|---|
'light' |
The light tokens, which are the default value of every token. |
'dark' |
The dark tokens, always. |
'auto' |
Light, and the dark tokens while prefers-color-scheme: dark is active. |
const gantt = createGantt(container, { tasks, theme: 'dark' });
gantt.setOptions({ theme: 'light' });<Gantt style={{ height: 420 }} defaultTasks={tasks} theme={dark ? 'dark' : 'light'} />Any other value is an invalid-option issue with the path theme, and the
chart uses 'auto'. Each theme also sets color-scheme, so the scrollbars of the chart follow it.
Following the host page
Section titled “Following the host page”'auto' follows the operating system, not your application. An application with a theme switch of
its own passes 'light' or 'dark' and changes it when its switch changes.
// The application marks its theme with a class on <html>.const html = document.documentElement;const follow = (): void => { gantt.setOptions({ theme: html.classList.contains('dark') ? 'dark' : 'light' });};const observer = new MutationObserver(follow);observer.observe(html, { attributes: true, attributeFilter: ['class'] });follow();
// When the chart goes: observer.disconnect(); gantt.destroy();function Plan() { const dark = useIsDark(); // your own hook or context return <Gantt style={{ height: 420 }} defaultTasks={tasks} theme={dark ? 'dark' : 'light'} />;}The attribute is plain markup, so setting it yourself (root.dataset.theme = 'dark' on div.gantt)
also works, and the charts of this documentation site do it for examples that never set theme. The
renderer writes the attribute again the next time theme is set through setOptions or a prop.
How a theme is built
Section titled “How a theme is built”A theme is nothing but a set of token values, so the rules never change between themes:
- the light values are declared on
.ganttand apply to every theme; darkoverrides the colour tokens under.gantt[data-theme='dark'];autorepeats those overrides under@media (prefers-color-scheme: dark), on.gantt[data-theme='auto'].
Tokens for sizes and widths are the same in both themes. The token reference below lists the light and dark value of each token.
There is no registry of named themes. A theme of your own (a brand theme, a high-contrast theme) is
a class on the container, or on any ancestor, and a rule for the .gantt inside it that sets the
tokens (.brand .gantt { ... }), as the next section shows. The tokens themselves go on .gantt,
not on the class.
Re-theming with tokens
Section titled “Re-theming with tokens”Where to put an override
Section titled “Where to put an override”All of the library’s CSS is in @layer gantt, and no selector in it is more specific than a class
plus a data-* attribute or a pseudo-class. A style of your own that is not in a layer wins over
all of it without !important:
/* Every chart on the page */.gantt { --gantt-font-family: 'Inter', sans-serif; --gantt-bar-color: #0f766e;}
/* One chart: give its container a class and use the descendant */.roadmap .gantt { --gantt-link-width: 2px;}In React, className on <Gantt> goes to the container div, which is the parent of div.gantt,
so .roadmap .gantt is the selector for that component too. If your own styles live in cascade
layers, declare the order once, @layer gantt, app;, so that yours come later.
A brand theme
Section titled “A brand theme”A theme of your own is a class plus one block per theme: the base values for every theme, then the
dark values under [data-theme='dark'], and again under the media query for 'auto'.
.brand .gantt { --gantt-font-family: 'Inter', system-ui, sans-serif; --gantt-bar-color: #0f766e; --gantt-link-selected-color: #0f766e; --gantt-focus-ring-color: #0f766e; --gantt-today-color: #b45309;}
.brand .gantt[data-theme='dark'] { --gantt-bar-color: #2dd4bf; --gantt-link-selected-color: #5eead4; --gantt-focus-ring-color: #5eead4; --gantt-today-color: #fbbf24;}
@media (prefers-color-scheme: dark) { .brand .gantt[data-theme='auto'] { --gantt-bar-color: #2dd4bf; --gantt-link-selected-color: #5eead4; --gantt-focus-ring-color: #5eead4; --gantt-today-color: #fbbf24; }}<Gantt className="brand" style={{ height: 420 }} defaultTasks={tasks} />The dark blocks have a higher specificity than the base block, so they win in the dark theme. If
your browsers support light-dark() (Chrome 123, Firefox 120, Safari 17.5), one block is enough:
each theme sets color-scheme on .gantt, which is what light-dark() reads. That is newer than
the browsers the library itself supports, so the three-block form above is the portable one.
.brand .gantt { --gantt-bar-color: light-dark(#0f766e, #2dd4bf);}Tokens
Section titled “Tokens”Every custom property below is declared on .gantt. A dash in the Dark column means the dark theme
keeps the light value. The dark values are the dark theme’s; 'auto' uses the same ones while the
system prefers dark.
Text and motion
Section titled “Text and motion”| Token | Light | Dark | Controls |
|---|---|---|---|
--gantt-font-family |
system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif |
- | Every text of the chart: header and bar labels |
--gantt-font-size |
12px |
- | Every text of the chart |
--gantt-transition-duration |
120ms |
- | Fade of the handles (none under prefers-reduced-motion) |
Surface and borders
Section titled “Surface and borders”| Token | Light | Dark | Controls |
|---|---|---|---|
--gantt-background-color |
#fff |
#15171c |
The chart’s background |
--gantt-foreground-color |
#1b2230 |
#e4e8ef |
Default text, the current header cell |
--gantt-border-color |
#d3d9e1 |
#333a46 |
Header borders and cell separators |
--gantt-border-width |
1px |
- | Header borders, the outline of a hovered bar |
Header
Section titled “Header”| Token | Light | Dark | Controls |
|---|---|---|---|
--gantt-header-background-color |
#f5f6f8 |
#1c1f26 |
The header |
--gantt-header-text-color |
#465163 |
#a9b3c3 |
Header labels |
--gantt-header-current-color |
rgb(229 72 77 / 10%) |
rgb(255 99 105 / 16%) |
The header cell that contains today |
--gantt-header-range-color |
rgb(37 99 235 / 14%) |
rgb(110 160 255 / 20%) |
The hovered bar’s range in the header |
Grid and rows
Section titled “Grid and rows”| Token | Light | Dark | Controls |
|---|---|---|---|
--gantt-grid-line-color |
#e7eaef |
#242932 |
Grid lines |
--gantt-grid-line-width |
1px |
- | Grid lines |
--gantt-grid-major-line-color |
#c8cfd9 |
#3a4252 |
Major grid lines (month and year boundaries) |
--gantt-grid-major-line-width |
1px |
- | Major grid lines |
--gantt-grid-minor-line-color |
#eff1f4 |
#1e222a |
Minor grid lines (the snap steps inside a column, dotted) |
--gantt-row-stripe-color |
rgb(15 23 42 / 3.5%) |
rgb(255 255 255 / 3.5%) |
Striped rows (display.rowStripes) |
--gantt-row-invalid-color |
rgb(229 72 77 / 8%) |
rgb(255 99 105 / 12%) |
A row whose task is invalid |
--gantt-row-placeholder-color |
rgb(100 116 139 / 8%) |
rgb(148 163 184 / 8%) |
A placeholder row (a task of type placeholder) |
--gantt-row-hover-color |
transparent |
transparent |
The tint over the row that is lit or focused. On with display.rowHover and a task list, or set it yourself |
--gantt-row-highlight-color |
rgb(37 99 235 / 8%) |
rgb(110 160 255 / 12%) |
The colour --gantt-row-hover-color takes when it is on |
--gantt-row-selected-color |
rgb(37 99 235 / 16%) |
rgb(110 160 255 / 24%) |
A selected row of a task list |
Days off, highlights and today
Section titled “Days off, highlights and today”| Token | Light | Dark | Controls |
|---|---|---|---|
--gantt-nonworking-color |
rgb(100 116 139 / 10%) |
rgb(148 163 184 / 9%) |
Non-working days in 'fill' mode |
--gantt-nonworking-stripe-color |
rgb(100 116 139 / 30%) |
rgb(148 163 184 / 26%) |
Non-working days in 'stripes' mode (the lines) |
--gantt-highlight-color |
#f5b301 |
#f5b301 |
Highlighted days; a highlight’s color overrides it |
--gantt-highlight-opacity |
0.2 |
0.16 |
Opacity of highlights, so any colour stays readable |
--gantt-today-color |
#e5484d |
#ff6369 |
The today line and the marker in the header |
--gantt-today-width |
1.5px |
- | The today line |
| Token | Light | Dark | Controls |
|---|---|---|---|
--gantt-bar-color |
#3f6fd1 |
#4170d3 |
Bars without their own colour |
--gantt-bar-progress-shade-color |
#000 |
#000 |
What the progress colour is mixed towards |
--gantt-bar-progress-shade |
30% |
35% |
How much of the shade the progress colour has |
--gantt-bar-label-color |
#fff |
#fff |
A bar’s label inside the bar |
--gantt-bar-label-outside-color |
#1b2230 |
#e4e8ef |
A bar’s label next to the bar |
--gantt-bar-selected-color |
#111827 |
#f3f4f6 |
The outline of a selected bar |
--gantt-bar-selected-width |
2px |
- | Outline of a selected, conflicting or invalid bar |
--gantt-bar-conflict-color |
#e5484d |
#ff6369 |
The outline of a bar in conflict |
--gantt-bar-invalid-color |
#d97706 |
#f0a43a |
The (dashed) outline of an invalid bar |
--gantt-handle-color |
rgb(255 255 255 / 55%) |
rgb(255 255 255 / 50%) |
Resize and progress handles |
| Token | Light | Dark | Controls |
|---|---|---|---|
--gantt-link-color |
#7a8699 |
#8794a8 |
Link arrows |
--gantt-link-width |
1.5px |
- | Link arrows |
--gantt-link-selected-color |
#2563eb |
#6ea0ff |
A selected or hovered link, a valid link preview |
--gantt-link-selected-width |
2.5px |
- | A selected link, the link preview |
--gantt-link-conflict-color |
#e5484d |
#ff6369 |
A link in conflict or in a cycle, an invalid preview |
--gantt-link-hit-width |
12px |
- | Width of the invisible area that catches clicks on a link |
Popovers and focus
Section titled “Popovers and focus”| Token | Light | Dark | Controls |
|---|---|---|---|
--gantt-popover-background-color |
#fff |
#222731 |
Tooltips, the link menu and day labels: background |
--gantt-popover-foreground-color |
#1b2230 |
#e4e8ef |
Popover text |
--gantt-popover-border-color |
#d3d9e1 |
#3a4252 |
Popover border, action button border |
--gantt-popover-shadow-color |
rgb(15 23 42 / 18%) |
rgb(0 0 0 / 55%) |
Popover shadow |
--gantt-popover-radius |
6px |
- | Popover corner radius |
--gantt-popover-max-width |
320px |
- | Widest a popover grows before its text wraps |
--gantt-focus-ring-color |
#2563eb |
#7aa7ff |
Keyboard focus ring (:focus-visible) |
--gantt-focus-ring-width |
2px |
- | Keyboard focus ring |
What is not a token
Section titled “What is not a token”- Geometry. The row height, bar height, bar radius, header row heights and every coordinate come
from the chart’s options (see Density and sizes). CSS only decides how they
look. The size of the round link handles is
interaction.handles.link, not a token. - Stacking. The sticky header has
z-index: 2and the layer of tooltips and menusz-index: 3, both fixed. If something on your page overlaps the chart, or the chart overlaps it, give the wrapper of the chartisolation: isolate, which keeps those two values inside it.
Colours of single bars
Section titled “Colours of single bars”Some tokens are also written by the renderer on a single element, through the CSSOM (never as
style text, so a strict style-src Content Security Policy is fine). An element without its own
value inherits the one of the theme.
| Property | Set on | From |
|---|---|---|
--gantt-bar-color |
.gantt__bar |
task.color. The progress colour is derived from it, see below. |
--gantt-bar-progress-color |
.gantt__bar |
task.progressColor: replaces the derived progress colour. |
--gantt-highlight-color |
highlight node | The color of an entry of highlights. |
The progress colour is color-mix(in srgb, <bar colour>, <shade> <amount>), with the shade and the
amount taken from --gantt-bar-progress-shade-color and --gantt-bar-progress-shade. Change those
two tokens to make the done part lighter or darker for every bar.
task.className is added to the bar’s own element, g.gantt__bar, and task.rowClassName to the
row’s rect.gantt__row. A custom property set by a class on the bar reaches the bar and its
progress, so a status colour is one rule per class, as in the release plan:
const tasks = [ { id: 'api', name: 'API', start: '2026-03-02', end: '2026-03-13', className: 'is-done', rowClassName: 'is-frozen', }, { id: 'ui', name: 'UI', start: '2026-03-09', end: '2026-03-20', className: 'is-late' },];.gantt__bar.is-done { --gantt-bar-color: #2f9e44;}.gantt__bar.is-late { --gantt-bar-color: #d9480f;}
/* task.rowClassName: a row is an SVG rect, so it takes `fill`. */.gantt__row.is-frozen { fill: rgb(100 116 139 / 12%);}A value of task.color is any string, so it can be a reference to a custom property of your page,
color: 'var(--status-done)', which then follows the theme. The task colours and
classes example shows colours and classes side by side.
Other things that change the look of a bar:
- States. A selected, conflicting or invalid bar is outlined with
--gantt-bar-selected-color,--gantt-bar-conflict-colorand--gantt-bar-invalid-color; when states combine, selection beats invalid and conflict, which beat the hover outline. The keyboard focus ring is anoutline, drawn beside all of them. - Labels.
--gantt-bar-label-coloris the label inside the bar and--gantt-bar-label-outside-colorthe one next to it. Where the label goes isdisplay.barLabel('auto','inside','outside','none'); see the bar labels example. - Milestones are drawn with
--gantt-bar-colortoo (.gantt__bar-milestone). - Highlights take a
classNameand acolorof their own per entry ofhighlights.
Dark mode
Section titled “Dark mode”Everything the chart draws follows theme, including tooltips, the link menu and the focus ring.
What does not follow it is what you add:
- Your own colours need a dark value. Put status classes in per-theme blocks as in the brand
theme above, or use a
var()reference intask.color. - The page around the chart is yours. A chart on a dark page with
theme: 'light'is a light card; matchthemeto the page, as in Following the host page. - Contrast. The two default themes have not been audited against WCAG AA. If your users need a higher contrast, override the colour tokens, and check the result.
Density and sizes
Section titled “Density and sizes”The size of rows and bars is not CSS: it decides the geometry the core computes, so it is an
option, set at creation or later with setOptions. CSS tokens only decide how that geometry looks.
| Option | Default | Meaning |
|---|---|---|
layout.rowHeight |
48 |
Height of a row in px |
layout.barHeight |
30 |
Height of a bar, centred in its row; above rowHeight it is clamped, with a warning |
layout.barRadius |
3 |
Corner radius; each bar clamps it to half its height and width |
layout.headerRowHeight |
[45, 30] |
One height for every header row, or one per row, top first |
layout.height |
container | A number of px, or 'auto' to grow with the rows and let the page scroll |
display.linkCurveRadius |
5 |
The largest corner radius of a link arrow |
createGantt(container, { tasks, layout: { rowHeight: 32, barHeight: 20, barRadius: 2, headerRowHeight: [32, 24] },});<Gantt style={{ height: 420 }} defaultTasks={tasks} layout={{ rowHeight: 32, barHeight: 20, barRadius: 2, headerRowHeight: [32, 24] }}/>A bad value is an invalid-layout-option issue and its default is used. The layout and sizes
example lets you change them live.
The handles are sized by interaction.handles, whose default is
{ resize: 8, progress: 6, link: 6, linkOffset: 10 }: the width of the resize zones, the half-size of
the progress handle, the radius of the round link handles and how far they sit outside the bar. The
hit test and the drawing share these numbers, and the stylesheet sets no radius (there is no
--gantt-handle-size token), so change the handles with the option.
--gantt-font-size changes the text but not the geometry. The width of a bar label is estimated,
never measured, for a 12 px text in an Arial-like face, because measuring would force a layout on
every frame. With a much larger font or a very different face (a monospace one), the 'auto'
placement of display.barLabel can put a label inside a bar where it does not fit: choose
'inside' or 'outside' then.
Grid, days off and the today line
Section titled “Grid, days off and the today line”Some of the look is a display option rather than a token. The tokens above colour these; the options switch them. The grid and today example shows them.
| Option | Values | Default |
|---|---|---|
display.gridLines |
'both' | 'vertical' | 'horizontal' | 'none' |
'both' |
display.today |
boolean: the line, its marker and the header cell |
true |
display.nonWorkingDays |
'fill' | 'stripes' | false |
'fill' |
display.rowStripes |
'row' | 'group' | false |
false |
display.barLabel |
'auto' | 'inside' | 'outside' | 'none' |
'auto' |
CSS support
Section titled “CSS support”The stylesheet uses cascade layers, :is(), overflow: clip and color-mix(). The newest of them
is color-mix() (Chrome and Edge 111, Firefox 113, Safari and iOS Safari 16.2); every browser the
library supports has all four. It uses no container queries, no url() and no @import, and the
renderer only sets custom properties and attributes, so a strict style-src policy works.
The stylesheet should be in effect before the chart is created. The chart measures its container when it is created, and a stylesheet that is still loading gives it the unstyled layout to measure.
The unstyled contract
Section titled “The unstyled contract”The JavaScript never imports CSS. @ganttlane/dom/styles.css is a separate file that you import
once per page, so bundlers, CSS-in-JS setups and plain <link> tags all work. You can use it as it
is, add to it with your own rules, or leave it out and style the chart yourself. For the last two,
the DOM structure, the class names and the data-* attributes below are part of the public API:
they change only in a major version. Everything else (the order of siblings inside a group, SVG
geometry attributes such as x, width or d, ids) is an implementation detail.
div.gantt[data-theme][data-height] div.gantt__pin (only with height 'auto': the header sits here) div.gantt__scroller div.gantt__header (inside the scroller unless height is 'auto') div.gantt__header-row[data-unit][data-row] div.gantt__header-cell[data-unit][data-current][data-major] span.gantt__header-label[data-sticky] div.gantt__header-range div.gantt__today-marker svg.gantt__body g.gantt__grid g.gantt__rows rect.gantt__row[data-row][data-task-id][data-stripe][data-invalid][data-placeholder] g.gantt__nonworking[data-mode] rect.gantt__nonworking-day[data-day][data-reason] defs > pattern.gantt__stripes > line.gantt__stripes-line (stripes mode only) g.gantt__highlights rect.gantt__highlight[data-highlight][data-day] g.gantt__gridlines line.gantt__gridline[data-orientation][data-major][data-minor] line.gantt__today g.gantt__links g.gantt__link[data-link-id][data-type][data-selected][data-conflict][data-cycle] path.gantt__link-path[fill=none] path.gantt__link-arrow path.gantt__link-hit[data-link-id][fill=none] g.gantt__bars g.gantt__bar[data-task-id][data-type][data-selected][data-hovered][data-dragging] [data-resizing][data-locked][data-readonly][data-movable][data-invalid] [data-conflict] rect.gantt__bar-track rect.gantt__bar-progress polygon.gantt__bar-milestone (a milestone instead of the two rects) text.gantt__bar-label[data-placement] foreignObject.gantt__bar-label[data-placement] (custom label: instead of the text) div.gantt__bar-label-content rect.gantt__handle[data-handle] circle.gantt__link-handle[data-side] g.gantt__overlay path.gantt__link-preview[data-valid] div.gantt__layer div.gantt__popover[data-kind][data-placement][data-interactive][role=tooltip|menu] div.gantt__popover-title div.gantt__popover-subtitle div.gantt__popover-details div.gantt__popover-actions button.gantt__popover-action[data-action][role=menuitem]Attributes marked optional appear only while they are true. The values of the others:
| Attribute | Values |
|---|---|
data-theme |
light, dark or auto |
data-height |
auto while the height follows the rows (the header is then in div.gantt__pin); absent otherwise |
data-unit |
A time scale unit: day, week, … |
data-handle |
resize-start, resize-end or progress |
data-side |
start or end (the link handles) |
data-orientation |
vertical or horizontal (grid lines) |
data-mode |
fill or stripes (non-working days) |
data-placement |
inside or outside on a label; top or bottom on a popover, after a flip |
data-type |
The task type on a bar, the link type (FS, SS, FF, SF) on a link |
data-day |
A day key: days since 1970-01-01, an integer |
data-reason |
Why a day does not work: weekend, holiday, custom or exception |
data-link-id, data-task-id |
The link’s id (on its group and its hit path), the task’s id (on a bar and on the row of its task) |
data-kind |
What a popover is about: task, link or day |
data-cursor |
On the root: the cursor of the gesture in progress; absent otherwise |
States
Section titled “States”| Attribute | On | Meaning |
|---|---|---|
data-selected |
bar, link | Selected |
data-hovered |
bar | The pointer is over it (the sheet also uses :hover) |
data-dragging, data-resizing |
bar | A drag or a resize of it is in progress |
data-locked |
bar | The task is locked |
data-readonly |
bar | The task cannot be edited |
data-movable |
bar | The task may be moved: its body can be dragged |
data-invalid |
bar, row | The task is invalid |
data-conflict |
bar, link | The schedule violates a link |
data-cycle |
link | The link closes a cycle (drawn dashed) |
data-stripe |
row | Gets the stripe colour |
data-placeholder |
row | The row of a placeholder task |
data-current |
header cell | The cell contains today |
data-major |
header cell, line | A major boundary |
data-minor |
line | A minor line (the snap steps; the sheet draws it dotted) |
data-sticky |
header label | The label stays in view while its cell scrolls out |
data-valid |
link preview | The link being created is allowed |
data-interactive |
popover | Has actions: takes pointer events (the rest do not) |
data-movable is the move permission of the task and nothing else: a bar that may only be
resized or linked does not have it, and neither does a data-readonly or data-locked bar. A
stylesheet can rely on it (.gantt__bar[data-movable] { touch-action: none }), and so does the
renderer’s touch handling: a movable bar takes the whole touch for itself, the others stay
scrollable.
Shipping your own stylesheet
Section titled “Shipping your own stylesheet”To replace styles.css, do not import it, and write a sheet that targets the names above. The
shipped file is a good starting point: copy it from node_modules/@ganttlane/dom/dist/styles.css
and change what you need. The renderer writes four custom properties on .gantt (through the
CSSOM), and a stylesheet that does not use ours has to size the structure from them:
| Property | Meaning |
|---|---|
--gantt-content-width |
Width of the scrolled content: both the header and the body are this wide |
--gantt-header-height |
Height of the sticky header |
--gantt-body-height |
Height of the rows: the body is this tall |
--gantt-height |
Height of the chart when layout.height is a number (else not set) |
On top of that, a replacement sheet must honour these rules:
.ganttfills its container (or is--gantt-heighttall) and is the positioning context of.gantt__layer. Withdata-height="auto"it is as tall as its content..gantt__scrolleris the one scroll container, on both axes (overflow: auto). The renderer owns its scroll position: keepoverflow-anchor: noneandscroll-behavior: auto. It is a keyboard-focusable named region, so the arrow keys scroll it..gantt__headerisposition: sticky; top: 0,--gantt-content-widthwide and--gantt-header-heighttall. The renderer places each header cell, the range and the today marker byleftandwidth(ortransform), so they needposition: absolute.- Header descendants clip with
overflow: clip, neverhidden(norautoorscroll). A label withdata-stickysticks to the left edge of the viewport, which works only while no element between it and the scroller is a scroll container, andoverflow: hiddenmakes its element one. .gantt__bodyisdisplay: block,--gantt-content-widthwide and--gantt-body-heighttall. Positions and sizes inside it are SVG attributes: do not override them with CSS, and never setr(orx,y,width,height,d, …) in CSS, because a CSS value outranks the attribute.- With
data-height="auto", the scroller hasheight: auto,overflow-y: hidden, apadding-block-startof--gantt-header-heightand a negativemargin-block-startof the same..gantt__pinis then the header’s container:position: sticky; top: 0,--gantt-header-heighttall,overflow: hidden, above the scroller and below.gantt__layer. .gantt__link-hitneedsfill: none, a transparent stroke, a widestroke-widthandpointer-events: stroke; the other link paths should not take pointer events..gantt__layer(tooltips and menus) covers the chart, above the scroller, and takes no pointer events itself.- A
.gantt__popoverisposition: absoluteat the top left of the layer. The renderer places it with--gantt-popover-xand--gantt-popover-y(px): move it withtransform: translate(var(--gantt-popover-x, 0), var(--gantt-popover-y, 0)), and give[data-interactive]popoverspointer-events: auto. - A
.gantt__bar-labelis SVG text or HTML in aforeignObject: set bothfillandcolor. - Leave the presentation attributes the renderer writes alone:
fill="none"on the link paths,fill="url(#...)"on.gantt__nonworking-dayinstripesmode (set itsfillonly for.gantt__nonworking[data-mode='fill']), andtext-anchor,dyandoverflowon labels.
Keep selection visible in the way the shipped sheet does: the outline of a selected bar beats the invalid and conflict outlines, which beat the hover outline.
Related
Section titled “Related”- Examples: theming, unstyled, task colours and classes, bar labels, grid and today, release plan.
- Options for the
layout,displayandinteractiongroups, and Accessibility for focus, contrast and motion. - React for the
themeprop and the stylesheet import, andThemeOptionin the API reference.