Accessibility
Keyboard navigation, ARIA attributes, and screen reader support in @gentleduck/calendar.
The calendar follows the WAI-ARIA Grid Pattern for date pickers. Prop getters apply all ARIA attributes automatically.
ARIA roles and attributes
| Element | Role / Attribute | Value |
|---|---|---|
| Grid container | role="grid" | - |
| Grid container | aria-roledescription | "calendar" |
| Day cell | role="gridcell" | - |
| Day cell | aria-label | Full date (e.g. "Saturday, March 14, 2026") |
| Day cell | aria-selected | true when selected |
| Day cell | aria-disabled | true when disabled |
| Day cell | aria-current | "date" for today |
| Nav wrapper | role="navigation" | Set by the CalendarNav primitives compound component, not by the hook |
| Nav button | aria-label | "Go to previous month" / "Go to next month" (from getNavProps) |
| Nav button | disabled | true when navigation in that direction is not allowed (from getNavProps) |
| Month header | aria-live | "polite" |
Keyboard navigation
| Key | Action |
|---|---|
| ArrowLeft / ArrowRight | Move focus +/- 1 day |
| ArrowUp / ArrowDown | Move focus +/- 1 week |
| PageUp / PageDown | Move focus +/- 1 month |
| Shift+PageUp / Shift+PageDown | Move focus +/- 1 year |
| Home / End | Move to start/end of week |
| Enter / Space | Select focused date |
| Escape | Dismiss (calls onDismiss) |
Focus auto-advances the displayed month when crossing boundaries. For example, pressing ArrowRight on the last day of the month moves focus to the first day of the next month and updates the displayed month.
Roving tabIndex
Only the currently focused date has tabIndex=0. All other days have tabIndex=-1. This means:
- Tab moves focus into the calendar grid (to the focused date), then out
- Arrow keys move focus between days within the grid
- The user never has to tab through every day cell
Screen reader announcements
The useAnnouncer hook (used internally by useCalendar) creates an aria-live="polite" region that announces:
- Month navigation changes (e.g. "March 2026")
- Selection changes (e.g. "Selected Saturday, March 14, 2026")
When using compound components, the announcer portal is rendered automatically inside Calendar.Root.
The prop getters (getDayProps, getGridProps, getNavProps, getHeaderProps) apply ARIA attributes for their respective elements automatically. Note that getNavProps provides aria-label, disabled, and onClick for individual prev/next buttons. The role="navigation" wrapper is provided by the CalendarNav primitives compound component.