Modal positioning, viewport-tier widths, and auto-engage on small screens
Floating mode anchors the calendar popover to its input. For multi-month layouts (visible-months-count="3", 2ร3 grid, etc.), the calendar can be wider than the viewport itself โ users get a horizontal scrollbar, the picker overflows the screen edge, and on mobile it becomes unusable.
Modal mode renders the calendar as a centered overlay with a backdrop scrim instead. It works at any viewport width, scales the configured layout responsively, and locks body scroll while open.
positioning-mode="modal"positioning_mode="modal" ยท open via el.isOpen = true
Set the attribute and the picker opens as a centered overlay. Closes via Escape, backdrop click, Apply (range mode), or after a complete selection.
<web-daterangepicker
selection-mode="range"
positioning-mode="modal"></web-daterangepicker>
// Open programmatically
document.querySelector('#picker').isOpen = true;
The modal's outer width adapts to viewport size, picking from four CSS variables. Breakpoints align with the rest of the codebase (480 / 768 / 1200, same as base.css and the Responsive Sizing demo's --drp-rem scaling).
| Tier | Viewport | Default | Variable |
|---|---|---|---|
| xs | โค 480px | calc(100vw โ 2 ร gap) | --drp-modal-width-xs |
| sm | 481โ768px | calc(100vw โ 2 ร gap) | --drp-modal-width-sm |
| md | 769โ1200px | 900px | --drp-modal-width-md |
| lg | โฅ 1201px | 1100px | --drp-modal-width-lg |
Override per-instance via the host element. The breakpoints themselves are hardcoded in modal.css because CSS media queries cannot read CSS variables โ only the widths inside each rule are variable-driven.
/* Custom widths per tier */
web-daterangepicker {
--drp-modal-width-md: 75vw;
--drp-modal-width-lg: 1300px;
}
/* Other modal hooks */
:root {
--drp-modal-gap: 16px; /* viewport-edge gap */
--drp-modal-backdrop-bg: rgba(0, 0, 0, 0.45); /* scrim color */
--drp-modal-transition: 150ms ease-out; /* fade timing */
--drp-z-index-modal: 9800;
--drp-z-index-modal-backdrop: 9700;
}
Beyond just sizing the outer modal, the inner multi-month layout scales based on the modal's actual width via CSS container queries. The visible month count and grid columns adapt without any JS.
| Modal width | Flex layout (single row) | Grid layout |
|---|---|---|
| โค 600px | 1 month visible (siblings hidden) | 1 column (siblings hidden) |
| 601โ900px | 2 months visible | 2 columns, all months visible (wraps to more rows) |
| 901โ1200px | 3 months visible | up to 3 columns (capped from configured) |
| > 1200px | Configured count | Configured grid as-is |
Hidden columns (when present) still update in lockstep through the existing collision-resolve navigation logic โ range selection across "more months than are visible" continues to work; the user just navigates time linearly with the visible month's prev/next.
mobile_modal_breakpoint="768px" ยท mobile_modal_min_height="500px"
For pickers that should be floating on desktop but modal on phones/tablets, set one or both of these attributes on a positioning-mode="floating" picker:
| Attribute | Triggers modal when |
|---|---|
mobile-modal-breakpoint="640px" | viewport width โค value |
mobile-modal-min-height="500px" | viewport height โค value |
If both are set, they OR โ modal engages when either matches, switches back to floating only when both are clear. Either attribute alone works on its own.
Floating below 768px wide OR 500px tall โ switches to modal. Resize horizontally and vertically.
<web-daterangepicker
selection-mode="range"
positioning-mode="floating"
mobile-modal-breakpoint="768px"
mobile-modal-min-height="500px"
visible-months-count="2"></web-daterangepicker>
matchMedia listener flips the positioning-mode attribute between the configured value (floating) and modal as the viewport crosses the threshold. The reactive attribute path triggers the picker's destroy + re-init cycle, but selection survives because the input value persists across the rebuild and the fresh init re-parses it.
auto-close="selection", the default).document.body.style.overflow.blur()-ed when the modal opens. This suppresses the mobile soft keyboard, which would otherwise overlap the modal. Focus is restored to the input on close.shouldCloseOnScroll is a no-op in modal mode โ you're meant to scroll inside the modal, not have it close on you.(pick a date anywhere on this page)