<!-- Protocore Design System — MonthPicker -->
# MonthPicker

- **Category:** Inputs (`inputs`)
- **Slug:** `inputs/month-picker`
- **Status:** stable
- **Platforms:** web
- **Import:** `import { MonthPicker } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/inputs/month-picker

> Inline 12-month grid with a year header for navigation and full arrow-key selection, returning the first day of the chosen month.

## When to use it

Use **MonthPicker** when the unit is a whole month — a billing period, a report month — and a day grid would be needless precision. For a year alone use **YearPicker**; when a specific day matters use **DatePicker** or **Calendar**.

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `className` | `string` | no | — | — |
| `defaultValue` | `Date \| null` | no | — | Initial selected month in uncontrolled mode. |
| `defaultYear` | `number` | no | — | Initial view year in uncontrolled mode. |
| `locale` | `string` | no | — | BCP-47 locale for month labels. |
| `max` | `Date` | no | — | Latest selectable month (inclusive). |
| `min` | `Date` | no | — | Earliest selectable month (inclusive). |
| `monthFormat` | `enum` | no | `short` | Month label style. |
| `onValueChange` | `((value: Date) => void)` | no | — | Fires with the first day of the chosen month. |
| `onYearChange` | `((year: number) => void)` | no | — | Fires when the view year changes via navigation. |
| `style` | `CSSProperties` | no | — | — |
| `value` | `Date \| null` | no | — | Controlled selected month (any day within it; normalised to the 1st). |
| `year` | `number` | no | — | Controlled view year. |

## Examples

### Basics

Twelve month cells sit under a year header; the chevrons move the view year. Selecting a month emits a `Date` normalised to its first day. Controllable via `value` / `defaultValue` / `onValueChange` plus a controllable view `year`.

```tsx
import { useState } from "react";
import { MonthPicker } from "@protocore/pds";

export default function Basics() {
  const [value, setValue] = useState<Date | null>(new Date(2026, 5, 1));

  return (
    <MonthPicker aria-label="Billing month" value={value} onValueChange={setValue} locale="en-US" />
  );
}
```

### Bounded range

Pass `min` / `max` to disable months outside the valid window — disabled cells stay keyboard-reachable but can't be selected.

```tsx
import { useState } from "react";
import { MonthPicker } from "@protocore/pds";

export default function Bounded() {
  const [value, setValue] = useState<Date | null>(new Date(2026, 6, 1));

  return (
    <MonthPicker
      aria-label="Report month"
      value={value}
      onValueChange={setValue}
      min={new Date(2026, 3, 1)}
      max={new Date(2026, 9, 1)}
      monthFormat="long"
      locale="en-US"
    />
  );
}
```

## Do & don't

**Do**

- Treat the emitted `Date` as a month — read its year and month, ignore the day.
- Set `min` / `max` to the selectable window so out-of-range months disable.
- Pass a `locale` and choose `monthFormat` (`short` / `long`) to fit the space.
- Wrap it in a `Field` when it needs a visible label.

**Don't**

- Don't use it when the day within the month actually matters — reach for Calendar.
- Don't infer selection from the header year — read `onValueChange`.
- Don't hand it into a popover expecting a trigger; it renders inline.
- Don't rely on colour alone — the selected cell also inverts its fill.

## Accessibility

**Keyboard**

| Keys | Action |
| --- | --- |
| `Arrow keys` | Move between month cells (clamped to the grid). |
| `Home / End` | Jump to the first / last month cell. |
| `Enter / Space` | Select the focused month. |

**Notes**

- The month cells form a `role="grid"` with `role="row"` groupings and roving tabindex, so only one cell is in the tab order.
- Each cell carries an `aria-label` of its full month and year and `aria-selected` when chosen.
- The year header exposes labelled Previous / Next buttons and an `aria-live` year readout.

## Related

`year-picker`, `date-picker`, `calendar`, `date-range-picker`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
