# Calendars

> A month laid out in weeks as its locale lays them out, and how the keyboard moves a day through it.

A calendar shows a month as weeks, so that a date can be found by where it
falls: which Friday, how far from today, what the day after a holiday is. It
answers what typing a date cannot, and is laid out as its reader's locale lays
out a week.

## Best practices

**Lay the month out in whole weeks.** Seven columns, one for each day of the
week, under the days' names. The days of the months either side fill out the
first and last weeks, set apart from the month's own, and can be chosen like any
other: the week a month ends in is still a week.

**Keep the grid the same size.** Always six weeks, whatever the month, so that
the controls around the grid stay where they are from month to month and a
second press lands where the first did.

**Start the week where the locale does.** On Sunday in the United States and
Japan, Monday in most of Europe, Saturday in Egypt. Ask the platform; where it
cannot say, use the locale's region. Mark the weekend as the locale counts it —
Friday and Saturday in Israel — rather than assuming Saturday and Sunday.

**Write it in the reader's language.** The month and year as the locale orders
them — October 2026, 2026年10月 — the days of the week by their short names, and
the days of the month in the digits the locale writes. A name with a place for a
value, such as the name of the control that opens the calendar, is a sentence
the translator orders, not words joined in English order.

**Mark today, and the chosen day, differently.** The chosen day is filled.
Today is marked by more than a colour — heavier, underlined — so it is found
without relying on colour, and is never mistaken for the choice.

**Show what cannot be chosen, and do not offer it.** Days outside the allowed
range stay in view, marked unavailable by more than a colour, and nothing
chooses them. A control that would lead to a month with no allowed day is
unavailable too.

**Open where the person is.** On the date already chosen; with none, on today,
or on the end of the range nearest today.

**Move by the calendar's own units.** A day, a week, a month, a year. A month
too short for the day lands on its last day: 31 January, a month on, is the last
day of February. Moving a day or a week past the end of a month goes on into the
next, and the month shown follows.

**Stop at the ends.** A move that would pass the end of the allowed range stops
at the end, so the keyboard is never somewhere nothing can be chosen.

**Name each day in full.** To assistive technology a day is its whole date,
weekday included — Tuesday, October 13, 2026 — not a number that means nothing
without the title above it and the column it is in.

**Announce the month.** When the month shown changes, say its name: the title is
what tells someone who cannot see the grid that it has turned.

## Keys

| Key                            | Moves to                                   |
| ------------------------------ | ------------------------------------------ |
| Left Arrow, Right Arrow        | The day before, the day after              |
| Up Arrow, Down Arrow           | The same day a week before, a week after   |
| Home, End                      | The first, the last day of the week        |
| Page Up, Page Down             | The same day a month before, a month after |
| Shift-Page Up, Shift-Page Down | The same day a year before, a year after   |

Where text reads right to left, the days run from the right and Left and Right
Arrow are mirrored with them. Home is still the week's first day.

The days are one stop in the Tab sequence: Tab reaches the day in focus, and the
arrow keys move within.

## Platform considerations

**Pointer.** A day is at least 32 CSS pixels square; it shows when the pointer is
over it and when it is pressed.

**Touch.** A day is at least 44 CSS pixels square. Where the platform has a date
picker of its own, prefer it: it is the one people already know, sized for a
finger and read by the platform's assistive technology.

**Assistive technology.** The days are a grid named by the month and year; the
chosen day is selected, today is the current date, and days outside the range
are disabled.

**Contrast and motion.** Under forced colours the chosen day takes the system's
highlight colours; nothing moves for those who ask for less motion.

See also [Dates](https://ui.publictheta.com/foundations/date-type.md), [Grid navigation](https://ui.publictheta.com/foundations/grid-navigation.md)
and [Date field](https://ui.publictheta.com/components/date-field.md).

## Behaviour

Each statement is verified by a test in Chromium, Chromium (touch), Firefox, WebKit, WebKit (touch), unless it names the environments it applies to or is marked open.

### Counting days

- Every day from the year 1 to the year 9999 is counted as the platform's calendar counts it.
- The last day of every month is the one the platform's calendar ends it on.
- There is no day before the year 1 or after the year 9999.

### A month

- A month is six weeks of seven days, each day the one after the last.
- Every day of the month is in it exactly once.
- The first column is the first day of the week, whichever day that is.
- The days of the months either side fill out the first and last weeks.
- A month may be given as any date in it.
- Where the years a date can write run out, the days beyond are left blank.
- The range in view runs from the first day shown to the last.

### The week

- The week starts on the day it starts on where the locale is written.
- The weekend is the locale's: Saturday and Sunday in most, Friday and Saturday in Egypt and Israel.
- A language given without a region is the region it is most written in.
- In an engine that does not know the week, the answers are the same.
- A locale that names its own first day of the week is given it.
- A locale that is not one is read as English, not failed on.

### Names

- The days of the week are named in the locale, in the order of its week.
- A month is titled with its name and year, in the words and the order of the locale.
- A day is named in full, with its day of the week.
- The day of the month is written in the digits the locale writes numbers in.
- Every day of every year is named as the platform names it, with no time zone moving it.

### Moving

- The arrow keys move by a day to either side, and by a week up and down.
- Left and right are mirrored where text reads right to left; up and down are not.
- Home and End go to the first and last days of the week, as the locale counts the week.
- Page Up and Page Down move by a month; with Shift, by a year.
- A month too short for the day ends on its last day.
- Any other key asks for no move.
- A move by days or weeks, and the same move back, returns to the day it left.
- A move by months or years, and the same move back, returns to the day it left, where no month was too short for it.
- The ends of a week are in it: seven days apart less one, with the day between them.
- A calendar moves a date as a date field steps it.

### A range

- A date is within a range that has it, ends included; an end not given is no limit.
- A date beyond a range is held at the end it is beyond.
- A move past the end of the range stops at the end.
- A move from outside the range lands inside it.
- A move past the year 1 or the year 9999 stops at its first or last day.

## Reference implementation

React: `npx shadcn@latest add https://ui.publictheta.com/r/calendar.json`
