skrewww

Content & Data

Calendar Grid

Calendar Grid is a composed month-view calendar with single-date or range selection and grid keyboard navigation.

Single-date or range month view — composed from Calendar Day

betaReact AvailableFigma PartialDocs Partial
React last updated
2026-07-12
Documentation last updated
2026-06-01
Accessibility target
WCAG 2.2 AA (target)
Version
0.5.0-beta

Known open questions

  • Month/year drill-up and range selection are implemented on Calendar Grid.
  • Six-week grid with outside-month days visible is an initial policy — not fully verified in Figma Month Grid.

Live preview

Single-date month grid with roving tabindex and arrow-key navigation.

Current month

Mon
Tue
Wed
Thu
Fri
Sat
Sun

Arrow keys move focus; Enter or Space selects. Page Up/Page Down change months. Initial policy uses Monday week start and en-GB formatting — localization deferred.

Range mode

Two-click range selection with a live hover/keyboard preview before the end is committed.

Pick a start, then an end date

Mon
Tue
Wed
Thu
Fri
Sat
Sun

First click sets the start date. Hover or arrow-key to preview the range before committing an end with a second click or Enter. Clicking inside a completed range starts a new one.

Purpose

A month-view calendar grid for selecting one date or a date range, composed from Calendar Day cells with month navigation.

Anatomy

Calendar Grid composes Calendar Day: header + previous/next month buttons + weekday row + week rows. Clicking the header drills up to a month grid, then a year grid, for fast month/year navigation.

Variants and states

single-date · range

When to use

Inline month or range selection, or as the calendar surface inside Date Picker.

When not to use

Multi-date (non-contiguous) selection or scheduling views with events — not confirmed in Figma.

Accessibility

role="grid" with weekday column headers, roving tabindex on day buttons, and arrow-key navigation.

Keyboard behavior

Arrow keys move by day; Home/End move within week; Page Up/Page Down change month; Enter/Space selects focused day. Roving tabindex keeps one day in tab order. The month and year drill-up grids use the same roving-tabindex model with arrow keys, Home/End, and Enter/Space over their own cells.

Common mistakes

Using Calendar Day outside a grid without providing collection keyboard semantics.

Properties

Composes Calendar Day building blocks in a 7×6 grid with month heading and previous/next controls.

How does Calendar Grid work with a keyboard?

The grid uses role="grid" with roving tabindex on day buttons and explicit arrow/Home/End/Page Up/Page Down handling.

How does focus move between months?

Page Up/Page Down and month buttons update the visible month while preserving the focused weekday where possible through date clamping.

How are outside-month dates handled?

Outside-month days remain visible and selectable unless disabled. They use muted styling.

What week-start and locale assumptions are used?

Initial batch uses en-GB formatting and Monday week start. Stored values remain locale-neutral YYYY-MM-DD strings.

How does range selection work?

Set mode="range" and use rangeValue/defaultRangeValue/onRangeValueChange ({ start, end }) instead of value. First click sets start; second click commits end (swapped into chronological order if picked backwards); clicking inside a completed range starts fresh. Hovering or moving keyboard focus after start is set shows a live provisional preview of the range before it's committed.

Tokens used

semantic/surface/defaultsemantic/border/defaultsemantic/text/primarycomponent/radius/container

Component API

PropTypeDefaultDescription
mode"single" | "range"Selection mode. Defaults to single-date; range mode uses separate range props below.
valueYYYY-MM-DDControlled selected date (single mode).
defaultValueYYYY-MM-DDInitial selected date (single mode).
onValueChange(date) => voidSelection callback (single mode).
rangeValue{ start, end }Controlled range value (range mode).
defaultRangeValue{ start, end }Initial range value (range mode).
onRangeValueChange(range) => voidRange selection callback (range mode).
visibleMonth{ year, month }Controlled visible month.
defaultVisibleMonth{ year, month }Initial visible month.

React example

Copy React example
import { CalendarGrid } from "@/components/ui/CalendarGrid";

export function Example() {
  return <CalendarGrid defaultValue="2026-07-14" />;
}