Date & Time.
We don't reinvent the date picker. Swivel uses Material Design 3's date and time components — their structure, keyboard model, and ARIA — and recolors every surface, container, and accent to the Swivel palette.
Policy
What we adopt and what we override.Date and time pickers are deceptively expensive to build well — keyboard nav, locale, range selection, year jumps, accessibility. Material Design 3 already solves all of that. So we inherit the M3 component specs end-to-end and only override the color tokens. Visually, a picker reads as Swivel; structurally and behaviorally, it reads as M3.
What we inherit from M3
- Component structure — modal and docked date pickers, single + range modes, dial + numeric time pickers.
- Interaction model — arrow-key navigation, type-to-jump, year/month dropdowns, click-and-drag range selection, dial drag.
- Accessibility — ARIA roles, focus rings, screen-reader landmarks, RTL handling, locale-aware day-of-week order.
- Layout dimensions — header height, grid cell size (40×40 single, 36×36 range), elevation levels, motion timings.
What we override
- Color tokens only. Every
primary,surface,outlinein the M3 token tree maps to a Swivel token (table below). - Type falls back to our display + body stack (Avantt / Antarctica). M3's "Roboto" recommendation is replaced by the platform font tokens.
- Radius follows our radius scale. M3's 16px container becomes
--radius-xl(12px) so pickers don't look out of family.
Token mapping
M3 color roles → Swivel tokens.Drop these CSS custom properties at the picker's root and the M3 component sheet picks them up unchanged. Every example below is rendered against exactly this mapping.
| M3 role | Swivel token | Where it lands |
|---|---|---|
primary |
--switch-contrast |
Selected day fill, time-dial hand, focused outline, primary text buttons |
on-primary |
--graphite-contrast |
Day number, dial mark, and range endpoint on the selected fill. Not --white: switch cyan is bright in both themes, so white ink reads at ~1.4:1 in dark mode. |
primary-container |
--switch-bg |
Range fill between start/end, active hour/minute readout |
surface |
--surface-base |
Picker plate |
surface-container |
--surface-muted |
Header band, time dial background |
outline |
--surface-stroke |
Outer hairline, text-field border |
outline-variant |
--graphite-10 |
Inner dividers between header and grid |
on-surface |
--graphite-90 |
Headline, day numbers, hour/minute readouts |
on-surface-variant |
--graphite-bg |
Eyebrow, day-of-week row, muted days outside the month |
Date picker
M3 single-date modal — recolored.Use for single-date selection — campaign start, deploy on, archive after. Falls back to the date-input field (§06) on dense forms where opening a picker is overkill.
Mon, May 18
Fri, May 22
Date range
Two-month grid with range fill.Use for any "from / to" pair — campaign flight, report window, analytics range. Range fill uses --switch-bg so the bracket reads as soft accent rather than a second selection.
Mon, May 12 → Fri, May 22
Time picker
Dial picker for hour selection.Default to the dial. The active hour/minute readout fills with --switch-bg so the user always knows which segment they're editing. AM/PM uses an outlined toggle pair with the active state filled.
Date input field
M3 outlined text field with calendar trailing icon.For dense forms where opening a picker is friction. Accepts MM/DD/YYYY typed input; the trailing icon opens the modal picker on click. Same focus and error states as the rest of our text fields.
Usage
When to pick which.- Date input field — default for forms. Compact, types-or-clicks, validates inline.
- Date picker (modal) — when the user needs a calendar view to orient: holidays, weekends, "next available."
- Date range — any from/to pair. Never present two separate date pickers — the range component handles the relationship.
- Time picker (dial) — default for time. Faster for round times (00, 15, 30, 45).
- Time picker (numeric) — when sub-minute precision is needed, or in dense forms. M3 toggle between dial and numeric is preserved.
- Don't roll your own. If a picker feels wrong, fix the token mapping, not the component.