/* * M3's date pickers — docked, modal and modal input (resources/views/components/datepicker.blade.php). * * One `` 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 * DateInput.kt; the docked picker's from material-web's md-comp-date-picker-docked tokens (v0_192). * Apache-2.0. * * [data-md-datepicker] the root: the field, its support line, the dialog * [data-md-datepicker-support] the hint, a server error, or what is wrong with the typed date * [data-md-datepicker-picker] the ; data-md-presentation="docked" | "modal" | "full" * [data-md-datepicker-surface] * [data-md-datepicker-header] modal only: title, headline, switch * [data-md-datepicker-full] full only: [data-md-datepicker-app-bar] (close, switch, Save) * and [data-md-datepicker-full-headline] (title, headline) * [data-md-datepicker-calendar] the scroller, full screen * [data-md-datepicker-nav] month and year: one menu button and arrows (modal), * or a month and a year stepper (docked, data-md-docked) * [data-md-datepicker-grid] the weekdays and six weeks of [data-md-datepicker-day] * cells: data-md-today, data-md-selected, data-md-start, * data-md-end, data-md-between, data-md-outside, * data-md-blank, aria-disabled — full screen, every month * of the window under a [data-md-datepicker-month-label] row * [data-md-datepicker-years] the modal year grid * [data-md-datepicker-menu] a docked month or year list of [data-md-datepicker-option]s * [data-md-datepicker-entry] the text field(s) of the modal input * [data-md-datepicker-actions] Cancel and OK * * The year, the menu buttons and the docked lists' options render the foundation's `md-state-layer` * and `md-focus-ring` (foundation/interaction.css): each draws its layer in its own ink and its * ring round itself, so the classes need one refinement only — an option's ring sits inside its * full-width row. Cancel, OK and the icon buttons are , which renders the classes too. * The day alone keeps a state layer and ring of its own: the element focused and pressed is the * 48px cell, while both are drawn on the 40px round indicator inside it, which a class drawn on the * element it sits on cannot do, and `:focus-visible` never matches that child. Its layer takes * M3's state opacities (tokens/state.css) and is gone from a disabled or blank day. * * Lengths are px, and spacing is the measurement tokens: M3 gives every size here in dp. A * disabled day or option is on-surface at M3's 38% content opacity. */ @layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility; @import './field.css'; @import './icon.css'; @import './button.css'; @layer material.components { /* ---- The support line under the field ------------------------------------------------------ */ /* The padding is on the lines, not the box, so a box with nothing to say takes no room. */ [data-md-datepicker-support] { --datepicker-pad: var(--md-sys-measurement-space200); color: var(--md-sys-color-on-surface-variant); font: var(--md-sys-typescale-body-sm); letter-spacing: var(--md-sys-typescale-body-sm-tracking); } [data-md-datepicker-support] > * { padding: var(--md-sys-measurement-space50) var(--datepicker-pad) 0; } [data-md-datepicker-support][data-md-size='sm'] { --datepicker-pad: 12px; } [data-md-datepicker-support][data-md-size='xs'] { --datepicker-pad: var(--md-sys-measurement-space125); } [data-md-datepicker-error] { color: var(--md-sys-color-error); } /* ---- The dialog ---------------------------------------------------------------------------- */ [data-md-datepicker-picker] { inset: auto; width: 360px; max-width: calc(100vw - var(--md-sys-measurement-space200)); max-height: calc(100dvh - var(--md-sys-measurement-space200)); margin: 0; padding: 0; overflow: visible; border: 0; background: transparent; color: var(--md-sys-color-on-surface); } [data-md-datepicker-picker]:popover-open { top: anchor(bottom); left: anchor(left); margin-block: var(--md-sys-measurement-space50); position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline; opacity: 1; transition-property: opacity; transition-duration: var(--md-sys-motion-effects-fast-duration); transition-timing-function: var(--md-sys-motion-effects-fast); } @starting-style { [data-md-datepicker-picker]:popover-open { opacity: 0; } } [data-md-datepicker-picker]:modal { inset: 0; margin: auto; opacity: 1; scale: 1; transition-property: opacity, scale; transition-duration: var(--md-sys-motion-spatial-fast-duration); transition-timing-function: var(--md-sys-motion-spatial-fast); } @starting-style { [data-md-datepicker-picker]:modal { opacity: 0; scale: 0.95; } } [data-md-datepicker-picker]:modal::backdrop { background-color: color-mix(in srgb, var(--md-sys-color-scrim) 32%, transparent); } [data-md-datepicker-surface] { display: flex; flex-direction: column; max-height: inherit; overflow-x: hidden; overflow-y: auto; border-radius: var(--md-sys-shape-corner-lg); background-color: var(--md-sys-color-surface-container-high); box-shadow: var(--md-sys-elevation-3); outline: none; } [data-md-datepicker-picker][data-md-presentation='modal'] [data-md-datepicker-surface] { 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-md-datepicker-picker][data-md-presentation='full'] { inset: 0; width: 100vw; max-width: none; height: 100dvh; max-height: none; margin: 0; } [data-md-datepicker-picker][data-md-presentation='full'] [data-md-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-md-datepicker-full] { flex: none; border-bottom: 1px solid var(--md-sys-color-outline-variant); color: var(--md-sys-color-on-surface-variant); } [data-md-datepicker-app-bar] { display: flex; align-items: center; justify-content: space-between; gap: var(--md-sys-measurement-space100); height: 64px; padding-inline: var(--md-sys-measurement-space50) 12px; } [data-md-datepicker-app-bar-actions] { display: flex; align-items: center; gap: var(--md-sys-measurement-space50); } [data-md-datepicker-full-headline] { display: flex; flex-direction: column; gap: var(--md-sys-measurement-space50); padding: 0 var(--md-sys-measurement-space300) var(--md-sys-measurement-space200); } /* The calendar is the only part that scrolls, with the weekday header held at its top. */ [data-md-datepicker-picker][data-md-presentation='full'] [data-md-datepicker-calendar] { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; } [data-md-datepicker-picker][data-md-presentation='full'] [data-md-datepicker-grid] thead th { position: sticky; top: 0; z-index: 1; background-color: var(--md-sys-color-surface-container-high); } [data-md-datepicker-picker][data-md-presentation='full'] [data-md-datepicker-day] { scroll-margin-block: 48px; } [data-md-datepicker-month-label] { padding: 20px 0 var(--md-sys-measurement-space100); 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-md-datepicker-header] { display: flex; flex-direction: column; justify-content: space-between; flex: none; min-height: 120px; border-bottom: 1px solid var(--md-sys-color-outline-variant); color: var(--md-sys-color-on-surface-variant); } [data-md-datepicker-header][data-md-range] { min-height: 128px; } [data-md-datepicker-title] { padding: var(--md-sys-measurement-space200) 12px 0 var(--md-sys-measurement-space300); font: var(--md-sys-typescale-label-lg); letter-spacing: var(--md-sys-typescale-label-lg-tracking); } [data-md-datepicker-headline-row] { display: flex; align-items: center; justify-content: space-between; gap: var(--md-sys-measurement-space100); padding: 0 12px 12px var(--md-sys-measurement-space300); } [data-md-datepicker-headline] { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: var(--md-sys-typescale-headline-lg); letter-spacing: var(--md-sys-typescale-headline-lg-tracking); } [data-md-datepicker-header][data-md-range] [data-md-datepicker-headline] { font: var(--md-sys-typescale-title-lg); letter-spacing: var(--md-sys-typescale-title-lg-tracking); } /* ---- Month and year navigation -------------------------------------------------------------- */ [data-md-datepicker-nav] { display: flex; flex: none; align-items: center; justify-content: space-between; height: 56px; padding-inline: 12px; color: var(--md-sys-color-on-surface-variant); } [data-md-datepicker-nav][data-md-docked] { height: 64px; padding-inline: var(--md-sys-measurement-space50); } [data-md-datepicker-stepper], [data-md-datepicker-arrows] { display: flex; align-items: center; } /* While a year or month list is open the arrows keep their place but are gone, as androidx hides them. */ [data-md-datepicker-arrows][data-md-concealed] { visibility: hidden; } [data-md-datepicker-menu-button] { display: inline-flex; align-items: center; gap: var(--md-sys-measurement-space100); height: 40px; padding-inline: 12px var(--md-sys-measurement-space100); border-radius: var(--md-sys-shape-corner-full); color: var(--md-sys-color-on-surface-variant); font: var(--md-sys-typescale-label-lg); letter-spacing: var(--md-sys-typescale-label-lg-tracking); white-space: nowrap; cursor: pointer; } [data-md-datepicker-nav][data-md-docked] [data-md-datepicker-menu-button] { padding-inline: var(--md-sys-measurement-space100) var(--md-sys-measurement-space50); gap: var(--md-sys-measurement-space50); } [data-md-datepicker-menu-arrow] { transition: rotate var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast); } [data-md-datepicker-menu-button][aria-expanded='true'] [data-md-datepicker-menu-arrow] { rotate: 180deg; } /* ---- The days ----------------------------------------------------------------------------- */ [data-md-datepicker-grid] { width: calc(100% - 24px); margin-inline: 12px; border-collapse: collapse; table-layout: fixed; } [data-md-datepicker-grid] th { height: 48px; padding: 0; color: var(--md-sys-color-on-surface); font: var(--md-sys-typescale-body-lg); letter-spacing: var(--md-sys-typescale-body-lg-tracking); text-align: center; } [data-md-datepicker-day] { --datepicker-layer: var(--md-sys-color-on-surface-variant); position: relative; height: 48px; padding: 0; color: var(--md-sys-color-on-surface); font: var(--md-sys-typescale-body-lg); letter-spacing: var(--md-sys-typescale-body-lg-tracking); text-align: center; cursor: pointer; outline: none; isolation: isolate; -webkit-tap-highlight-color: transparent; } /* The 40px indicator, its own stacking context so the state layer lies over a chosen day's primary fill rather than under it. */ [data-md-datepicker-day] > span { position: relative; display: grid; place-items: center; width: 40px; height: 40px; margin: auto; border-radius: var(--md-sys-shape-corner-full); isolation: isolate; transition: background-color var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast); } /* The day's state layer and focus ring, on the indicator (see the header). */ [data-md-datepicker-day] > span::before { content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit; background-color: var(--datepicker-layer); opacity: 0; pointer-events: none; transition: opacity var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast); } @media (hover: hover) { [data-md-datepicker-day]:hover > span::before { opacity: var(--md-sys-state-hover-state-layer-opacity); } } [data-md-datepicker-day]:focus-visible > span::before { opacity: var(--md-sys-state-focus-state-layer-opacity); } [data-md-datepicker-day]:active > span::before { opacity: var(--md-sys-state-pressed-state-layer-opacity); } [data-md-datepicker-day]:is([aria-disabled='true'], [data-md-blank]) > span::before { display: none; } [data-md-datepicker-day]:focus-visible > span { outline: 3px solid var(--md-sys-color-secondary); outline-offset: 2px; } [data-md-datepicker-day][data-md-blank] { cursor: default; } /* A docked picker shows the neighbouring months' days, quieter. */ [data-md-datepicker-day][data-md-outside] { color: var(--md-sys-color-on-surface-variant); } [data-md-datepicker-day][data-md-today] { --datepicker-layer: var(--md-sys-color-primary); color: var(--md-sys-color-primary); } [data-md-datepicker-day][data-md-today] > span { box-shadow: inset 0 0 0 1px var(--md-sys-color-primary); } /* A range's band: secondary-container behind the days between its ends, from the middle of each end. */ [data-md-datepicker-day][data-md-between]::before, [data-md-datepicker-day][data-md-start]::before, [data-md-datepicker-day][data-md-end]::before { content: ''; position: absolute; inset-block: var(--md-sys-measurement-space50); inset-inline: 0; z-index: -1; background-color: var(--md-sys-color-secondary-container); } [data-md-datepicker-day][data-md-start]::before { inset-inline-start: 50%; } [data-md-datepicker-day][data-md-end]::before { inset-inline-end: 50%; } [data-md-datepicker-day][data-md-between] { --datepicker-layer: var(--md-sys-color-on-secondary-container); color: var(--md-sys-color-on-secondary-container); } [data-md-datepicker-day][data-md-selected] { --datepicker-layer: var(--md-sys-color-on-primary); color: var(--md-sys-color-on-primary); } [data-md-datepicker-day][data-md-selected] > span { background-color: var(--md-sys-color-primary); box-shadow: none; } [data-md-datepicker-day][aria-disabled='true'] { color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-content-opacity) * 100%), transparent); cursor: default; } [data-md-datepicker-day][aria-disabled='true'][data-md-today] > span { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-content-opacity) * 100%), transparent); } /* ---- The modal year grid ----------------------------------------------------------------- */ [data-md-datepicker-years] { display: grid; grid-template-columns: repeat(3, 1fr); align-content: start; justify-items: center; row-gap: var(--md-sys-measurement-space200); height: 335px; padding: var(--md-sys-measurement-space100) 12px; overflow-y: auto; overscroll-behavior: contain; border-bottom: 1px solid var(--md-sys-color-outline-variant); } [data-md-datepicker-year] { width: 72px; height: 36px; border-radius: var(--md-sys-shape-corner-full); color: var(--md-sys-color-on-surface-variant); font: var(--md-sys-typescale-body-lg); letter-spacing: var(--md-sys-typescale-body-lg-tracking); cursor: pointer; scroll-margin-block: var(--md-sys-measurement-space100); } [data-md-datepicker-year][data-md-current] { color: var(--md-sys-color-primary); box-shadow: inset 0 0 0 1px var(--md-sys-color-primary); } [data-md-datepicker-year][aria-selected='true'] { background-color: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); box-shadow: none; } /* ---- The docked month and year lists ------------------------------------------------------ */ [data-md-datepicker-menu] { height: 344px; padding-block: var(--md-sys-measurement-space100); overflow-y: auto; overscroll-behavior: contain; border-top: 1px solid var(--md-sys-color-outline-variant); } [data-md-datepicker-option] { display: flex; align-items: center; gap: var(--md-sys-measurement-space200); width: 100%; height: 48px; padding-inline: var(--md-sys-measurement-space200); color: var(--md-sys-color-on-surface); font: var(--md-sys-typescale-body-lg); letter-spacing: var(--md-sys-typescale-body-lg-tracking); text-align: start; cursor: pointer; } /* The foundation's ring, drawn inside the full-width row rather than round it. */ [data-md-datepicker-option]:focus-visible { outline-offset: -3px; } [data-md-datepicker-option][aria-selected='true'] { background-color: var(--md-sys-color-surface-container-highest); } [data-md-datepicker-option][aria-disabled='true'] { color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-content-opacity) * 100%), transparent); cursor: default; } [data-md-datepicker-check] { color: var(--md-sys-color-on-surface); } [data-md-datepicker-option]:not([aria-selected='true']) [data-md-datepicker-check] { visibility: hidden; } /* ---- The modal input ------------------------------------------------------------------------ */ [data-md-datepicker-entry] { padding: var(--md-sys-measurement-space125) var(--md-sys-measurement-space300) var(--md-sys-measurement-space200); } [data-md-datepicker-entry-fields][data-md-range] { display: grid; grid-template-columns: 1fr 1fr; gap: var(--md-sys-measurement-space100); } /* ---- Cancel and OK ---------------------------------------------------------------------------- */ [data-md-datepicker-actions] { display: flex; flex: none; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--md-sys-measurement-space100); padding: var(--md-sys-measurement-space50) var(--md-sys-measurement-space75) var(--md-sys-measurement-space100) 12px; } }