# Period Picker

> One period for a page of charts: period pills, the days they cover said once, Custom dates, and a switch to compare with the period before.

- Name: `period-picker`
- Status: beta
- Categories: dates, forms
- File: the install writes `period-picker.tsx` to your `ui` alias, `@/components/ui` by default.
- npm packages: `@base-ui/react`, `class-variance-authority`. The install adds them.
- Other registry items: `@arbokui/input`, `@arbokui/label`, `@arbokui/switch`, `@arbokui/tabs`. The install adds them.
- Gallery: https://arbokui.com/components/period-picker
- Registry item: https://arbokui.com/r/period-picker.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@arbokui=https://arbokui.com/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@arbokui": "https://arbokui.com/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@arbokui` in a public list that Arbokui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @arbokui/theme
   ```

3. Add the component:

   ```bash
   npx shadcn@latest add @arbokui/period-picker
   ```

## Use it for

- The period for a whole page of charts and numbers, like a monthly sales report.
- Pass periods, each with an id and a label. Give each a range, like 'Jul 1 to Sep 29', so the days are said once.
- Give each period a previous, like 'last quarter', so the switch reads 'Compare with last quarter'.
- Changes apply at once. Update the charts in onValueChange, with no Apply button.
- Pass today, as an ISO day, so Custom starts on the four weeks up to it and no date can pass it.

## Avoid it for

- A range for one chart. Give that chart its own tabs.
- Picking one day, like a due date. Use Calendar.
- A form people submit later. Use date fields in the form.

## Props

- `periods`: `{ id: string; label: string; range?: string; previous?: string }[]`. Required. The period pills, in order. Custom comes after them.
- `value`: `{ period: string; compare?: boolean; from?: string; to?: string }`. The picked period, when controlled. from and to are ISO days, set only when period is 'custom'.
- `defaultValue`: `PeriodValue`. Default `the first period`. The first value when uncontrolled.
- `onValueChange`: `(value: PeriodValue) => void`. Called at once on each change. While a custom end is before its start, it waits.
- `custom`: `boolean`. Default `true`. Offer Custom, which opens From and To date fields.
- `compare`: `boolean`. Default `true`. Offer the switch to compare with the period before.
- `today`: `string`. Default `today`. Today as an ISO day. Custom starts on the four weeks up to it, and no date can pass it. One that is not a whole day falls back to the browser's today.
- `tone`: `"neutral" | "brand"`. Default `"neutral"`. The color of the picked pill.
- `aria-label`: `string`. Default `"Period"`. Names the pills for screen readers.
- `locale`: `string`. Default `"en-US"`. The locale for the custom days, like 'Sep 2 to Sep 29'.
- `className`: `string`. Extra classes for the root. Later classes win.

## Accessibility

- The pills are Tabs, one Tab stop, moved with the arrow keys. Enter or Space picks one, so passing a pill does not reload the charts. The list is named 'Period' and described by the days it covers.
- The days are a polite live region, so a new period's days are read once.
- The compare switch is named by its words, like 'Compare with last quarter'.
- From and To are native date fields with visible labels, so each browser gives its own keyboard and screen reader support.
- An end before the start marks To as invalid and says so in a role="alert" line under the fields.

## Examples

### For a page of charts

```tsx
const PERIODS = [
  { id: "quarter", label: "This quarter", range: "Jul 1 to Sep 29", previous: "last quarter" },
  { id: "month", label: "This month", range: "Sep 1 to Sep 29", previous: "August" },
];

const [period, setPeriod] = useState({ period: "quarter" });

<PeriodPicker periods={PERIODS} value={period} onValueChange={setPeriod} today="2026-09-29" />
```

### Pills only

```tsx
<PeriodPicker periods={PERIODS} custom={false} compare={false} />
```
