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.
Installation
Install the component via the CLI in one command.
npx @work-rjkashyap/unified-ui add calendarpnpm dlx @work-rjkashyap/unified-ui add calendarnpx @work-rjkashyap/unified-ui add calendarbunx @work-rjkashyap/unified-ui add calendarIf 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-uipnpm add @work-rjkashyap/unified-uiyarn add @work-rjkashyap/unified-uibun add @work-rjkashyap/unified-uiAnatomy
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.
Range Selection
Set mode="range" to allow selecting a date range. Click once for the start date, then again for the end date.
Week Numbers
Show ISO week numbers in a leading column with showWeekNumbers.
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
| Prop | Type | Default | Description |
|---|---|---|---|
mode | "single" | "range" | "single" | Selection mode — single date or date range. |
selected | Date | null | — | The currently selected date (single mode, controlled). |
selectedRange | DateRange | null | — | The currently selected range (range mode, controlled). |
onSelect | (date: Date) => void | — | Callback when a date is selected (single mode). |
onSelectRange | (range: DateRange) => void | — | Callback when a range is selected (range mode). |
defaultMonth | Date | Current month | The month/year to display initially. |
month | Date | — | Controlled displayed month. |
onMonthChange | (month: Date) => void | — | Called when the displayed month changes. |
disabledDate | (date: Date) => boolean | — | Function returning true for dates that should be disabled. |
disabledDates | Date[] | — | Array of individually disabled dates. |
minDate | Date | — | Minimum selectable date. |
maxDate | Date | — | Maximum selectable date. |
showWeekNumbers | boolean | false | Show the ISO week number column. |
className | string | — | Additional CSS classes on the calendar wrapper. |
Day States
The calendar uses CVA variants for day cell styling:
| State | Description |
|---|---|
default | Normal selectable day. |
today | Current date — subtle ring highlight. |
selected | Selected date (single mode) — primary fill. |
rangeStart | First date in a range — primary fill. |
rangeEnd | Last date in a range — primary fill. |
rangeMiddle | Dates between range start/end — accent fill. |
outsideMonth | Days from adjacent months — dimmed text. |
disabled | Non-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-motionviauseReducedMotion().
Keyboard Navigation
| Key | Action |
|---|---|
ArrowLeft | Move focus to the previous day. |
ArrowRight | Move focus to the next day. |
ArrowUp | Move focus to the same day in the previous week. |
ArrowDown | Move focus to the same day in the next week. |
Home | Move focus to the first day of the week. |
End | Move focus to the last day of the week. |
PageUp | Navigate to the previous month. |
PageDown | Navigate to the next month. |
Enter/Space | Select the focused date. |
Accessibility
- Each day cell is a focusable
<button>with a completearia-labelincluding weekday, month, day, and year. - Disabled dates have
aria-disabled="true"and are skipped during keyboard navigation. - The month navigation buttons have descriptive
aria-labelvalues ("Previous month","Next month"). - The calendar grid uses
role="grid"semantics. - Selected dates have
aria-selected="true".
Design Tokens
| Token | Usage |
|---|---|
--primary | Selected day background, range endpoints |
--accent | Range middle background, hover state |
--border | Calendar container border |
--ring | Today indicator ring, focus ring |
--radius-md | Day cell border radius |
--radius-lg | Calendar container border radius |
--duration-fast | Month transition speed |