Day
The run
Today is a hairline. The selected day is ink. Arrow keys move the cursor.
Selects a date from a month grid. Selected day fills with ink; today has a 1px outline.
The run
Today is a hairline. The selected day is ink. Arrow keys move the cursor.
Three ways in. They share one source, so the pixels match whichever you pick.
npm install @noorddev/vlak-react
// once, next to your app's root
import "@noorddev/vlak-react/css";
import { Calendar } from "@noorddev/vlak-react";Precompiled React and one stylesheet. No compiler to configure. Per-component imports work too: @noorddev/vlak-react/components/calendar.
npx @noorddev/vlak-cli add calendar
The StyleX leaf lands in components/vlak/ with its dependencies, for your own compiler to own. See StyleX.
npx shadcn add https://vlak.dev/r/calendar.json
The same registry item, installed by shadcn's CLI.
<link rel="stylesheet" href="node_modules/@noorddev/vlak/css/vlak.css" />
No React. Link vlak.css and use the markup and classes below.
import { useState } from "react";
import { Calendar } from "@noorddev/vlak-react";
const [date, setDate] = useState<Date>();
<Calendar value={date} onValueChange={setDate} weekStart={1} />Month grid with one roving tab stop. Arrows move by day and week, Home/End to the week's ends, PageUp/PageDown by month (Shift: year). Selected is ink; today is a hairline.
| Prop | Type | Default | Description |
|---|---|---|---|
value | Date | ||
defaultValue | Date | ||
onValueChange | (date: Date) => void | ||
onSelect | (date: Date) => void | Deprecated. Use `onValueChange`. | |
defaultMonth | Date | ||
weekStart | 0 | 1 | 1 | 0 = Sunday, 1 = Monday. |
autoFocus | boolean | Move focus to the roving day on mount (a date picker opening). |
Also accepts Omit<HTMLAttributes<HTMLDivElement>, "onSelect" | "defaultValue">.
| Keys | Does |
|---|---|
| Tab | Focuses the month buttons, then the roving day |
| Arrow left, Arrow right | Previous or next day |
| Arrow up, Arrow down | Same day the week before or after |
| Home, End | First or last day of the week |
| Page up, Page down | Same day the month before or after |
| Shift + Page up, Shift + Page down | Same day the year before or after |
| Enter, Space | Selects the focused day |
<div class="rs-cal"><div class="rs-cal-head"><span class="rs-cal-title">July 2026</span><span class="rs-cal-nav"><button class="rs-page"><svg viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter"><path d="M10.5 3.75 L5.5 8.25 L10.5 12.75" vector-effect="non-scaling-stroke"/></svg></button><button class="rs-page"><svg viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter"><path d="M5.5 3.75 L10.5 8.25 L5.5 12.75" vector-effect="non-scaling-stroke"/></svg></button></span></div><div class="rs-cal-grid" role="grid"><div class="rs-cal-row" role="row"><span class="rs-cal-dow" role="columnheader">Mo</span><span class="rs-cal-dow" role="columnheader">Tu</span><span class="rs-cal-dow" role="columnheader">We</span><span class="rs-cal-dow" role="columnheader">Th</span><span class="rs-cal-dow" role="columnheader">Fr</span><span class="rs-cal-dow" role="columnheader">Sa</span><span class="rs-cal-dow" role="columnheader">Su</span></div><div class="rs-cal-row" role="row"><button class="rs-cal-day" role="gridcell" tabindex="-1">20</button><button class="rs-cal-day" role="gridcell" tabindex="-1">21</button><button class="rs-cal-day" role="gridcell" tabindex="-1">22</button><button class="rs-cal-day" role="gridcell" tabindex="-1">23</button><button class="rs-cal-day rs-cal-day-selected" role="gridcell" tabindex="0" aria-selected="true">24</button><button class="rs-cal-day rs-cal-day-today" role="gridcell" tabindex="-1" aria-current="date">25</button><button class="rs-cal-day" role="gridcell" tabindex="-1">26</button></div></div></div>