Design system

Date picker

A calendar panel for picking one date or a date range, with month and year menus.

import { DatePicker } from "@/ui";

DatePicker matches the Figma Date Picker component, including its day, month and year cells. It is the panel only. Put it in a popover under the field or button that opens it.

The examples on this page fix today at Dec 11, 2024, to match Figma. In the app, leave today out and it uses the real date.

Single date

Click a day to pick it. Today has a blue outline, and the picked day is filled blue. The arrows change the month.

const [date, setDate] = useState(null);

<DatePicker value={date} onChange={setDate} />

With Cancel and Apply

With footer, picks are a draft until the person clicks Apply. Use it when a change reloads data, such as a dashboard's date filter.

<DatePicker value={applied} footer onApply={setApplied} onCancel={close} />

Range

mode="range" shows two months side by side. The first click sets the start, the second sets the end, and the days between turn light blue. A click before the start swaps them. While you choose the end, the range follows the pointer.

const [range, setRange] = useState({ from: null, to: null });

<DatePicker mode="range" value={range} onChange={setRange} />

Use months={1} when the range fits in one month or space is tight.

In a popover

Open the picker under a trigger that shows the current value. With a footer, Apply closes it.

<Button variant="secondaryGhost" label="Nov 1, 2024 โ€“ Nov 30, 2024" onClick={toggle} />
{open && (
  <DatePicker mode="range" value={range} footer onApply={(r) => { setRange(r); close(); }} onCancel={close} />
)}

Month and year menus

Click the month or the year in the header to jump. The current month and year are blue, and the one on screen is filled. The year list scrolls to the selected year.

Cells

CellRestHoverSelected
Day#757A97 textPrimary 50 fill, Primary 500 textPrimary 500 fill, white text
TodayAdds a 1px Primary 500 border
Day in a rangePrimary 50 fill, square corners
Month or year#757A97 text (Primary 500 when current)Primary 50 fill, Primary 500 textPrimary 500 fill, white text

Anatomy

PartSpec
Panel264px wide (512px for two months), 12px padding, 8px radius, white, shadow 0 8px 12px at 10%
Header20px blue arrows. Month and year in 14px medium, each with a 12px caret
Weekdays12px primary text, Sunday first
Days32px tall, 8px radius, 12px text, 4px between weeks
Month and year menus80px wide, 8px padding, 4px gaps, items 2px 20px with a 4px radius
Footerghost Cancel and secondary Apply, both sm

Figma's footer buttons use 10px text. Code uses sm Buttons, which are 12px, because 12px is the smallest text in the product.

Props

PropTypeDefaultNotes
mode"single" | "range""single"
valueDate | null or { from, to }Controlled value.
defaultValuesame as valueUncontrolled start value.
onChangefunctionEvery pick without a footer; on Apply with one.
months1 | 21, or 2 for a range
footerbooleanfalseCancel and Apply. Apply stays disabled until the pick is complete.
onApplyfunctionGets the draft.
onCancelfunctionThe draft resets to value.
todayDatenow
yearRange[number, number]10 years back to 5 aheadYears in the year menu.
classNamestring""For layout only.

Accessibility

  • Every day is a button labelled with the full date, such as "Wednesday, December 11, 2024". Picked days have aria-pressed, and today has aria-current="date".
  • Only one day is in the tab order. Arrow keys move by a day or a week, Home and End go to the start or end of the week, and Page Up and Page Down change the month. The view follows the focus.
  • The month and year menus are listboxes. Escape or a click outside closes them.

Do and don't

  • Do show the chosen date or range on the trigger, so people can see it without opening the picker.
  • Do use the footer when picking reloads data.
  • Don't use a date picker for a fixed set of periods like "Last 7 days". Use a Dropdown.

On this page