โ† Back to Examples

๐Ÿ“ฑ Responsive Behavior

Modal positioning, viewport-tier widths, and auto-engage on small screens

1. The Problem with Floating Mode 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.

2. Opting In: 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;

3. Modal Width Tiers

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).

TierViewportDefaultVariable
xsโ‰ค 480pxcalc(100vw โˆ’ 2 ร— gap)--drp-modal-width-xs
sm481โ€“768pxcalc(100vw โˆ’ 2 ร— gap)--drp-modal-width-sm
md769โ€“1200px900px--drp-modal-width-md
lgโ‰ฅ 1201px1100px--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;
}

4. Progressive Inner-Content Layout

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 widthFlex layout (single row)Grid layout
โ‰ค 600px1 month visible (siblings hidden)1 column (siblings hidden)
601โ€“900px2 months visible2 columns, all months visible (wraps to more rows)
901โ€“1200px3 months visibleup to 3 columns (capped from configured)
> 1200pxConfigured countConfigured grid as-is
Why grid keeps all months visible: a 2ร—3 configuration is a deliberate layout choice โ€” collapsing it to "show fewer months" would surprise the user. Instead, narrow grid modals just stack the same months into more rows (2ร—3 โ†’ 3ร—2 at 601โ€“900px, etc.). Only at the very narrow โ‰ค600px tier do we hide siblings, since 6 months stacked vertically would be unusably tall on a phone.

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.

5. Auto-Engage on Small Viewports

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:

AttributeTriggers 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>
How it works internally: a 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.

6. Modal Behavior Details

  • Closes on: Escape, backdrop click, Apply button (range mode), or completing a selection (when auto-close="selection", the default).
  • Body scroll is locked while the modal is open. Reference-counted so multiple modal pickers don't fight over document.body.style.overflow.
  • Input is 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.
  • Sticky regions inside the modal: per-month headers stay pinned to the top of the scrolling months area, the action bar (Today/Clear/Apply) stays pinned to the bottom. Days scroll between them; the action bar is always reachable.
  • shouldCloseOnScroll is a no-op in modal mode โ€” you're meant to scroll inside the modal, not have it close on you.
๐Ÿ”Œ Server round-trip
0
The LiveView server saw 0 event(s) from the wrapper.
(pick a date anywhere on this page)