Theme
Sound
Jump to a component
Core · Forms
DateField
DateField picks a date, or a range of dates read by period: this month, the quarter, the dates on an invoice. A record people read by period, a campaign's start and end.
Live, from @anicanon/sakuga 2.0.0
Sakuga for iOS and Android is coming
iOS
Web only
DateField is made for the web, and isn’t part of Sakuga for iOS yet.
Android
Web only
DateField is made for the web, and isn’t part of Sakuga for Android yet.
- Use it for
- A date or a span of dates someone picks or reads by period.
- Not for
- A choice from a few named periods onlySelect
- A time of day or a durationNumberField
Guidelines
rangemakes it a range: the calendar shows two months beside thepresets({label, start, end}, a list a screen reader hears named bypresetsLabel, default "Presets"). Without it, one month and one date.valueis{start, end}for a range and a string for a date, both'YYYY-MM-DD'.onChangegets the same.- The field writes the dates the viewer's way (Intl,
locale, default the viewer's): "Oct 1 – 23, 2026". When the value is a preset, the preset's name comes first, muted. - A preset applies in one press and closes. A range of your own takes two presses, the start and then the end, and the band follows the pointer between them. A press before the start begins again from there. A date takes one press. Escape closes without a change.
minandmaxkeep days out of reach; they show muted and can't be picked.align: 'end'lines the open calendar up with the field's end edge, for a field at the end of a toolbar.- The range is one pale band; its start and end are solid ink, as Button's primary is, and invert with it in dark. The accent is only today's dot. In a range, each month's grid leaves the other month's days blank; a single date shows them muted.
- Where two months don't fit beside the presets (a phone), the range shows one month with the presets above it.
- Opened by
openor a pointer, the focused day draws no ring until a key is pressed; opened from the keyboard, it does. - For a board:
open,month(the first month shown,'YYYY-MM'or a day in it),todayandweekStartsOn(default from the locale) fix what it shows.
- A range can also be set in one gesture: press on the start, slide to the end and let go; sliding backwards sets the range the other way round. Let go where no day in reach was crossed, and only the start is set, as after a first press.
import { DateField } from '@anicanon/sakuga/ui';
const presets = [
{ label: 'This month', start: '2026-10-01', end: '2026-10-23' },
{ label: 'Last month', start: '2026-09-01', end: '2026-09-30' },
{ label: 'Last 30 days', start: '2026-09-24', end: '2026-10-23' },
{ label: 'Last 90 days', start: '2026-07-26', end: '2026-10-23' },
{ label: 'This year', start: '2026-01-01', end: '2026-10-23' },
];
export default function DateFieldExample() {
return (
<DateField
label="Dates"
range
open
today="2026-10-23"
max="2026-10-23"
month="2026-09-01"
locale="en-US"
value={{ start: '2026-10-01', end: '2026-10-23' }}
presets={presets}
/>
);
}PropsDateFieldProps
labelstringhintstringdisabledbooleanplaceholderstringrangebooleanvaluestring | DateRangeonChange(value: string | DateRange) => voidpresets{ label: string; start: string; end: string; }[]presetsLabelstringDefault 'Presets'minstringmaxstringopenbooleanmonthstringThe first month shown, 'YYYY-MM' or any day in it ('YYYY-MM-DD'); anything else falls back to the default.
todaystring'YYYY-MM-DD'. Default the real today.
localestringDefault the viewer's.
weekStartsOnnumber0 is Sunday. Default from the locale.
align'start' | 'end'Default 'start'The open calendar lines up with the field's start or end edge.
previousLabelstringDefault "Previous month" and "Next month".
nextLabelstringoptionalbooleanAs on TextField.
help{ title?: string; text: ReactNode; }maxWidthnumberidstringclassNamestringTypes
type DateRange = { start: string; end: string; };
Each token reads a role, and the role lands on a value in each theme. Change the role and every component that reads it follows.
Space and shape
datefield-icon-sizeselect-value-icon-size20px
datefield-chevron-sizeselect-chevron-icon-size16px
datefield-disabled-opacityopacity-disabled0.35
datefield-body-gapgap-xl16px
datefield-months-gapgap-xl16px
datefield-month-gapgap-xs4px
datefield-nav-sizeicon-button-size44px
datefield-day-sizesize-control-sm36px
datefield-weekday-heightsize-control-sm36px
datefield-day-focus-offsetits own value
calc(-1 * 2px)
datefield-today-sizestate-dot-size6px
datefield-today-bottominset-xs4px
datefield-today-radiusradius-full9999px
datefield-forced-band-widthline-hairline1px
Type
datefield-title-fonttext-label600 14px/20px "Funnel Sans", system-ui, -apple-system, "Segoe UI", sans-serif
datefield-weekday-fonttext-small500 12px/16px "Funnel Sans", system-ui, -apple-system, "Segoe UI", sans-serif
datefield-day-fonttext-body400 14px/20px "Funnel Sans", system-ui, -apple-system, "Segoe UI", sans-serif
datefield-end-font-weightweight-strong600
Motion
datefield-durationmotion-fast160ms
datefield-easingease-standardcubic-bezier(0.2, 0, 0, 1)
Checked automatically with axe-core 4.11.4 against WCAG 2.2 A and AA, on the live preview above, in light and dark, on 26 October 2026.
- Result
- No issues found, in light or in dark.
- By design
- The field is a button named by
labeland the dates, that opens a dialog named bylabel. Each month is a grid named by its month and year, with one Tab stop: arrows move a day or a week, Page Up and Page Down a month (with Shift a year), Home and End to the start and end of the week, and Enter or Space picks. Days in the range are selected, days out of reach disabled, and today is the current date; the new month is announced when it changes. The presets are a listbox with one Tab stop. Escape, a press outside or Tab out of it closes, and after Escape focus goes back to the field. Sliding across days is for the pointer; two presses, or the keys, do the same. - Not tested
- The automated check doesn't test keyboard use, focus order or what a screen reader says. The lines above are the design system's own account of them.