1. Six themes — built for real interfaces
Themes vary by border radius, border weight and shadow personality — not color. Click any chip.
2. With and without flipping arrows
The arrow centers on the trigger and flips to whichever side keeps the popup on screen. Use data-smart-popup-arrow="0" to disable.
3. Triggers built for content UIs
Triggers do not have to be buttons. Use text anchors, info badges, table cells, list items — anything with the smart-popup-trigger class.
Inline text anchors
The new VAT calculation uses the destination country and rolls up automatically in the order summary. Customers in the EU see a reverse-charge note when they enter a valid business ID. Stock numbers are refreshed every 5 minutes and never block checkout.
Question-mark info badges
| Field | Margin | Status | Net |
|---|---|---|---|
| SKU-1042 | 32% | Active | €48.20 |
| SKU-1043 | 41% | Draft | €12.90 |
| SKU-1044 | 27% | Active | €199.00 |
Product card with detail popup
4. Placement preference
Auto picks the side with the most room. Hint with data-smart-popup-placement. The popup re-evaluates on scroll and resize.
5. Scroll & viewport behaviour
Open the popup, then scroll. The popup stays attached to its trigger; once the trigger leaves the viewport, the popup flips and pins to the edge so it remains visible as long as possible.
6. Multiple popups open at once
Opening a second popup does not close the first — useful when comparing two help texts side-by-side, or when a help popup spawns a related help popup. Press Esc to close only the focused popup; click outside any popup to close all of them.
7. AJAX-loaded content
Set data-smart-popup-url instead of data-smart-popup-trigger to fetch the popup body from a URL. A spinner shows while loading, then the response HTML is injected. In Drupal, Drupal.attachBehaviors runs on the fetched markup, so use-ajax links and ajax forms inside the response work without extra wiring.
8. Accessibility
- Triggers get
aria-haspopup="dialog",aria-expandedandaria-controlspointing at the popup id. - Popups render with
role="dialog",aria-modal="false"(because multiple popups can coexist) and anaria-labelderived from the trigger. - Focus moves into the popup on open, is trapped inside while open, and returns to the trigger on close.
- Esc closes the focused popup; Tab / Shift+Tab cycle within it.
- A visually-hidden
aria-live="polite"region announces open / close / loading state to screen readers. - Respects
prefers-reduced-motion.
How to use
Three pieces: a hidden source, a trigger, and the library. Nothing else.
<!-- 1. Hidden source -->
<template id="sp-src-hello" class="smart-popup-source">
<strong>Estimated delivery</strong>
<p>Ships in 1–2 business days from our Berlin warehouse.
<a href="/shipping">Read shipping policy</a>.</p>
</template>
<!-- 2. Trigger (anchor, badge, button, anything) -->
<span class="sp-info smart-popup-trigger smart-popup--theme-bordered"
data-smart-popup-trigger="sp-src-hello"
data-smart-popup-placement="auto"
data-smart-popup-arrow="1"
aria-label="Help"></span>
<!-- 3. Library (load base + only the theme(s) you use) -->
<link rel="stylesheet" href="css/smart_popup.base.css">
<link rel="stylesheet" href="css/smart_popup.theme-bordered.css">
<script src="js/smart_popup.js"></script>
Attributes
data-smart-popup-trigger— id of the hidden source (required, OR use data-smart-popup-url)data-smart-popup-url— fetch popup HTML from this URL (AJAX content)data-smart-popup-placement—auto | top | bottom | left | rightdata-smart-popup-arrow—1(default) or0
CSS files (one base + one file per theme)
css/smart_popup.base.css— required layout, positioning, animations, a11ycss/smart_popup.theme-default.css— clean white, soft shadow, medium radiuscss/smart_popup.theme-soft.css— off-white, generous radius, diffused shadowcss/smart_popup.theme-slate.css— dark opaque, small radius, tight shadowcss/smart_popup.theme-bordered.css— hairline border, minimal shadow, small radiuscss/smart_popup.theme-elevated.css— large radius, deep dramatic shadowcss/smart_popup.theme-sharp.css— hard border, square corners, offset shadow
Closing
Popups close only on: outside click, Esc, or the × button. They never auto-dismiss on hover-out, so users can read links and select text inside.
Drupal 10 / 11 / 12
The Drupal build (js/smart_popup.drupal.js) registers Drupal.behaviors.smartPopup and calls Drupal.attachBehaviors on inserted popup content — for both data-smart-popup-trigger (template-sourced) and data-smart-popup-url (AJAX) content. This means <a class="use-ajax" data-dialog-type="modal"> links inside the popup open as Drupal modal dialogs as expected. The library depends on core/drupal.ajax and core/drupal.dialog.ajax.