Date Picker
Calendar surface for selecting a single date, a range, a month, or a year. Floats as a panel under a Text Input trigger (the input shows the chosen date; click it to open the picker). Mirrors Figma node 105-1786 / 105-2329 / 105-2431 (light) + 5913-1594 / 5913-1609 / 5913-1616 (dark) 1:1. Panel chrome matches Dropdown — same `--shadow-3`, 8px radius, 1px border. Cells invert on Active state (dark fill + white text in light, white fill + dark text in dark).
When to use
- Use for date selection in forms (booking, filtering, scheduling)
- Use the Range variant for start/end date selection (check-in/check-out, date filters)
- Always attach to a Text Input trigger that shows the selected date
- Pre-select today or the most common default date to reduce user effort
- Use for time selection — the picker is date-only
- Use without a Text Input trigger — the panel must always float below a trigger element
- Allow past dates when the context requires future dates only
- Use inline (always visible) for non-primary date fields — keep it as a floating panel
DigiLawyer Patterns
No specific DigiLawyer patterns documented yet — to be added.
Anatomy
A Date Picker is a floating panel with a header, weekday row, and day grid. Panel chrome matches Dropdown.
- 1Panel —
.datepicker. Floating container, 320px wide, border + shadow-3. - 2Header —
.datepicker-header. Nav buttons + centered month/year title. - 3Nav buttons —
.datepicker-nav. Previous/next month. Arrow icons. - 4Weekday row —
.datepicker-weekdays. 7 column headers (Mo–Su). - 5Day grid —
.datepicker-grid. 7-column grid of day cells. - 6Day cell —
.datepicker-day. States:.is-active(selected, inverts),.is-today,.is-other(dimmed).
Default
A .datepicker panel sized 320px wide. Header has previous / next nav buttons + a centered title. Below it sits the 7-column weekday row and a 7-column day grid. Days from the previous / next month dim via .is-other; the currently selected day uses .is-active.
Variants
Four picker variants. Single is the day grid above. Range shows two months side-by-side; range endpoints use .is-active, the days between use .is-in-range. Month picker swaps the day grid for a 4×3 month-name grid. Year picker swaps for a 4×3 year grid (12 years at a time, navigated with the same prev/next arrows).
States
Five cell states. Default (no class). Hover — pointer-driven, also forced via .is-hover for static demos. Active (.is-active or aria-pressed="true") — the selected date / month / year. In-range (.is-in-range) — the days between two range endpoints. Disabled / Other-month (.is-disabled / .is-other) — non-selectable cells dimmed via --datepicker-cell-text-disabled.
Interactive
A real Text Input wired to a Date Picker — click the input to open the calendar, click a date to fill the input and close the picker, click outside to dismiss. The calendar navigates months with ‹/›; the title is itself a button that flips the view to Month picker → Year picker → back to Day. All state lives in the page script; lifting it into ds.js would make this a reusable widget.
Click the field to open the calendar.
Full matrix
Cell states across the three picker views — Day / Month / Year. Each row shows the same five states applied to the same cell-type so you can compare visual treatment at a glance.
Accessibility
Follows the WAI-ARIA Date Picker Dialog pattern.
| Attribute / Behaviour | Value | Notes |
|---|---|---|
| Panel role | role="dialog" + aria-modal="true" | The calendar panel is a modal dialog when open |
| Trigger | aria-haspopup="dialog" + aria-expanded | Set on the Text Input trigger to communicate open/closed state |
| Keyboard — open | Enter on trigger | Opens the calendar panel |
| Keyboard — navigate days | ↑ ↓ ← → | Move focus between day cells |
| Keyboard — select | Enter / Space | Selects the focused day and closes the panel |
| Keyboard — close | Esc | Closes without selecting; returns focus to the trigger |
| Nav buttons | aria-label="Previous month" / "Next month" | Icon-only buttons must have descriptive labels |
API
| Class / attr | On | Description |
|---|---|---|
| .datepicker | <div role="dialog"> | Panel wrapper. 320px wide, 16px padding, white surface, 8px radius, --shadow-3 drop, 1px border. Holds the header + a content grid (day / month / year). |
| .datepicker-range | .datepicker | Two-month side-by-side variant. Width is auto (sum of the two .datepicker-month children + gap). |
| .datepicker-month | <div> | Inner month container, used only inside .datepicker-range to host one of the two months. |
| .datepicker-header | <div> | Top row — flex with previous nav arrow + title + next nav arrow. |
| .datepicker-nav | <button> | The ‹ / › arrow buttons. 24×24, transparent, 16×16 icon. Hover uses --datepicker-cell-bg-hover. |
| .datepicker-title | <button> | The "July 2024" centered title. Click cycles the view to the Month picker when a dropdown chevron is shown. |
| .datepicker-weekdays | <div> | 7-column grid of weekday abbreviations (Mo Tu We Th Fr Sa Su). Body 2 Medium typography. |
| .datepicker-days | <div> | 7-column grid of .datepicker-cell buttons. Padded leading / trailing days from the prev / next month carry .is-other. |
| .datepicker-months | <div> | 4-column grid of 12 month names. 16px row gap. |
| .datepicker-years | <div> | 4-column grid of 12 years. Same row gap as months. |
| .datepicker-cell | <button> | The day / month / year cell. 32px tall (36px in month/year grids), centered content, 8px radius, Body 2 Medium typography. |
| .is-active / aria-pressed="true" | .datepicker-cell | Marks the selected cell. Inverts via tokens — dark fill + white text in light, white fill + dark text in dark. |
| .is-in-range | .datepicker-cell | Range middle — uses --datepicker-cell-bg-in-range and drops the cell's border-radius so neighbours flow together visually. |
| .is-range-start / .is-range-end | .datepicker-cell | The endpoint cells in a range. They keep their .is-active fill but drop the matching corner radius so they butt up against the in-range neighbour. |
| .is-disabled / disabled | .datepicker-cell | Non-selectable cell — dims via --datepicker-cell-text-disabled, cursor not-allowed. |
| .is-other | .datepicker-cell | Padded day from prev / next month. Same dimmed colour as Disabled but the cursor stays default. |