Unified UI

Calendar

A month/year grid calendar with single and range date selection, keyboard navigation, disabled dates, and animated month transitions.

Basic

A full-featured month calendar with single date selection, range selection, keyboard navigation, and smooth animated month transitions.

March 2026
Su
Mo
Tu
We
Th
Fr
Sa

Installation

Install the component via the CLI in one command.

npx @work-rjkashyap/unified-ui add calendar
pnpm dlx @work-rjkashyap/unified-ui add calendar
npx @work-rjkashyap/unified-ui add calendar
bunx @work-rjkashyap/unified-ui add calendar

If you haven't initialized your project yet, run npx @work-rjkashyap/unified-ui init first. See the CLI docs for details.

Or install the full package

Use this approach if you prefer to install the entire design system as a dependency instead of copying individual components.

npm install @work-rjkashyap/unified-ui
pnpm add @work-rjkashyap/unified-ui
yarn add @work-rjkashyap/unified-ui
bun add @work-rjkashyap/unified-ui

Anatomy

import { Calendar } from "@work-rjkashyap/unified-ui";

Basic Usage

Drop Calendar anywhere you need a date picker grid. It manages its own internal state by default (uncontrolled).

<Calendar onSelect={(date) => console.log(date)} />

Single Date Selection

The default mode selects a single date. Pass onSelect to receive the selected date.

March 2026
Su
Mo
Tu
We
Th
Fr
Sa

Range Selection

Set mode="range" to allow selecting a date range. Click once for the start date, then again for the end date.

March 2026
Su
Mo
Tu
We
Th
Fr
Sa

Week Numbers

Show ISO week numbers in a leading column with showWeekNumbers.

March 2026
Su
Mo
Tu
We
Th
Fr
Sa
9
10
11
12
13

Disabled Dates

Use the disabledDate function to disable specific dates, or pass minDate / maxDate to restrict the selectable range.

// Disable weekends
<Calendar
  disabledDate={(date) => date.getDay() === 0 || date.getDay() === 6}
/>

// Restrict to a date range
<Calendar
  minDate={new Date(2025, 0, 1)}
  maxDate={new Date(2025, 11, 31)}
/>

// Disable specific dates
const holidays = [new Date(2025, 11, 25), new Date(2025, 0, 1)];
<Calendar
  disabledDates={holidays}
/>

Controlled Month

Control which month is displayed using the month and onMonthChange props.

const [month, setMonth] = useState(new Date(2025, 5));

<Calendar
  month={month}
  onMonthChange={setMonth}
/>

Default Month

Set the initially displayed month without controlling it:

<Calendar defaultMonth={new Date(2025, 11)} />

With DatePicker

Calendar is the core building block for the DatePicker component, which wraps it in a popover with a trigger input:

import { DatePicker } from "@work-rjkashyap/unified-ui/components";

<DatePicker
  onSelect={(date) => setDate(date)}
  placeholder="Pick a date"
/>

Props

PropTypeDefaultDescription
mode"single" | "range""single"Selection mode — single date or date range.
selectedDate | nullThe currently selected date (single mode, controlled).
selectedRangeDateRange | nullThe currently selected range (range mode, controlled).
onSelect(date: Date) => voidCallback when a date is selected (single mode).
onSelectRange(range: DateRange) => voidCallback when a range is selected (range mode).
defaultMonthDateCurrent monthThe month/year to display initially.
monthDateControlled displayed month.
onMonthChange(month: Date) => voidCalled when the displayed month changes.
disabledDate(date: Date) => booleanFunction returning true for dates that should be disabled.
disabledDatesDate[]Array of individually disabled dates.
minDateDateMinimum selectable date.
maxDateDateMaximum selectable date.
showWeekNumbersbooleanfalseShow the ISO week number column.
classNamestringAdditional CSS classes on the calendar wrapper.

Day States

The calendar uses CVA variants for day cell styling:

StateDescription
defaultNormal selectable day.
todayCurrent date — subtle ring highlight.
selectedSelected date (single mode) — primary fill.
rangeStartFirst date in a range — primary fill.
rangeEndLast date in a range — primary fill.
rangeMiddleDates between range start/end — accent fill.
outsideMonthDays from adjacent months — dimmed text.
disabledNon-selectable — reduced opacity.

Motion

  • Month transitions — Animated slide left/right with opacity fade when navigating months. Direction-aware: sliding left for "next" and right for "previous".
  • Day selection — Subtle spring scale animation on the selected day cell.
  • All animations respect prefers-reduced-motion via useReducedMotion().

Keyboard Navigation

KeyAction
ArrowLeftMove focus to the previous day.
ArrowRightMove focus to the next day.
ArrowUpMove focus to the same day in the previous week.
ArrowDownMove focus to the same day in the next week.
HomeMove focus to the first day of the week.
EndMove focus to the last day of the week.
PageUpNavigate to the previous month.
PageDownNavigate to the next month.
Enter/SpaceSelect the focused date.

Accessibility

  • Each day cell is a focusable <button> with a complete aria-label including weekday, month, day, and year.
  • Disabled dates have aria-disabled="true" and are skipped during keyboard navigation.
  • The month navigation buttons have descriptive aria-label values ("Previous month", "Next month").
  • The calendar grid uses role="grid" semantics.
  • Selected dates have aria-selected="true".

Design Tokens

TokenUsage
--primarySelected day background, range endpoints
--accentRange middle background, hover state
--borderCalendar container border
--ringToday indicator ring, focus ring
--radius-mdDay cell border radius
--radius-lgCalendar container border radius
--duration-fastMonth transition speed

On this page