# Gantt Timeline and Zoom
URL: https://pro.rv-grid.com/guides/gantt/customization/timeline-zoom/
Source: src/content/docs/guides/gantt/customization/timeline-zoom.mdx
Description: Configure timeline scale, zoom behavior, wheel interaction, and visual timeline overlays.
The Gantt timeline can be configured at two levels:

1. `zoomPreset` for fast setup with built-in levels.
2. `zoom` for full control over levels, wheel behavior, and anchor strategy.

Use `weekStartsOn` when week headers or week ticks should align to a specific
weekday. Gantt defaults to Sunday-start weeks.

---

## Quick Start with `zoomPreset`

Use a built-in preset when you only need a standard timeline scale:

```typescript
grid.gantt = {
  // ...required project fields
  zoomPreset: 'week-month',
};
```

Supported presets:

- `'minute-hour'` (15-minute ticks grouped by hour)
- `'hour-day'` (hour ticks grouped by day)
- `'day-week'`
- `'week-month'`
- `'month-quarter'`
- `'quarter-year'`
- `'year-quarter'`
- `'multi-year-quarter'`

By default, the interactive zoom ladder starts at `day-week` and excludes the intraday presets for performance. Use `zoomPreset: 'hour-day'`, `zoomPreset: 'minute-hour'`, or explicit `zoom.levels` when an intraday timeline is required.

## Week Start

Week-based timeline headers, week ticks, and padded week ranges use Sunday by default:

```typescript
grid.gantt = {
  // ...required project fields
  weekStartsOn: 0, // Sunday, default
};
```

Set `weekStartsOn: 1` when the Gantt should use Monday-start weeks:

```typescript
grid.gantt = {
  // ...required project fields
  weekStartsOn: 1, // Monday
  zoomPreset: 'week-month',
};
```

`weekStartsOn` accepts `0` for Sunday or `1` for Monday. It affects timeline week boundaries only; task calendars still use their own `workingDays` configuration.

## Time Modes

Gantt timeline modes are defined by `tickUnit` + `headerRows` in each zoom level.

- `minute-hour`: 15-minute timeline resolution for intraday planning windows.
- `hour-day`: hourly timeline resolution grouped under day headers.
- `day-week`: daily planning view.
- `week-month`: weekly planning view.
- `month-quarter`: monthly planning view.
- `quarter-year`, `year-quarter`, `multi-year-quarter`: portfolio and long-range views.

Recommended usage pattern:

- Opt into `minute-hour` for short execution windows and handoffs.
- Opt into `hour-day` for same-day and next-day coordination.
- Use `day-week` or coarser for baseline and milestone planning.

  Intraday modes (`minute-hour`, `hour-day`) control timeline granularity and interaction scale. Temporal fields still use the same names, but when you need intraday precision you should pass UTC datetime values (`...Z`) instead of date-only strings.

## Hour Mode

Use hour mode when task bars should move, resize, and create on hour boundaries
instead of whole-day boundaries. The important settings are:

- `timelinePrecision: 'hour'` makes timeline interactions use hour precision by default.
- `zoomPreset: 'hour-day'` starts the visible timeline with one column per hour.
- `snap.unit: 'hour'` forces task create, move, and resize snapping to hours even if the active zoom level changes later.
- `snap.workingTime` controls whether snapping can use working-time rules. Closed weekdays, holidays, and closed hours are skipped only when `scheduling.excludeHolidaysFromDuration` is also `true`.

```typescript
grid.gantt = {
  // ...required project fields
  timelinePrecision: 'hour',
  zoomPreset: 'hour-day',
  snap: {
    unit: 'hour',
    workingTime: true,
  },
  scheduling: {
    excludeHolidaysFromDuration: true,
  },
  calendars: [
    {
      id: 'standard',
      name: 'Standard',
      workingDays: [1, 2, 3, 4, 5],
      workingHours: [{ start: '09:00', end: '17:00' }],
      hoursPerDay: 8,
    },
  ],
  tasks: [
    {
      id: 'task-1',
      name: 'Same-day implementation',
      startDate: '2026-04-06T09:00:00.000Z',
      endDate: '2026-04-06T13:00:00.000Z',
    },
  ],
};
```

  `timelinePrecision` controls interaction precision. `zoomPreset` and `zoom.defaultLevelId`
  control the initial visible scale. Keep them aligned for an hour-first experience,
  or set `zoom.defaultLevelId: 'day-week'` if you want hour snapping while opening
  on a coarser planning view.

When `snap.workingTime` is `true` or omitted and
`scheduling.excludeHolidaysFromDuration` is `true`, hour snapping uses the
primary project calendar:

- A drag before `09:00` snaps forward to `09:00`.
- A drag after `17:00` snaps forward to the next working opening.
- Weekend or holiday drags snap to the next or previous working opening depending on the interaction direction.

Set `snap.workingTime: false` when users should be able to place tasks in
non-working hours while still snapping to the start of each hour.

If `scheduling.excludeHolidaysFromDuration` is `false` or omitted, hour
snapping rounds to the start of the hour and does not move drags out of
non-working time.

## Full Zoom Configuration

Use `zoom` when you need custom levels, min/max bounds, locale, or wheel interaction rules.

```typescript
grid.gantt = {
  // ...required project fields
  zoomPreset: 'week-month',
  zoom: {
    enabled: true,
    defaultLevelId: 'hour-day',
    minLevelId: 'minute-hour',
    maxLevelId: 'quarter-year',
    locale: 'en-US',
    zoomAnchorMode: 'pointer',
    wheelZoomEnabled: true,
    wheelZoomTrigger: 'ctrlKey',
    wheelZoomMode: 'discrete',
    invertWheelDirection: false,
  },
};
```

  If both `zoomPreset` and `zoom.defaultLevelId` are provided, the resolved zoom config uses the `zoom` values where specified.

## Custom Zoom Levels

You can define your own `zoom.levels` list from finest to coarsest.

```typescript
grid.gantt = {
  // ...required project fields
  zoom: {
    levels: [
      {
        id: 'minute-hour',
        label: '15 Min / Hour',
        tickUnit: 'minute',
        tickCount: 15,
        tickWidth: 44,
        headerRows: [
          { id: 'day', unit: 'day' },
          { id: 'hour', unit: 'hour' },
          { id: 'minute', unit: 'minute', count: 15 },
        ],
      },
      {
        id: 'hour-day',
        label: 'Hour / Day',
        tickUnit: 'hour',
        tickWidth: 52,
        headerRows: [
          { id: 'day', unit: 'day' },
          { id: 'hour', unit: 'hour' },
        ],
      },
      {
        id: 'day-week',
        label: 'Day / Week',
        tickUnit: 'day',
        tickWidth: 56,
        headerRows: [
          { id: 'week', unit: 'week' },
          { id: 'day', unit: 'day' },
        ],
      },
      {
        id: 'week-month',
        label: 'Week / Month',
        tickUnit: 'week',
        tickWidth: 84,
        headerRows: [
          { id: 'month', unit: 'month' },
          { id: 'week', unit: 'week' },
        ],
      },
      {
        id: 'month-quarter',
        label: 'Month / Quarter',
        tickUnit: 'month',
        tickWidth: 120,
        headerRows: [
          { id: 'year', unit: 'year' },
          { id: 'month', unit: 'month' },
        ],
      },
    ],
    defaultLevelId: 'hour-day',
  },
};
```

## Wheel Zoom Options

Control whether users can zoom with the mouse wheel and which modifier key is required.

```typescript
grid.gantt = {
  // ...required project fields
  zoom: {
    wheelZoomEnabled: true,
    wheelZoomTrigger: 'metaKey', // macOS Cmd key
    wheelZoomMode: 'smooth-discrete',
    invertWheelDirection: false,
  },
};
```

Available `wheelZoomTrigger` values:

- `'ctrlKey'`
- `'metaKey'`
- `'altKey'`
- `'shiftKey'`
- `'none'`

## Anchor Behavior During Zoom

`zoomAnchorMode` controls which point in the viewport stays stable when switching levels:

- `'pointer'`: keeps the date under the mouse pointer fixed.
- `'center'`: keeps the center date fixed.
- `'start'`: keeps the left edge date fixed.

```typescript
grid.gantt = {
  // ...required project fields
  zoom: {
    zoomAnchorMode: 'center',
  },
};
```

## Timeline Overlays

Timeline visuals are configured in `visuals`.

```typescript
grid.gantt = {
  // ...required project fields
  visuals: {
    projectLineDate: '2026-04-06',
    timeRanges: [
      {
        id: 'sprint-1',
        startDate: '2026-04-06',
        endDate: '2026-04-17',
        label: 'Sprint 1',
        color: '#5B8DEF',
      },
      {
        id: 'freeze',
        startDate: '2026-05-18',
        endDate: '2026-05-22',
        label: 'Code Freeze',
        color: '#F59E0B',
      },
    ],
    shadeNonWorkingTime: true,
  },
};
```

  `shadeNonWorkingTime` is accepted in both `zoom` and `visuals`. If both are set, zoom-level setting takes priority.
