Accessibility
Learn how the Scheduler implements accessibility features and guidelines, including ARIA semantics, keyboard navigation, and localization of accessible labels.
Guidelines
Common conformance guidelines for accessibility include:
- Globally accepted standard: WCAG
- US:
- ADA - US Department of Justice
- Section 508 - US federal agencies
- Europe: EAA (European Accessibility Act)
WCAG 2.2 has three levels of conformance: A, AA, and AAA. Level AA exceeds the basic criteria for accessibility and is a common target for most organizations, so this is what we aim to support.
The WAI-ARIA Authoring Practices provide valuable information on how to optimize the accessibility of a scheduler component.
ARIA structure and semantics
The Scheduler uses WAI-ARIA grid and tree roles to expose its structure to assistive technologies.
Week and Day views
The day-time grid is exposed as a role="grid" with three logical rows:
| Row | aria-rowindex |
Content |
|---|---|---|
| Header | 1 |
Day column headers β role="columnheader" with an aria-label containing the full weekday name and date (for example, "Monday 26") |
| All-day | 2 |
All-day event cells β role="gridcell" with aria-labelledby pointing to the column header. The visible "All day" label is aria-hidden="true" and referenced via aria-labelledby on each cell |
| Timed | 3 |
Per-column time cells |
The grid also exposes aria-colcount for the number of visible days and aria-colindex on every cell.
The time axis (hour labels and grid lines) is aria-hidden="true" as it is decorative.
Month view
The month grid uses the same role="grid" pattern with aria-rowcount, aria-colcount, aria-rowindex, and aria-colindex.
Each weekday header cell carries a role="columnheader".
When showWeekNumber is enabled, the week-number labels are aria-hidden="true" but each day cell in that row references the week-number element via aria-labelledby, so screen readers can announce it.
Mini calendar (side panel)
The mini calendar is exposed as a role="grid" with an aria-label (localized β default: "Calendar").
The grid body is wrapped in a role="rowgroup" element. Each day cell consists of a role="gridcell" container (with aria-colindex) wrapping a button element.
The button element carries:
aria-labelβ the full formatted date stringaria-current="date"β on today's datearia-selectedβ on the currently active (selected) date- A roving
tabIndexβ0on the active cell,-1on all others
Weekday column headers carry a role="columnheader" with an aria-label containing the full weekday name (for example, "Sunday").
Events
Each event element has an aria-labelledby that composes the day column header ID and the event's own title element ID, so a screen reader announces the day context alongside the event title.
Multi-day events are rendered once as the main (visible) element and additionally as invisible placeholder elements in the spanned cells. The placeholder elements carry aria-hidden="true" so assistive technologies see only one announcement per event.
The resource color indicator inside an event uses role="img" with an aria-label describing the resource (for example, "Resource: Sport"). When no resource is assigned the label falls back to the localized noResourceAriaLabel value.
Recurring event icons are aria-hidden="true" as they are decorative.
Agenda view
Each day group in the agenda carries aria-labelledby pointing to its day header cell. The day header cell itself has an aria-label composed of the weekday name and day-of-month (for example, "Monday 26").
Main calendar region
The main calendar view area is rendered as a <section> element with a localized aria-label (default: "Calendar content"), providing a named landmark region that assistive technology users can navigate to directly.
Side panel
The inline side panel (mini calendar, divider, and resources tree) is rendered as an <aside> element, exposing the complementary landmark role. The header toolbar's toggle button references it via aria-controls and reflects its open state with aria-expanded.
While the panel is collapsed, it carries aria-hidden="true" so its content β including the mini calendar's focusable active day β is removed from the accessibility tree. The attribute is applied once the collapse animation has finished, and cleared when the panel reopens.
Keyboard interactions
Calendar grid
The Week, Day, and Month views implement a role="grid" with full arrow key navigation between cells.
Week and Day views expose three logical rows: the header row (day names), the all-day row, and the timed-events row. Arrow keys move across all three rows.
Month view exposes the header row and one day-grid row per week. Arrow keys move between the header and each week row, and between individual weeks.
| Keys | Description |
|---|---|
| Arrow Left | Moves focus to the previous column in the same row |
| Arrow Right | Moves focus to the next column in the same row |
| Arrow Up | Moves focus to the same column in the row above. In Week/Day view: from all-day row to header, or from time-grid row to all-day. In Month view: from a week row to the one above, or from the first week row to the header |
| Arrow Down | Moves focus to the same column in the row below |
| Enter | On a day-grid or time-grid cell: starts event creation for that date or time slot. On a header cell: activates the day button (navigates to that day's view) |
Tab sequence
All grid cells have tabIndex="0", so the Tab key flows through the grid cell by cell in document order. When a focused cell contains events, Tab enters the events inside it before moving to the next cell.
Resources sidebar
The Resources sidebar uses the MUIΒ X Rich Tree View internally and inherits its full keyboard behavior. See the Tree View accessibility page for the complete keyboard interaction reference.
Space additionally toggles the visibility (checked state) of the focused resource.
Menus and popovers
| Keys | Description |
|---|---|
| Escape | Closes the View Switcher menu, Preferences menu, More Events popover, or event context menu |
| Enter | Activates (opens) a focused event |
| Space | Opens the event context menu for a focused event (mouse/trackpad only β see below) |
Live region announcements
The current month and year label in the header toolbar is wrapped in an aria-live="polite" region.
When a user navigates to the previous or next time span (for example, clicking Previous week), the updated toolbar label is automatically announced by screen readers without requiring focus to move.
Event dialog
The Event Dialog is a non-modal dialog (aria-modal="false") that floats next to the event that opened it.
The dialog is labeled by its event title via aria-labelledby.
Read-only dialog
- The Close button carries a localized
aria-label(default:"Close").
Editable dialog
- The event title input is labeled with a localized
aria-label(default:"Event title"). - The color picker group has an
aria-label(default:"Event color"), and each individual color option button is labeled (for example,"Select green as event color"). - When the dialog shows the General and Recurrence tabs, the General tab panel uses
role="tabpanel"witharia-labelledbypointing to its tab; without tabs the panel claims no tab semantics. The panel and its attributes stay owned by the dialog when the tab content is replaced through theeventDialogGeneralTabslot. - The Recurrence tab panel uses
role="tabpanel"witharia-labelledbypointing to its tab. - The recurring scope confirmation dialog radio group has an
aria-label(default:"Editing recurring events scope").
More Events popover
When a month cell has more events than can be displayed, a "X more" button opens a popover listing all events for that day.
- The popover header element carries an
aria-labelwith the full formatted date (for example,"Monday, May 26"). - Each event inside the popover uses
aria-labelledbythat composes the popover header ID and the event title element ID, so screen readers announce the day context alongside the event title. - Event items have
role="button"withtabIndex="0", and can be activated with Enter. On a mouse/trackpad, Space opens the event context menu instead of activating the event directly. - Editing an event from the popover closes the popover with it. When focus would otherwise be lost, it returns to the "X more" button that opened it, or to the day cell if editing left the day with too few events for that button to be displayed. Focus that already moved outside the popover is preserved.
- Deleting an event from the popover's context menu does not close the popover.
Preferences menu
The Preferences menu button exposes aria-haspopup="true", aria-expanded, and aria-controls.
Inside the menu:
- Toggle items (for example, Show weekends, Show week number) use
role="menuitemcheckbox"witharia-checked. - Time format options use
role="menuitemradio"witharia-checked.
Event context menu
Right-clicking an event, or pressing Space while it is focused, opens a context menu with Edit and Delete actions.
- The menu exposes
role="menu"(via MUI'sMenu) with a localizedaria-label(default:"Event actions"). - Edit opens the event dialog directly, the same as clicking the event on a mouse/trackpad.
- Delete removes the event immediately. For a recurring event it opens the recurring scope confirmation dialog instead, the same as the Delete action in the event dialog and the armed-event toolbar.
- For a read-only event, Edit is replaced by Show details β it opens the same read-only view a click would, and Delete is omitted, matching every other surface's mutation gate (the dialog swaps to its read-only content, and the armed toolbar never appears for one).
The menu is suppressed on a coarse pointer (touch): activating an event there arms its toolbar instead of opening the dialog directly β which already exposes Edit and Delete β so right-click and Space fall through to that same behavior rather than opening a redundant menu. This check is on pointer type alone; it doesn't depend on which view or layout is rendering the event.
Localization of ARIA labels
All user-facing accessible labels are provided through the localeText prop and can be customized.
The following keys are specifically relevant to accessibility:
{
// Main calendar region
calendarContentAriaLabel: 'Calendar content',
// Event dialog
closeButtonAriaLabel: 'Close',
eventTitleAriaLabel: 'Event title',
colorPickerLabel: 'Event color',
selectColorAriaLabel: (color) => `Select ${color} as event color`,
radioGroupAriaLabel: 'Editing recurring events scope',
// Event context menu
eventContextMenuAriaLabel: 'Event actions',
showEventDetails: 'Show details',
// Events
noResourceAriaLabel: 'No specific resource',
resourceAriaLabel: (resourceName) => `Resource: ${resourceName}`,
hiddenEvents: (count) => `${count} more..`,
// Month view
weekNumberAriaLabel: (weekNumber) => `Week ${weekNumber}`,
// Mini calendar
miniCalendarLabel: 'Calendar',
miniCalendarGoToPreviousMonth: 'Show previous month in calendar',
miniCalendarGoToNextMonth: 'Show next month in calendar',
// Header toolbar
previousTimeSpan: (timeSpan) => `Previous ${timeSpan}`,
nextTimeSpan: (timeSpan) => `Next ${timeSpan}`,
openSidePanel: 'Open side panel',
closeSidePanel: 'Close side panel',
}
See the Event Calendar localization page and the Event Timeline localization page for the full list of available keys and instructions on how to apply them.
Known limitations
No arrow key navigation in the mini calendar
The mini calendar in the side panel exposes role="grid" and a roving tabIndex on the active date, but arrow key navigation between day cells is not implemented. Clicking a date or using the previous/next month buttons are the only supported interaction methods.