Design reference

Pop-ups that sort people, not bug them.

The popup system now lives as Astro components with prop variables and named slots, in src/components/popups/. The examples below are rendered through those components, so the reference page shows the same API that can be used across the Lead Caravan site.

  1. 01 Full-screen audit overlay PopupFrame + PopupDialog variant=fullscreen
  2. 02 Qualified lead modal PopupFrame + PopupDialog formAction
  3. 03 Side panel prompt PopupPanel
  4. 04 Toast stack PopupToastStack + PopupToast
  5. 05 Cookie consent banner PopupDialog class=popup-consent
  6. 06 Announcement banner PopupBanner placement=top
  7. 07 Sticky CTA banner PopupBanner placement=bottom

Component API

PopupFrame

Variables: id, class, hidden, dialog, modal, labelledBy, describedBy, ariaLabel, overlay

Slots: default

PopupDialog

Variables: id, variant, as, class, title, titleClass, kicker, description, closeLabel, dismissible, hidden, formAction, formMethod, inert

Slots: default, fields, actions, consent, support

PopupPanel

Variables: id, class, title, kicker, description, closeLabel, hidden, inert

Slots: default, actions

PopupToastStack / PopupToast

Variables: id, class, hidden, live, atomic, role, title, message, tone, closeLabel, ariaLabel, alert, inert

Slots: toast default content

PopupBanner

Variables: id, class, placement, hidden, closeLabel, role, ariaLabel, inert

Slots: default, actions

Live demo

The one sample on this page that really works.

Every pattern below is a static reference: its close button, decline link, and consent choices look right but do nothing, because a dead control is worse than no control. This one is wired. Open it and tab through it — focus moves into the dialog, Tab and Shift+Tab stay trapped inside it, Escape and a backdrop click dismiss it, and focus returns to the button below when it closes.

Already shipped

Two of these patterns are live today, both config-gated.

ChatWidget.astro is env-gated by LEADCARAVAN_CHAT_WIDGET_ENABLED and opens a bottom-right dialog panel when enabled. The wired cookie consent banner above the fold is the real version of the cookie specimen further up this page: ConsentBanner.astro renders through the same PopupDialog component and appears only when BaseLayout detects a configured ad-pixel ID, staying hidden once a choice is stored via window.__setConsent. The side panel and toast patterns above reserve the same corner spacing so chat can coexist with the rest of the system instead of stacking over it.