Open a range picker full screen on a compact window

M3 § Date Pickers, Anatomy describes a 14-element full-screen range picker for
compact breakpoints; `<x-datepicker range>` always used the 360px modal dialog,
which on a phone is cramped for a two-month scroll (plan step 24, audit
docs/audits/m3-alignment/inputs.md § Missing). A third presentation, "full",
joins docked and modal: the same dialog grown to the screen, an app bar with a
close button and Save, the supporting text and the range as the headline over a
divider, the weekday labels held at the top, and the months in one vertically
scrolling list, each under its own label. The grid now draws from one `rows`
getter, which is this month's weeks everywhere else, so the docked and
single-date pickers render exactly as before. The list is a window of months
grown by the scroll and by the keyboard, since nothing here is lazy.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold / reini
2026-09-14 06:36:49 +02:00
co-authored by Claude Fable 5.1
parent 790ef93c6a
commit 2662040c24
6 changed files with 277 additions and 20 deletions
+94 -7
View File
@@ -1,10 +1,12 @@
/*
* M3's date pickers — docked, modal and modal input (resources/views/components/datepicker.blade.php).
*
* One `<dialog>` wears both: docked it is a popover hung under the field by CSS anchor positioning
* (large corner), modal it is opened with `showModal()` over a scrim (extra-large corner, with M3's
* header: a title, the chosen date as the headline, and the switch between calendar and typing).
* Both are 360px of surface-container-high at elevation 3.
* One `<dialog>` wears all three: docked it is a popover hung under the field by CSS anchor
* positioning (large corner), modal it is opened with `showModal()` over a scrim (extra-large
* corner, with M3's header: a title, the chosen date as the headline, and the switch between
* calendar and typing), and full — a `range` picker on a compact window — is that modal dialog
* grown to the whole screen, M3's full-screen range picker. The first two are 360px of
* surface-container-high at elevation 3; the third keeps the colour and drops the corner.
*
* Values from DatePickerModalTokens and DateInputModalTokens (androidx Compose Material 3, commit
* 27cf9a7d5788aa0f5f2d8b6699ce279560daf326) and the layout of DatePicker.kt, DateRangePicker.kt and
@@ -13,15 +15,19 @@
*
* [data-datepicker] the root: the field, its support line, the dialog
* [data-datepicker-support] the hint, a server error, or what is wrong with the typed date
* [data-datepicker-picker] the <dialog>; data-presentation="docked" | "modal"
* [data-datepicker-picker] the <dialog>; data-presentation="docked" | "modal" | "full"
* [data-datepicker-surface]
* [data-datepicker-header] modal only: title, headline, switch
* [data-datepicker-calendar]
* [data-datepicker-full] full only: [data-datepicker-app-bar] (close, switch, Save)
* and [data-datepicker-full-headline] (title, headline)
* [data-datepicker-calendar] the scroller, full screen
* [data-datepicker-nav] month and year: one menu button and arrows (modal),
* or a month and a year stepper (docked, data-docked)
* [data-datepicker-grid] the weekdays and six weeks of [data-datepicker-day] cells:
* data-today, data-selected, data-start, data-end,
* data-between, data-outside, data-blank, aria-disabled
* data-between, data-outside, data-blank, aria-disabled
* full screen, every month of the window under a
* [data-datepicker-month-label] row
* [data-datepicker-years] the modal year grid
* [data-datepicker-menu] a docked month or year list of [data-datepicker-option]s
* [data-datepicker-entry] the text field(s) of the modal input
@@ -128,6 +134,87 @@
border-radius: var(--md-sys-shape-corner-xl);
}
/* ---- The full-screen range picker (compact) ------------------------------------------------- */
/* M3 § Date Pickers: a range picker at a compact breakpoint is a full-screen dialog, "covering
* the whole screen for readability and touch-target size", with a close (×) icon button and a
* Save confirmation instead of Cancel and OK. Its anatomy is the modal picker's parts in a
* different order: an app bar, the supporting text and the range as the headline, a divider, the
* day-of-week labels, and a vertically scrolling list of months under them, each with its label.
* Only the months scroll; everything above them stays where it is. */
[data-datepicker-picker][data-presentation="full"] {
inset: 0;
width: 100vw;
max-width: none;
height: 100dvh;
max-height: none;
margin: 0;
}
[data-datepicker-picker][data-presentation="full"] [data-datepicker-surface] {
height: 100%;
max-height: none;
padding-block: var(--material-safe-top, env(safe-area-inset-top)) var(--material-safe-bottom, env(safe-area-inset-bottom));
overflow: hidden;
border-radius: 0;
box-shadow: none;
}
[data-datepicker-full] {
flex: none;
border-bottom: 1px solid var(--md-sys-color-outline-variant);
color: var(--md-sys-color-on-surface-variant);
}
[data-datepicker-app-bar] {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
height: 4rem;
padding-inline: 0.25rem 0.75rem;
}
[data-datepicker-app-bar-actions] {
display: flex;
align-items: center;
gap: 0.25rem;
}
[data-datepicker-full-headline] {
display: flex;
flex-direction: column;
gap: 0.25rem;
padding: 0 1.5rem 1rem;
}
/* The calendar is the only part that scrolls, with the weekday header held at its top. */
[data-datepicker-picker][data-presentation="full"] [data-datepicker-calendar] {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
overscroll-behavior: contain;
}
[data-datepicker-picker][data-presentation="full"] [data-datepicker-grid] thead th {
position: sticky;
top: 0;
z-index: 1;
background-color: var(--md-sys-color-surface-container-high);
}
[data-datepicker-picker][data-presentation="full"] [data-datepicker-day] {
scroll-margin-block: 3rem;
}
[data-datepicker-month-label] {
padding: 1.25rem 0 0.5rem;
color: var(--md-sys-color-on-surface-variant);
font: var(--md-sys-typescale-title-sm);
letter-spacing: var(--md-sys-typescale-title-sm-tracking);
text-align: start;
}
/* ---- The modal header ---------------------------------------------------------------------- */
[data-datepicker-header] {