Accessible Calendar and Timeline Views

Permalink to "Accessible Calendar and Timeline Views"

Calendar and timeline views turn date data into space: a month grid of bookings, a week schedule of shifts, a Gantt chart of project tasks, a resource timeline of machines. The layout carries the meaning — overlap, sequence, gaps — and that is exactly what screen reader users cannot see. Many calendar components render events as absolutely positioned divs with no structure, so a screen reader reads a jumble of titles with no dates. Most also make dragging the only way to move or resize an event.

This guide belongs to accessible date & time data.

Spec reference

Permalink to "Spec reference"
  • SC 1.3.1 Info and Relationships (A) — the relationship between an event and its date and time must be programmatic: an event inside a day cell with a header, or an event whose name includes its time.
  • SC 1.3.2 Meaningful Sequence (A) — DOM order should follow chronological order even if events are positioned visually.
  • SC 2.1.1 Keyboard (A) — navigation, selection, creation and editing by keyboard.
  • SC 2.5.7 Dragging Movements (AA) — moving and resizing events need single-pointer alternatives (edit dialog, menu).
  • SC 1.4.1 Use of Color (A) — event categories and statuses need text, not just colour.

A month view maps naturally to role="grid" with weekday column headers. Week and timeline views are better served by a list of days, each containing a list of events, with the visual timeline as presentation.

Positioned divs versus structured calendar Comparison: absolutely positioned event divs read as a jumble; a structured calendar groups events under days and names each with its time. Positioned divs versus structured calendar✗ Positioned event divsTitles read without datesReading order follows DOM, not timeCategory by colour onlyMove only by draggingNo overview of busy days✓ Structured calendar"Design review, Thursday 2 October, 10:00 to11:00"DOM in chronological orderCategory in the name: "Customer meeting"Move via edit dialog or menuDay cell: "Thursday 2 October, 3 events"

When to use — and when not to

Permalink to "When to use — and when not to"

Use calendar or timeline views when spatial overview matters — finding gaps, spotting overlaps, seeing a project’s critical path. Always provide an agenda or table view of the same data; for many tasks (what is next, what is on Friday) it is faster for everyone, and it is the primary view for many screen reader users.

Do not encode information only in position or length. A Gantt bar’s length is its duration; say the duration in the bar’s name. The misapplication: a resource timeline where the only way to discover a conflict is to see two bars overlap.

A month grid with event counts Month grid mock for a week in October: each day cell shows its date and event count, with one day focused; weekday headers above. A month grid with event countsMonTueWedThuFri28 · 1 event2930 · 2 events12 · 3 events56 · 1 event78 · 4 events9Focused: Friday 2 October, 3 events. A conflict on 8 October is named "4 events, 1 conflict" rather than shown byoverlap alone.
Day cells name the date and the number of events; Enter opens the day's event list.

Annotated code example

Permalink to "Annotated code example"
<!-- Month view: grid of days; events listed inside each day -->
<table role="grid" aria-labelledby="month-h">
  <caption id="month-h">October 2026</caption>
  <thead><tr><th scope="col" abbr="Monday">Mon</th>…</tr></thead>
  <tbody>
    <tr>
      <!-- Day cell name: date and count (SC 1.3.1) -->
      <td role="gridcell" tabindex="0" aria-label="Friday 2 October, 3 events">
        <span aria-hidden="true">2</span>
        <ul class="events">
          <!-- Event name includes time, duration and category (SC 1.3.1, 1.4.1) -->
          <li><a href="/events/41">Design review, 10:00 to 11:00, internal</a></li>
          <li><a href="/events/42">Lund &amp; Co call, 13:30 to 14:00, customer</a></li>
          <li><a href="/events/43">Release, all day</a></li>
        </ul>
      </td>
    </tr>
  </tbody>
</table>
<a href="?view=agenda">Switch to agenda view</a>   <!-- equivalent alternative -->
// Grid navigation by day and week; Enter moves into the day's event list
cal.addEventListener('keydown', (e) => {
  const step = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }[e.key];
  if (step) { e.preventDefault(); focusDay(addDays(current, step)); }
  if (e.key === 'PageUp' || e.key === 'PageDown') { e.preventDefault(); focusDay(addMonths(current, e.key === 'PageUp' ? -1 : 1)); }
  if (e.key === 'Enter') { e.preventDefault(); activeCell().querySelector('.events a')?.focus(); }
  if (e.key === 'Escape' && e.target.closest('.events')) { e.preventDefault(); activeCell().focus(); }
});

Keyboard & AT behaviour

Permalink to "Keyboard & AT behaviour"
Key Action Expected announcement AT deviations
Arrows Move by day / week “Friday 2 October, 3 events” Column header (weekday) read on change by some readers
Page Up / Down Previous / next month “October 2026” then the day —
Enter Into the day’s events “Design review, 10:00 to 11:00, internal, link” —
Tab (inside day) Next event Event name —
Escape Back to the day cell Day name —
E or Enter on an event Open edit dialog (move or resize) Dialog with start and end fields —
Calendar view types and their best structure Matrix of view types — month, week, day schedule, Gantt, resource timeline — against recommended structure, primary keyboard model and the alternative view to offer. Calendar view types and their best structureStructureKeyboard modelAlternativeMonthGrid of daysArrows by day and weekAgenda listWeekList of days with eventlistsTab or arrows through eventsAgenda listDay scheduleList in time orderArrows through slotsAgenda listGanttTreegrid of tasksArrows through tasksTask table with datesResource timelineGrid of resources by timeComplexTable per resource
Every view has an agenda or table alternative.

Integration context

Permalink to "Integration context"

Date picking inside events uses the date picker grid keyboard pattern. Moving events without dragging mirrors keyboard row reordering with move buttons. Gantt task lists are treegrids — see building an accessible treegrid with expandable rows. Event durations follow formatting durations for screen readers.

Gotchas

Permalink to "Gotchas"

Overflow events. “+3 more” links must say which day and how many: “Show 3 more events on 8 October”.

All-day and multi-day events. An event spanning several days should appear once in each day’s list with “day 2 of 3” in its name, or once with its range — not as an unlabelled bar crossing cells.

Time zones. Event times shown in the viewer’s zone need the zone stated once, in the view’s heading or toolbar, as in announcing time zones in data tables.

Framework notes

Permalink to "Framework notes"

FullCalendar renders month views as tables and supports keyboard focus on events, but drag is the default way to move; add an edit dialog with start and end fields, and enable listWeek or listMonth views as agenda alternatives. React Big Calendar has an agenda view; its month view needs extra ARIA work. Gantt libraries vary widely; prefer ones that expose a task table and test keyboard access before adopting.

// FullCalendar: include an agenda alternative in the header toolbar
new Calendar(el, { headerToolbar: { right: 'dayGridMonth,timeGridWeek,listMonth' }, eventStartEditable: true });

Worked example

Permalink to "Worked example"

A clinic’s appointment calendar showed a week grid of coloured blocks. A receptionist using JAWS could not find free slots and booked over existing appointments by mistake. The fix added an agenda view as the default for her account, named every block “Room 2, 09:00 to 09:30, follow-up, Ana P.” with the category in words, made each day a list in time order, and replaced drag-to-move with an “Edit time” dialog. Free slots are listed as items too (“Room 2, 09:30 to 10:00, free”), so finding gaps no longer depends on seeing white space.

Testing checklist

Permalink to "Testing checklist"

Design system notes

Permalink to "Design system notes"

Separate the calendar’s data model from its layouts so month, week, timeline and agenda views render the same events with the same names. Provide event-name templates (title, time range, category, status) and require an agenda view in every calendar instance.

FAQ

Permalink to "FAQ"
Should a month view be a table or a grid?

If users only read it, a table works. If they move focus between days and act on them, use role=“grid” so screen readers pass arrow keys through.

Is an agenda view enough on its own?

For many users and tasks, yes, and it should be easy to choose. The visual calendar remains useful for overview, so keep both.

How do I make a Gantt chart accessible?

Pair it with a task table or treegrid that includes start, end, duration, dependencies and status. The bars become a visual summary of the table.

How should overlapping events be described?

In words: “Overlaps with Lund & Co call” in the event’s description, and a conflict count in the day cell.

Should free time be listed?

In scheduling tools, yes — as items in the agenda or day list. Free time is information users are looking for.

Permalink to "Related"

← Back to Accessible Date & Time Data