Calendar

● Stable · v0.1

A month-grid date picker for choosing a single day — the date of a sale or expense, a loan's due date, a reminder. Big, readable day cells the user taps, with the selected day and today made obvious at a glance.

In context

9:41

Try it

Purpose

Dates are the one input people are worst at typing — formats differ (is 03/04 March or April?), and our users are often low-literacy and on a slow keyboard. A grid sidesteps all of it: the month is laid out the way a wall calendar is, the user finds the day and taps it, and there's no format to get wrong. Today and the chosen day are marked so the user always knows where they are.

It is controlled and presentational: you own the selected value and the component emits the picked Date via onChange. It carries no copy of its own — month and weekday names come in as props, so the same calendar localises by passing the right label arrays.

Anatomy

  • Header — the current month and year, with previous / next arrows to move between months.
  • Weekday row — short day labels, ordered by firstDayOfWeek (Sunday or Monday).
  • Day grid — one tappable cell per day; each is at least a 44dp touch target.
  • Selected day — a filled brand-teal circle with white text. Today — a teal ring and teal text when it isn't the selection.

Code

import { Calendar } from '@daytwo/components'
const [date, setDate] = useState<Date | null>(null)
<Calendar
value={date}
onChange={setDate}
maxDate={new Date()} // can't pick the future
/>

To localise, pass the month and weekday names (and start the week on Monday where that's the norm):

<Calendar
value={date}
onChange={setDate}
firstDayOfWeek={1}
monthNames={MY_MONTHS} // 12 names, Jan→Dec
weekdayNames={MY_WEEKDAYS} // 7 short names, Sun→Sat
fontFamily={burmeseFamily}
/>

When to use

  • To pick a specific calendar day — backdating a sale, setting a due date or a reminder.
  • Whenever a typed date would be slow or error-prone (most of the time, for our users).

When NOT to use

  • For a time, not a day — pair a separate time control; the Calendar only returns a date.
  • For a wide range of years (a birth year) — month-by-month paging is slow; a year/decade picker fits better.
  • When the choice is really "today vs a few recent days" — a row of chips (Segmented control) is faster than opening a full grid.

The correct scenario

Recording an expense the owner forgot to log yesterday: the sheet shows "When was this?" with the Calendar opened on the current month, today ringed. They tap yesterday — it fills teal — and continue. No keyboard, no format, no mistake.

Anti-patterns

Do
A month grid the user reads like a wall calendar and taps the day — today and the selection clearly marked.
Don't
A free-text field asking the user to type 'DD / MM / YYYY' — ambiguous format, fiddly on a slow keyboard, easy to get wrong.
Tap a day, don't type one

Typed dates are the most error-prone input we have — wrong format, wrong order, typos. Let the user point at the day instead.

Edge cases

No selection yet
Open on today's month with today ringed
WhyGives the user a known starting point
Out-of-range days
minDate/maxDate grey them out and block the tap
WhyStops invalid picks (e.g. a future sale) before they happen
Month boundaries
Leading and trailing cells stay blank, not crammed with other months
WhyKeeps each month readable on a small screen
Localized labels
Pass monthNames/weekdayNames + fontFamily
WhyBurmese/Thai names and script render correctly
Week start
firstDayOfWeek orders the header and grid
WhySome locales start the week on Monday
Disabled
The whole grid dims and stops responding
WhyA clear "not now" state

Tokens used

color.brand.primarycolor.brand.tintcolor.brand.onPrimarycolor.text.primarycolor.text.tertiarycolor.text.disabledradius.pilltypography.subhead