facebook / facebook/astryx

[RFC] Calendar: add a month/year picker for fast navigation to distant dates

Open
#4,941 4 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
TypeScript
Stars
13.1k
Forks
1.1k
Avg merge
1d 14h
Merged PRs (30d)
669

Description

## Problem Statement

Calendar only supports moving between months one step at a time, via the prev/next nav buttons in its header. There is no way to jump directly to a specific month or year.

Reaching a far-past or far-future date — a birth date, a multi-year-old record, a long-lead scheduling date — means clicking prev/next dozens or hundreds of times. The only alternative today is typing directly into a paired DateInput/DateRangeInput, which sidesteps the calendar's own guarantees: free-text date entry is easy to mistype, format-ambiguous, and gives no validation feedback until blur/submit.

This is a UX gap, not a missing prop: any flow that asks someone to locate a specific, known, non-recent date currently forces a choice between tedious clicking or an error-prone text fallback. Neither is what the calendar UI is supposed to be for.

## Evidence of Demand

External systems that ship this pattern:

- **react-day-picker** — `captionLayout="dropdown"` replaces the static caption with month/year ``s.
- **shadcn/ui** — Calendar component ships a "dropdown" caption variant using the same pattern.
- **MUI X Date Pickers** — the `views` prop supports a dedicated year-selection view, reachable by clicking the header label.
- **Ant Design** — DatePicker's panel header is clickable and escalates to a year/decade picker view.

This isn't a niche pattern — it's the default or near-default behavior in every major React date-picker library, which suggests the one-step-at-a-time-only header is the outlier, not the norm.

## Why Existing Components Don't Cover This

Calendar's header is not currently composable for this — the month/year label is a fixed, non-interactive element with no exposed slot or prop to swap in an interactive control. There's no caption render-prop, no way to replace just the header while keeping the day-grid, and no documented escape hatch.

The only composition attempt available today is building a fully custom header wrapper around Calendar, which means:
- Re-implementing month-label formatting
- Re-wiring `onNavigate`
- Manually matching Astryx's spacing/typography tokens to avoid visual drift

At which point you've lost the theming and behavioral consistency Calendar is supposed to guarantee, for a problem that isn't product-specific.

The remaining fallback — swapping to a raw DateInput — isn't a composition of Calendar at all; it's an admission that Calendar doesn't support the use case, and it reintroduces the validation/format problems described above.

## Rough Approaches Considered

*Not proposed solutions — included to show the design space has been considered. Final shape should go through API Arbitration if this RFC is accepted.*

### Approach A — Dropdown-based caption
Replace the static label with two dropdowns (month, year) so users can jump directly.

```jsx

```

Simple, keyboard-accessible for free (native/Selector-style dropdowns already handle this), but two long `` lists can be awkward on touch/mobile.

Image

### Approach B — Secondary grid view
Clicking the caption label opens a month-grid (and year stepper/list) to pick from, similar to shadcn's calendar block 21.

```jsx

```

Nicer on touch/mobile, avoids long scrollable selects, but is a bigger surface to build and test (new keyboard/focus pattern, an extra transient view state).

Image

Both are compatible with keeping `captionLayout="label"` (today's behavior) as the default, so neither implies a breaking change.

## Accessibility Considerations

- Maps to the existing listbox pattern already established elsewhere in Astryx (e.g. Listbox conventions)
- Approach A via native/Selector dropdowns, Approach B via a grid using the same roving-focus convention, rather than inventing a new one-off interaction
- Month and year controls need accessible names distinct from the day-grid's own labeling (e.g. `aria-label="Month"` / `aria-label="Year"`), so screen reader users don't confuse header navigation with day selection
- Keyboard users must be able to reach and operate the month/year control via Tab, independent of the day-grid's roving arrow-key focus — the two shouldn't compete for the same keys

**Benefits:** screen reader and keyboard-only users disproportionately bear the cost of the current one-step-at-a-time header, since "click next 200 times" isn't a viable strategy for them the way a sighted mouse user might tolerate it briefly. This is as much an accessibility fix as a convenience feature.

## Performance Considerations

Not applicable — this changes the header/caption only, not the day-grid's item count or rendering strategy. No new list/table/grid of items is introduced.

## Pre-submission Checklist

- [x] I have read the [Contributing guide](https://github.com/facebook/astryx/wiki/Contributing)
- [x] I have read the [API Conventions](https://github.com/facebook/astryx/wiki/API-Conventions)
- [x] I have checked that existing Astryx components cannot compose to solve this
- [x] This is a general-purpose UI pattern (not specific to one product)

Contributor guide

Open the contributing guide

Research direction

Begin with Calendar's existing header/caption and the API Conventions; review the Listbox conventions when assessing the proposed controls. Compare the dropdown and secondary-grid approaches against the stated accessibility requirements, and consider the RFC complete only once one API and its keyboard behavior are agreed.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
accessibility, design, frontend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.