Accessibility
EventRecurrencePicker composes a Combobox for frequency, a nested CustomRecurrenceOptions group for custom cadences, a DatePicker for the start anchor, a TimeRangePicker for start and end times, a Switch for All-day, and end-rule selectors. Each sub-control carries its own @prepared911/ui-core accessibility guarantees; the picker wires them into one panel. The app owns the accessible name for the whole control, announcing the getRecurrenceSummary prose, and the focus handoff into a surrounding Popover when trigger is used.
Combobox, Switch, DatePicker, TimeRangePicker, Input, Label) is a Prepared-shipped component with its own a11y story: labels, keyboard handling, and focus styling.Custom slides in the custom pane; focus stays on the operator's most recent control and does not get stolen by the animation.cta + trigger are set, the picker holds draft changes internally and only emits onChange when Apply fires: so closing the popover via Escape discards the draft without committing.disabled passes down to every sub-control so a parent "view only" state doesn't leave part of the rule editable.getRecurrenceSummary(value) returns the same plain-English rule the picker shows; use it for live regions and echoed chips.role="group" with aria-labelledby pointing at a visible heading (e.g. "Schedule"), or aria-label if no heading exists.trigger is set, the trigger element owns its accessible name; icon-only triggers need aria-label.getRecurrenceSummary returns the prose; render it as visible text or pipe it into a polite live region so the committed rule is heard on change.Combobox (or your preferred entry point) when the popover opens so keyboard users land inside the panel.Comboboxs move between options; Enter or Space commits the focused option; Escape closes the combobox without committing.DatePicker match DatePicker: Up / Down by week, Left / Right by day.Switch.cta + trigger are set).role="group" with a name.Combobox from @prepared911/ui-core (Radix-backed) with aria-expanded and aria-controls.FlexBox with Combobox and selectors; each selector owns its own semantics.@prepared911/ui-core primitive with its own roles and labels.<button> via @prepared911/ui-core Button.Opening a popover-hosted picker should place focus inside the panel—typically the frequency Combobox—so keyboard operators don't land on the trigger only to need another Tab. Closing should restore focus to the trigger. When the operator enters the custom pane, keep focus on the most recent control they were on; animated transitions should not steal focus.
The picker does not announce getRecurrenceSummary updates by itself. Route the prose through a polite live region (as in the example above) so the rule is heard after every committed sub-control change. Keep the announcement brief—it should reflect the committed rule, not every intermediate keystroke inside a draft popover.
When operators switch Frequency to Custom, the sliding animation does not announce the pane change; a short heading inside the custom pane ("Custom cadence") read by screen readers as focus moves in is sufficient.
@prepared911/ui-core animation helpers. The component does not currently consult prefers-reduced-motion; match whatever the surrounding surface already does for reduced motion.Combobox, DatePicker, or Switch show up here, too: validate against those components' individual guidance.cta is false, every sub-control change emits onChange. Surfaces that rely on draft-only behavior should always pair cta with trigger.timezone string; if your app displays times in a different zone, surface the source zone in visible copy.