Accessibility
SelectMonth, SelectYear, and MonthCaption are @prepared911/ui-core Select and IconButton compositions that DatePicker uses internally. Each primitive inherits the accessibility story from ui-core. The app owns the surrounding structure when these primitives are composed into a custom DayPicker—heading order, grid pairing, and announcement of month / year changes outside the default DatePicker surface.
SelectMonth / SelectYear. @prepared911/ui-core Select (Radix-backed) with aria-expanded on the trigger, arrow-key navigation through options, and a localized aria-label ("Select month", "Select year").MonthCaption. Composes IconButton chevrons (with localized title values: "Previous month" / "Next month") and the two selects. Chevrons disable at minSelectableDate / maxSelectableDate boundaries; the selects disable entries outside the range.getMonthNamesFull(), which respects the active locale for both display and i18n keys.MonthCaption in a custom DayPicker, make sure the caption and grid share the same month state so navigation advances the grid; otherwise screen readers will announce a month change that the grid ignores.SelectMonth rendered without surrounding context (an analytics "pick a month" control, say) should be wrapped in labeled chrome so screen reader users know the context of the selection.Select triggers. Space / Enter opens; arrow keys move through options; Escape closes without committing; Enter or Space on an option commits it.IconButton chevrons. Enter / Space activates; disabled at boundaries.MonthCaption as a unit. Tab order runs through Previous chevron → Month select → Year select → Next chevron.SelectMonth / SelectYear. Radix Select listbox pattern: button trigger with aria-expanded, listbox popper with role="listbox", options with role="option".IconButton chevrons. Native <button> with title; title text provides the accessible name.MonthCaption has no explicit role; rely on the surrounding DayPicker grid structure.DayPicker surfaces. react-day-picker expects captions to coordinate with the grid's month context; passing a decoupled MonthCaption without wiring goToMonth breaks keyboard navigation.SelectMonth / SelectYear. Without surrounding labeled chrome, "September" alone reads with no context. Add a wrapper label when these primitives live outside a calendar.SelectYear defaults to 2020–currentYear; override minYear / maxYear for historical-range surfaces.