Skip to content

Accessibility And Keyboard Navigation

Use Tab to move through the toolbar and task table, then test keyboard editing and focus after closing the task dialog. Accessibility support and remaining limits are described below.

Source code
TypeScriptts
// src/components/gantt/gantt-showcase/GanttShowcase.ts
import './gantt-showcase.scss';
import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();

import { GanttPlugin } from '@revolist/gantt';
import { ExportExcelPlugin, RowStatusPlugin } from '@revolist/revogrid-pro';
import {
  STANDARD_CALENDAR,
  SHOWCASE_ASSIGNMENTS,
  SHOWCASE_BASELINES,
  SHOWCASE_COLUMNS_WITH_COMPLETION,
  SHOWCASE_DEFAULT_HIDDEN,
  SHOWCASE_DEPENDENCIES,
  SHOWCASE_GANTT_CONFIG,
  SHOWCASE_RESOURCES,
  SHOWCASE_TASKS,
  renderShowcaseTaskBarColor,
  renderShowcaseTaskBarContent,
} from '../shared/gantt-project-data';
import { currentTheme } from '../../composables/useRandomData';

// ─── Entry point ──────────────────────────────────────────────────────────────

export function load(parentSelector: string): void {
  const parent = document.querySelector(parentSelector);
  if (!parent) return;
  const darkTheme = currentTheme().isDark();

  const container = document.createElement('div');
  container.className = `gantt-showcase-shell grow h-full ${darkTheme ? 'gantt-showcase-shell--dark' : 'gantt-showcase-shell--light'}`;
  parent.appendChild(container);

  const grid = document.createElement('revo-grid') as HTMLRevoGridElement;
  const controls = document.createElement('div');
  controls.className = 'gantt-showcase-controls';
  container.appendChild(controls);

  grid.theme          = darkTheme ? 'darkCompact' : 'compact';
  grid.readonly       = false;
  grid.range          = true;
  grid.resize         = true;
  grid.rowSize        = 42;
  grid.rowHeaders     = false;
  grid.hideAttribution = true;
  grid.autoSizeColumn = false;
  grid.classList.add('gantt-showcase-grid');
  grid.plugins        = [GanttPlugin, ExportExcelPlugin, RowStatusPlugin];
  grid.hideColumns    = [...SHOWCASE_DEFAULT_HIDDEN];
  grid.columns        = [...SHOWCASE_COLUMNS_WITH_COMPLETION];
  grid.source         = [...SHOWCASE_TASKS];
  grid.ganttDependencies = [...SHOWCASE_DEPENDENCIES];
  grid.ganttCalendars    = [{ ...STANDARD_CALENDAR }];
  grid.ganttResources    = [...SHOWCASE_RESOURCES];
  grid.ganttAssignments  = [...SHOWCASE_ASSIGNMENTS];
  grid.ganttBaselines    = [...SHOWCASE_BASELINES];
  let showCriticalPath = Boolean(SHOWCASE_GANTT_CONFIG.visuals.showCriticalPath);
  let showBaseline = false;

  function applyGanttConfig() {
    grid.gantt = {
      ...SHOWCASE_GANTT_CONFIG,
      visuals: {
        ...SHOWCASE_GANTT_CONFIG.visuals,
        showCriticalPath,
        showBaseline,
        taskBarColorHook: renderShowcaseTaskBarColor,
        taskBarContentHook: renderShowcaseTaskBarContent,
      },
    } as typeof grid.gantt;
  }

  function createToggle(label: string, checked: () => boolean, onChange: (value: boolean) => void) {
    const control = document.createElement('label');
    control.className = 'gantt-showcase-control';
    const input = document.createElement('input');
    input.type = 'checkbox';
    input.className = 'gantt-showcase-control__input';
    const text = document.createElement('span');
    text.className = 'gantt-showcase-control__label';
    text.textContent = label;
    const sync = () => {
      input.checked = checked();
    };
    input.addEventListener('change', () => {
      onChange(input.checked);
      applyGanttConfig();
    });
    control.append(input, text);
    sync();
    return control;
  }

  controls.append(
    createToggle('Critical path', () => showCriticalPath, (value) => {
      showCriticalPath = value;
    }),
    createToggle('Baselines', () => showBaseline, (value) => {
      showBaseline = value;
    }),
  );

  applyGanttConfig();
  container.appendChild(grid);
}
Reacttsx
// src/components/gantt/gantt-showcase/GanttShowcase.tsx
import './gantt-showcase.scss';
import React, { useMemo, useRef, useState } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import { GanttPlugin } from '@revolist/gantt';
import { ExportExcelPlugin, RowStatusPlugin } from '@revolist/revogrid-pro';
import {
  STANDARD_CALENDAR,
  SHOWCASE_ASSIGNMENTS,
  SHOWCASE_BASELINES,
  SHOWCASE_COLUMNS_WITH_COMPLETION,
  SHOWCASE_DEFAULT_HIDDEN,
  SHOWCASE_DEPENDENCIES,
  SHOWCASE_GANTT_CONFIG,
  SHOWCASE_RESOURCES,
  SHOWCASE_TASKS,
  renderShowcaseTaskBarColor,
  renderShowcaseTaskBarContent,
} from '../shared/gantt-project-data';
import type { GanttPluginConfig } from '@revolist/gantt';
import { currentTheme } from '../../composables/useRandomData';

const plugins = [GanttPlugin, ExportExcelPlugin, RowStatusPlugin];
const source      = [...SHOWCASE_TASKS];
const dependencies = [...SHOWCASE_DEPENDENCIES];
const calendars    = [{ ...STANDARD_CALENDAR }];
const resources    = [...SHOWCASE_RESOURCES];
const assignments  = [...SHOWCASE_ASSIGNMENTS];
const baselines    = [...SHOWCASE_BASELINES];
const columns      = [...SHOWCASE_COLUMNS_WITH_COMPLETION];
const hiddenColumns = [...SHOWCASE_DEFAULT_HIDDEN];

function GanttShowcase() {
  const { isDark } = currentTheme();
  const darkTheme = isDark();
  const gridRef = useRef<HTMLRevoGridElement>(null);
  const [showCriticalPath, setShowCriticalPath] = useState(Boolean(SHOWCASE_GANTT_CONFIG.visuals.showCriticalPath));
  const [showBaseline, setShowBaseline] = useState(false);
  const ganttConfig: GanttPluginConfig = useMemo(() => ({
    ...SHOWCASE_GANTT_CONFIG,
    visuals: {
      ...SHOWCASE_GANTT_CONFIG.visuals,
      showCriticalPath,
      showBaseline,
      taskBarColorHook: renderShowcaseTaskBarColor,
      taskBarContentHook: renderShowcaseTaskBarContent,
    },
  } as GanttPluginConfig), [showCriticalPath, showBaseline]);

  return (
    <div className={`gantt-showcase-shell grow h-full ${darkTheme ? 'gantt-showcase-shell--dark' : 'gantt-showcase-shell--light'}`}>
      <div className="gantt-showcase-controls">
        <label className="gantt-showcase-control">
          <input
            className="gantt-showcase-control__input"
            type="checkbox"
            checked={showCriticalPath}
            onChange={(event) => setShowCriticalPath(event.currentTarget.checked)}
          />
          <span className="gantt-showcase-control__label">Critical path</span>
        </label>
        <label className="gantt-showcase-control">
          <input
            className="gantt-showcase-control__input"
            type="checkbox"
            checked={showBaseline}
            onChange={(event) => setShowBaseline(event.currentTarget.checked)}
          />
          <span className="gantt-showcase-control__label">Baselines</span>
        </label>
      </div>
      <RevoGrid
        ref={gridRef}
        className="gantt-showcase-grid"
        theme={darkTheme ? 'darkCompact' : 'compact'}
        hideAttribution
        readonly={false}
        range
        resize
        rowSize={42}
        rowHeaders={false}
        autoSizeColumn={false}
        plugins={plugins}
        hideColumns={hiddenColumns}
        source={source}
        columns={columns}
        gantt={ganttConfig}
        ganttDependencies={dependencies}
        ganttCalendars={calendars}
        ganttResources={resources}
        ganttAssignments={assignments}
        ganttBaselines={baselines}
      />
    </div>
  );
}

export default GanttShowcase;
Vuevue
<template>
  <div :class="shellClass">
    <div class="gantt-showcase-controls">
      <label class="gantt-showcase-control">
        <input v-model="showCriticalPath" class="gantt-showcase-control__input" type="checkbox" />
        <span class="gantt-showcase-control__label">Critical path</span>
      </label>
      <label class="gantt-showcase-control">
        <input v-model="showBaseline" class="gantt-showcase-control__input" type="checkbox" />
        <span class="gantt-showcase-control__label">Baselines</span>
      </label>
    </div>
    <RevoGrid
      ref="gridRef"
      class="gantt-showcase-grid skip-style cell-border"
      hide-attribution
      :readonly="false"
      :range="true"
      :resize="true"
      :row-size="42"
      :row-headers="false"
      :auto-size-column="false"
      :theme="gridTheme"
      :plugins="plugins"
      :hide-columns.prop="hiddenColumns"
      :source="source"
      :columns="columns"
      :gantt.prop="ganttConfig"
      :gantt-dependencies.prop="dependencies"
      :gantt-calendars.prop="calendars"
      :gantt-resources.prop="resources"
      :gantt-assignments.prop="assignments"
      :gantt-baselines.prop="baselines"
    />
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import RevoGrid from '@revolist/vue3-datagrid';
import { ExportExcelPlugin, RowStatusPlugin } from '@revolist/revogrid-pro';
import {
  STANDARD_CALENDAR,
  SHOWCASE_ASSIGNMENTS,
  SHOWCASE_BASELINES,
  SHOWCASE_COLUMNS_WITH_COMPLETION,
  SHOWCASE_DEFAULT_HIDDEN,
  SHOWCASE_DEPENDENCIES,
  SHOWCASE_GANTT_CONFIG,
  SHOWCASE_RESOURCES,
  SHOWCASE_TASKS,
  renderShowcaseTaskBarColor,
  renderShowcaseTaskBarContent,
} from '../shared/gantt-project-data';
import { currentThemeVue } from '../../composables/useRandomData';

// ── Static grid data ──────────────────────────────────────────────────────────
const plugins = ref<unknown[]>([]);
const source      = ref([...SHOWCASE_TASKS]);
const dependencies = ref([...SHOWCASE_DEPENDENCIES]);
const calendars    = ref([{ ...STANDARD_CALENDAR }]);
const resources    = ref([...SHOWCASE_RESOURCES]);
const assignments  = ref([...SHOWCASE_ASSIGNMENTS]);
const baselines    = ref([...SHOWCASE_BASELINES]);
const columns      = ref([...SHOWCASE_COLUMNS_WITH_COMPLETION]);
const hiddenColumns = [...SHOWCASE_DEFAULT_HIDDEN];
const showCriticalPath = ref(Boolean(SHOWCASE_GANTT_CONFIG.visuals.showCriticalPath));
const showBaseline = ref(false);
const { isDark } = currentThemeVue();
const gridTheme = computed(() => (isDark.value ? 'darkCompact' : 'compact'));
const shellClass = computed(() => [
  'gantt-showcase',
  'gantt-showcase-shell',
  'grow',
  'h-full',
  isDark.value ? 'gantt-showcase-shell--dark' : 'gantt-showcase-shell--light',
]);

const ganttConfig = computed(() => ({
  ...SHOWCASE_GANTT_CONFIG,
  visuals: {
    ...SHOWCASE_GANTT_CONFIG.visuals,
    showCriticalPath: showCriticalPath.value,
    showBaseline: showBaseline.value,
    taskBarColorHook: renderShowcaseTaskBarColor,
    taskBarContentHook: renderShowcaseTaskBarContent,
  },
}));

// ── Refs ──────────────────────────────────────────────────────────────────────
const gridRef    = ref<InstanceType<typeof RevoGrid> | HTMLRevoGridElement | null>(null);

onMounted(async () => {
  const { GanttPlugin } = await import('@revolist/gantt');

  plugins.value = [GanttPlugin, ExportExcelPlugin, RowStatusPlugin];
});
</script>

<style src="./gantt-showcase.scss" lang="scss"></style>

<style scoped>
.gantt-showcase :deep(revo-grid) {
  flex: 1;
  min-height: 0;
}
</style>
Angularts
// src/components/gantt/gantt-showcase/GanttShowcaseAngular.ts
import {
  Component,
  NO_ERRORS_SCHEMA,
  ViewEncapsulation,
} from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import { GanttPlugin } from '@revolist/gantt';
import { ExportExcelPlugin, RowStatusPlugin } from '@revolist/revogrid-pro';
import type { GanttPluginConfig } from '@revolist/gantt';
import {
  STANDARD_CALENDAR,
  SHOWCASE_ASSIGNMENTS,
  SHOWCASE_BASELINES,
  SHOWCASE_COLUMNS_WITH_COMPLETION,
  SHOWCASE_DEFAULT_HIDDEN,
  SHOWCASE_DEPENDENCIES,
  SHOWCASE_GANTT_CONFIG,
  SHOWCASE_RESOURCES,
  SHOWCASE_TASKS,
  renderShowcaseTaskBarColor,
  renderShowcaseTaskBarContent,
} from '../shared/gantt-project-data';
import { currentTheme } from '../../composables/useRandomData';

function createGanttConfig(showCriticalPath: boolean, showBaseline: boolean): GanttPluginConfig {
  return {
    ...SHOWCASE_GANTT_CONFIG,
    visuals: {
      ...SHOWCASE_GANTT_CONFIG.visuals,
      showCriticalPath,
      showBaseline,
      taskBarColorHook: renderShowcaseTaskBarColor,
      taskBarContentHook: renderShowcaseTaskBarContent,
    },
  } as GanttPluginConfig;
}

@Component({
  selector: 'gantt-showcase-grid',
  standalone: true,
  host: {
    class: 'gantt-showcase-angular-host',
  },
  // Allows Angular demos to bind RevoGrid plugin props that are not wrapper inputs.
  schemas: [NO_ERRORS_SCHEMA],
  imports: [RevoGrid],
  encapsulation: ViewEncapsulation.None,
  styleUrls: ['./gantt-showcase.scss'],
  template: `
    <div [class]="shellClass">
      <div class="gantt-showcase-controls">
        <label class="gantt-showcase-control">
          <input
            class="gantt-showcase-control__input"
            type="checkbox"
            [checked]="showCriticalPath"
            (change)="setCriticalPath($any($event.target).checked)"
          />
          <span class="gantt-showcase-control__label">Critical path</span>
        </label>
        <label class="gantt-showcase-control">
          <input
            class="gantt-showcase-control__input"
            type="checkbox"
            [checked]="showBaseline"
            (change)="setBaseline($any($event.target).checked)"
          />
          <span class="gantt-showcase-control__label">Baselines</span>
        </label>
      </div>
      <revo-grid
        class="gantt-showcase-grid skip-style cell-border"
        [theme]="theme"
        [hideAttribution]="true"
        [readonly]="false"
        [range]="true"
        [resize]="true"
        [rowSize]="42"
        [rowHeaders]="false"
        [autoSizeColumn]="false"
        [plugins]="plugins"
        [hideColumns]="hiddenColumns"
        [source]="source"
        [columns]="columns"
        [gantt]="ganttConfig"
        [ganttDependencies]="dependencies"
        [ganttCalendars]="calendars"
        [ganttResources]="resources"
        [ganttAssignments]="assignments"
        [ganttBaselines]="baselines"
      ></revo-grid>
    </div>
  `,
})
export class GanttShowcaseGridComponent {
  readonly isDark       = currentTheme().isDark();
  readonly theme        = this.isDark ? 'darkCompact' : 'compact';
  readonly shellClass   = `gantt-showcase-shell grow h-full ${this.isDark ? 'gantt-showcase-shell--dark' : 'gantt-showcase-shell--light'}`;
  readonly plugins      = [GanttPlugin, ExportExcelPlugin, RowStatusPlugin];
  showCriticalPath      = Boolean(SHOWCASE_GANTT_CONFIG.visuals.showCriticalPath);
  showBaseline          = false;
  ganttConfig           = createGanttConfig(this.showCriticalPath, this.showBaseline);
  readonly source       = [...SHOWCASE_TASKS];
  readonly dependencies = [...SHOWCASE_DEPENDENCIES];
  readonly calendars    = [{ ...STANDARD_CALENDAR }];
  readonly resources    = [...SHOWCASE_RESOURCES];
  readonly assignments  = [...SHOWCASE_ASSIGNMENTS];
  readonly baselines    = [...SHOWCASE_BASELINES];
  readonly columns      = [...SHOWCASE_COLUMNS_WITH_COMPLETION];
  readonly hiddenColumns = [...SHOWCASE_DEFAULT_HIDDEN];

  setCriticalPath(value: boolean): void {
    this.showCriticalPath = value;
    this.ganttConfig = createGanttConfig(this.showCriticalPath, this.showBaseline);
  }

  setBaseline(value: boolean): void {
    this.showBaseline = value;
    this.ganttConfig = createGanttConfig(this.showCriticalPath, this.showBaseline);
  }
}

Gantt inherits grid-cell focus, selection, editing, and keyboard navigation from RevoGrid. Timeline gestures supplement those workflows; they must not be the only way to change or inspect a task.

  • Provide meaningful task, resource, column, and toolbar labels.
  • Keep focus outlines visible and meet contrast requirements in custom task-bar colors.
  • Expose move, resize, progress, dependency, and hierarchy actions through task-table fields, menus, or application controls in addition to pointer gestures.
  • Keep the packaged task editor available when complex fields cannot be edited comfortably inline.
  • Preserve expected Tab, Enter, Escape, and arrow-key behavior in custom editors.
  • Announce validation errors next to the affected input and keep canceled edits focused.
  • Do not place essential information only in color, hover tooltips, or dependency geometry.

When using taskBarContentHook or custom cell templates, return concise visible content and keep the task name available in the task table. Custom toolbar buttons need native button semantics, accessible names, and pressed state where applicable.

Test Gantt with keyboard-only navigation, browser zoom, high contrast, and a screen reader in the frameworks your application supports. See Task Editing and Visual Configuration.