Calendar
A calendar is a month grid that stays on the page. People pick one day, or a range in mode="range".
<tk-calendar label="Departure date" value="2026-03-17"
min="2026-03-05" max="2026-09-30"></tk-calendar>Anatomy
| Part | What it is |
|---|---|
tk-calendar |
the element; one Tab stop |
| heading | the month and year, such as "March 2026"; it names the grid |
| paging buttons | "Previous month" and "Next month"; disabled when that month is out of bounds |
| weekday header | two-letter day names; screen readers hear the full name |
| day grid | always six rows, so the height never changes while paging |
| day cell | 32×32px; the number with a circle behind it when selected |
grid part |
the day-grid table |
Days from the next and previous month fill the corners. They are dimmed but still work: picking one selects it and pages to its month. With more than one month shown, the corners stay empty. The grid shows days only.
States
<tk-calendar label="Delivery date" value="2026-04-08"
min="2026-04-06" max="2026-04-24"></tk-calendar>| Day | Looks like |
|---|---|
| today | a small accent dot under the number |
| selected | a filled accent circle behind the number |
| selected and today | the circle, with the dot on it |
| hover / press | a gray circle |
| keyboard focus | a focus ring around the day |
| from another month | gray number, still clickable |
outside min / max |
faded, can't be clicked or focused |
Week start
week-start sets the first day of the week, from 1 (Monday, the default) to 7
(Sunday).
<tk-calendar label="Shift date" week-start="7"></tk-calendar>
Range mode
mode="range" picks a period. The first pick sets start, the second sets end. A pick
before the start moves the start; a pick after a full range starts a new one. Hovering
shows the span before the second click.
<tk-calendar mode="range" start="2026-03-10" end="2026-03-18"
visible-months="2" label="Stay dates"></tk-calendar>- The two ends get the accent circle; the days between get a faint accent band.
visible-monthsshows up to three months side by side. They work as one grid: arrows move from one month into the next, and the paging buttons move the whole set.- For a form, use the
Date range picker ; it builds on this.
When to use
- Use a calendar when the grid should stay visible, such as a booking or scheduling panel.
- Use a
Date picker for a date in a form. It opens this grid in a popover and lets people type. - Use a
Date range picker for a period in a form. - Don't use any calendar for a date people already know, such as a birth date. Let
them type it: see
Memorable dates .
Do / Don't
| ✅ Do | ❌ Don't |
|---|---|
| Use a calendar when the grid should stay visible | Use a bare calendar as a form field |
Set min and max to the task's real dates |
Offer years of dates nobody will pick |
| Let people type a birth date | Make people page back decades in a calendar |
| Use range mode for a period | Put two calendars side by side for a range |
| Leave a question unanswered until people pick | Pre-select a date to steer the answer |
Related
Date picker : a date field that opens this calendar.Date range picker : a period field with a two-month range calendar.Time picker : the time next to a date.
API
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
value |
ISO date (yyyy-mm-dd) |
'' |
the selected day; setting it shows that month |
mode |
single | range |
single |
one day or a period |
start / end |
ISO date | '' |
the period's ends, in range mode |
visible-months |
1–3 |
1 |
months shown side by side |
min / max |
ISO date | — | the days people can pick; paging stops at these months |
label |
string | — | the purpose, read before the month name, such as "Departure date" |
week-start |
1–7 |
1 |
first day of the week, Monday to Sunday |
Events
change fires when someone picks a day that changes the selection. In range mode it
fires on both picks. It doesn't fire while people move through the grid, or when you set
value, start, or end in code.
The calendar isn't a form field: it submits nothing. In a form, use the
CSS
::part(grid): a day-grid table.
Tokens
| Part | Tokens |
|---|---|
| day number | text/default, Body Small; other month text/secondary; out of bounds text/disabled |
| hover / press | state/hover / state/press, radius-full |
| selected, range ends | fill/accent + text/accent/on-fill |
| range band | state/accent/on |
| today dot | icon/accent; on the selected circle icon/accent/on-fill |
| focus ring | border/accent/focus |
| heading / weekday header | Label Medium text/default / Caption text/secondary |
| paging buttons | icon/secondary, icon-size-16; disabled icon/disabled |
| spacing | space-4 between cells, space-8 around the grid |
Values per theme:
Accessibility
Built in:
- Screen readers hear a grid with column headers and full dates, such as "28 February".
- One Tab stop; arrows move inside the grid.
- Moving through the grid never changes the value. Only Enter, Space, or a click does.
- The heading announces each new month and names the grid.
- Today is marked as the current date; days outside
minandmaxare announced as unavailable and skipped by the keyboard. - In range mode, every day in the range is announced as selected, the ends as "range start" and "range end", and the finished range with its number of days.
- Days are 32px, above the 24px minimum target size.
Your part:
- Set
labelwhen nothing around the calendar names it. - Set
minandmaxto the dates that make sense for the task.
| Key | Action |
|---|---|
Right / Left |
next / previous day, into the next month if needed |
Down / Up |
same weekday next / previous week |
Home / End |
first / last day of the week |
Page Down / Page Up |
same day next / previous month |
Shift + Page Down / Page Up |
same day next / previous year |
Enter / Space |
selects the day; in range mode, first the start, then the end |
Tab |
leaves the calendar |
Arrow keys stop at min and max.