Smart Popup

Smooth, smart-positioning popups for admin dashboards and shops. Vanilla JavaScript, no libraries, six opaque themes, flipping arrows, text anchors and question-mark badges as first-class triggers.

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.

Inline help with arrow Inline help, no arrow

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-104232%Active€48.20
SKU-104341%Draft€12.90
SKU-104427%Active€199.00

Product card with detail popup

Studio Lamp — Walnut Details
SKU 9921 · 12 in stock
€129.00

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

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

CSS files (one base + one file per theme)

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.