# Calendar

> A keyboard-accessible month grid for choosing a date or a date range.

Category: Forms · Status: stable · Package: @bindshq/stance-react@0.1.1 · Docs: https://stanceui.com/components/calendar

Calendar is the always-visible date grid used inside DatePicker and DateRangePicker. Use it directly when a date choice is the main task of the view, such as scheduling a signature roll-out. Dates outside min/max or rejected by isDateDisabled cannot be selected. Month and weekday names come from Intl, so it follows the chosen locale with no date library.

## Import

```tsx
import { Calendar } from '@bindshq/stance-react';
```

Styles are imported once per app: `@bindshq/stance-tokens/css` and `@bindshq/stance-react/styles.css`.

## Examples

### Basic

```tsx
import { useState } from 'react';
import { Calendar } from '@bindshq/stance-react';

const TODAY = new Date(2026, 10, 11);

export function Basic() {
  const [date, setDate] = useState<Date | null>(new Date(2026, 10, 18));
  return <Calendar today={TODAY} locale="en-GB" value={date} onValueChange={setDate} />;
}
```

### Range, two months

Click a start and end date; hovering previews the range.

```tsx
import { useState } from 'react';
import { Calendar } from '@bindshq/stance-react';
import type { DateRange } from '@bindshq/stance-react';

const TODAY = new Date(2026, 10, 11);

export function Range() {
  const [range, setRange] = useState<DateRange>({
    start: new Date(2026, 10, 16),
    end: new Date(2026, 10, 27),
  });
  return (
    <Calendar
      mode="range"
      numberOfMonths={2}
      today={TODAY}
      locale="en-GB"
      value={range}
      onValueChange={setRange}
    />
  );
}
```

### Min, max and disabled dates

Only weekdays in the next three weeks are available.

```tsx
import { Calendar } from '@bindshq/stance-react';

const TODAY = new Date(2026, 10, 11);

export function Constraints() {
  return (
    <Calendar
      today={TODAY}
      locale="en-GB"
      min={TODAY}
      max={new Date(2026, 11, 2)}
      isDateDisabled={(d) => d.getDay() === 0 || d.getDay() === 6}
    />
  );
}
```

### Locale and week start

US English with weeks starting on Sunday.

```tsx
import { Calendar } from '@bindshq/stance-react';

const TODAY = new Date(2026, 10, 11);

export function Localised() {
  return (
    <Calendar today={TODAY} locale="en-US" weekStartsOn={0} defaultValue={new Date(2026, 10, 26)} />
  );
}
```

## API reference

| Component | Prop | Type | Default | Description |
| --- | --- | --- | --- | --- |
| Calendar | `mode` | `'single' \| 'range'` | `'single'` | Select one date or a range. |
| Calendar | `value` | `Date \| null  \|  { start, end }` | None | Controlled value (shape depends on mode). |
| Calendar | `defaultValue` | `Date \| null  \|  { start, end }` | None | Initial value when uncontrolled. |
| Calendar | `onValueChange` | `(value) => void` | None | Called with the new date or range. |
| Calendar | `month / defaultMonth / onMonthChange` | `Date / Date / (month: Date) => void` | None | The first displayed month. |
| Calendar | `min` | `Date \| null` | None | Earliest selectable date. |
| Calendar | `max` | `Date \| null` | None | Latest selectable date. |
| Calendar | `isDateDisabled` | `(date: Date) => boolean` | None | Marks individual dates unavailable. |
| Calendar | `weekStartsOn` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `1` | 0 = Sunday, 1 = Monday. |
| Calendar | `locale` | `string` | None | BCP 47 locale for names and labels. |
| Calendar | `captionLayout` | `'dropdown' \| 'label'` | `'dropdown' for one month` | Dropdown shows month and year menus so people can jump to a distant date. Label shows a plain title. |
| Calendar | `fromYear / toYear` | `number` | None | First and last year in the year menu. Default to the min and max years, or 100 years back and 10 ahead. |
| Calendar | `numberOfMonths` | `1 \| 2` | `1` | Months shown side by side. |
| Calendar | `showOutsideDays` | `boolean` | `true` | Show adjacent-month days (single month only). |
| Calendar | `today` | `Date` | None | Override today, e.g. for a time zone or tests. |
| Calendar | `autoFocus` | `boolean` | `false` | Focus the active day on mount. |
| Calendar | `labels` | `Partial<CalendarLabels>` | None | Translatable strings for navigation and day states. |

## Accessibility

- Month and year menus are native selects with accessible names, so they work with every keyboard, screen reader and touch device.
- Each month is a grid labelled by its month and year; weekday headers carry full names via abbr.
- Each day is a button with a full spoken label, e.g. “Wednesday, 11 November 2026, Today, Selected”; today has aria-current="date".
- Unavailable days stay discoverable but are announced as unavailable and cannot be chosen.
- The month title is a polite live region, so changing months by keyboard is announced.
- One day is in the Tab order; arrow keys, Page Up/Down and Home/End follow the APG date grid model.

## Keyboard

| Keys | Action |
| --- | --- |
| `←` `→` | Previous / next day. |
| `↑` `↓` | Same day previous / next week. |
| `Home` `End` | First / last day of the week. |
| `Page Up` `Page Down` | Same day previous / next month. |
| `Shift + Page Up` `Shift + Page Down` | Same day previous / next year. |
| `Enter` `Space` | Select the focused day (start, then end in range mode). |

## Guidelines

**Do**

- Set min and max whenever dates outside a window are invalid.
- Use the user’s locale.

**Don’t**

- Use a calendar for dates far in the past, like birth dates; let people type instead (DatePicker).

## Related

- https://stanceui.com/components/date-picker
- https://stanceui.com/components/date-range-picker
- https://stanceui.com/components/time-picker
