/* * 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 * * Cancel, OK and the icon buttons in the app bar and the nav are , which draws its own * state layer and focus ring from the foundation classes. The day, the year, the menu button and * the list option keep their own hand-rolled state layer and focus ring, as `data-md-field-button` * does (field.css): each interactive box is bigger than the round indicator drawn inside it (a * day's cell is 48px, its indicator 40px; a menu button and an option are full rows), which the * foundation's classes, fixed to the element they are on, cannot draw — and `:focus-visible` only * ever matches the element actually focused, never a smaller child inside it. * * Every state layer here is the content colour at 8% on hover (only where a pointer can hover) and * 10% on focus and press; keyboard focus adds the package's 3px secondary ring. */ @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: 1rem; 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: 0.25rem var(--datepicker-pad) 0; } [data-md-datepicker-support][data-md-size='sm'] { --datepicker-pad: 0.75rem; } [data-md-datepicker-support][data-md-size='xs'] { --datepicker-pad: 0.625rem; } [data-md-datepicker-error] { color: var(--md-sys-color-error); } /* ---- The dialog ---------------------------------------------------------------------------- */ [data-md-datepicker-picker] { inset: auto; width: 22.5rem; max-width: calc(100vw - 1rem); max-height: calc(100dvh - 1rem); 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: 0.25rem; 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: 0.5rem; height: 4rem; padding-inline: 0.25rem 0.75rem; } [data-md-datepicker-app-bar-actions] { display: flex; align-items: center; gap: 0.25rem; } [data-md-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-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: 3rem; } [data-md-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-md-datepicker-header] { display: flex; flex-direction: column; justify-content: space-between; flex: none; min-height: 7.5rem; 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: 8rem; } [data-md-datepicker-title] { padding: 1rem 0.75rem 0 1.5rem; 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: 0.5rem; padding: 0 0.75rem 0.75rem 1.5rem; } [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: 3.5rem; padding-inline: 0.75rem; color: var(--md-sys-color-on-surface-variant); } [data-md-datepicker-nav][data-md-docked] { height: 4rem; padding-inline: 0.25rem; } [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] { position: relative; display: inline-flex; align-items: center; gap: 0.5rem; height: 2.5rem; padding-inline: 0.75rem 0.5rem; 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; outline: none; isolation: isolate; } [data-md-datepicker-nav][data-md-docked] [data-md-datepicker-menu-button] { padding-inline: 0.5rem 0.25rem; gap: 0.25rem; } [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; } /* ---- State layers shared by days, years, menu buttons and list rows ------------------------ */ [data-md-datepicker-menu-button]::before, [data-md-datepicker-day] > span::before, [data-md-datepicker-year]::before, [data-md-datepicker-option]::before { content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit; background-color: var(--datepicker-layer, currentColor); 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-menu-button]:hover::before, [data-md-datepicker-day]:not([aria-disabled='true'], [data-md-blank]):hover > span::before, [data-md-datepicker-year]:hover::before, [data-md-datepicker-option]:not([aria-disabled='true']):hover::before { opacity: 0.08; } } [data-md-datepicker-menu-button]:focus-visible::before, [data-md-datepicker-menu-button]:active::before, [data-md-datepicker-day]:focus-visible > span::before, [data-md-datepicker-day]:not([aria-disabled='true'], [data-md-blank]):active > span::before, [data-md-datepicker-year]:focus-visible::before, [data-md-datepicker-year]:active::before, [data-md-datepicker-option]:focus-visible::before, [data-md-datepicker-option]:not([aria-disabled='true']):active::before { opacity: 0.1; } [data-md-datepicker-menu-button]:focus-visible, [data-md-datepicker-day]:focus-visible > span, [data-md-datepicker-year]:focus-visible { outline: 3px solid var(--md-sys-color-secondary); outline-offset: 2px; } [data-md-datepicker-option]:focus-visible { outline: 3px solid var(--md-sys-color-secondary); outline-offset: -3px; } /* ---- The days ----------------------------------------------------------------------------- */ [data-md-datepicker-grid] { width: calc(100% - 1.5rem); margin-inline: 0.75rem; border-collapse: collapse; table-layout: fixed; } [data-md-datepicker-grid] th { height: 3rem; 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: 3rem; 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; } [data-md-datepicker-day] > span { position: relative; display: grid; place-items: center; width: 2.5rem; height: 2.5rem; margin: auto; border-radius: var(--md-sys-shape-corner-full); transition: background-color var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast); } [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: 0.25rem; 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) 38%, 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) 38%, 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: 1rem; height: 20.9375rem; padding: 0.5rem 0.75rem; overflow-y: auto; overscroll-behavior: contain; border-bottom: 1px solid var(--md-sys-color-outline-variant); } [data-md-datepicker-year] { --datepicker-layer: var(--md-sys-color-on-surface-variant); position: relative; width: 4.5rem; height: 2.25rem; 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; outline: none; isolation: isolate; scroll-margin-block: 0.5rem; } [data-md-datepicker-year][data-md-current] { --datepicker-layer: var(--md-sys-color-primary); 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'] { --datepicker-layer: var(--md-sys-color-on-primary); 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: 21.5rem; padding-block: 0.5rem; overflow-y: auto; overscroll-behavior: contain; border-top: 1px solid var(--md-sys-color-outline-variant); } [data-md-datepicker-option] { --datepicker-layer: var(--md-sys-color-on-surface); position: relative; display: flex; align-items: center; gap: 1rem; width: 100%; height: 3rem; padding-inline: 1rem; 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; outline: none; isolation: isolate; } [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) 38%, 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: 0.625rem 1.5rem 1rem; } [data-md-datepicker-entry-fields][data-md-range] { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; } /* ---- The month announcement, spoken but not drawn -------------------------------------------- */ /* Same clip as the text classes' `md-visually-hidden`, kept as a hook because a package view writes no class but the interaction classes (plan step 36). */ [data-md-datepicker-month-live] { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border-width: 0; } /* ---- Cancel and OK ---------------------------------------------------------------------------- */ [data-md-datepicker-actions] { display: flex; flex: none; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 0.5rem; padding: 0.25rem 0.375rem 0.5rem 0.75rem; } }