Popups and banners — @sonordev/site-kit/website/popups
The popups, banners and toasts you publish in Sonor (Website → Popups & Banners), shown on your site where and when you set them, in the site's own design.
Usage
SiteKitLayout mounts it for you, after the page goes idle:
<SiteKitLayout>{children}</SiteKitLayout> // popups on (default)
<SiteKitLayout popups={false}>{children}</SiteKitLayout> // no popupsOn a site without SiteKitLayout, mount SitePopups the same way:
'use client'
import { SitePopups } from '@sonordev/site-kit/website/popups'
export function Popups() {
return <SitePopups />
}A project with nothing published costs one request and draws nothing. The renderer downloads only when a popup is about to open.
Props
interface SitePopupsProps {
apiUrl?: string // Default: from SiteKitLayout, else https://api.sonor.io
apiKey?: string // Default: from SiteKitLayout
zIndex?: number // The layer popups, banners and toasts sit on. Default: 9999
allowInFrame?: boolean // Show inside a cross-origin frame. Default: false
allowLocalhost?: boolean // Show on localhost. Default: false
debug?: boolean
}Through SiteKitLayout, zIndex comes from chat={{ zIndex }}, so the chat
launcher and popups share one layer.
What shows
Each popup is a set of blocks (heading, formatted text, image, button,
divider) drawn in your site's design: its colors, fonts, radius and buttons.
SiteKitLayout measures the design from the home page at idle and reports it
to Sonor, so the popup builder previews popups the way they'll look. Its
design prop declares any part of it yourself
(design={{ primary: 'var(--brand)', fontHeading: 'Fraunces, serif' }}).
| Placement | What it is |
|---|---|
| Popup | Centred over a dimmed page. One at a time: a second waits until the first closes |
| Banner | A bar along the top or bottom edge |
| Toast | A small card in a corner (slide-in) |
Targeting and triggers
All set in Sonor, no code changes:
- Pages: include and exclude paths, with
*for a prefix (/services/*) - Devices: desktop, mobile, tablet
- Schedule: start and end dates
- Trigger: immediately, after a delay, at a scroll depth, or on exit intent
- Frequency: once, once per session, or every N days. Closing a popup and pressing its button both count as seen.
A popup targeted at one page leaves when the visitor navigates away from it.
Popups wait for forms
A popup or toast that opens on its own first checks whether the visitor is in the middle of a form, and waits while they are. Mid-form means:
- focus is in a form field, or in a card field embedded in a form;
- a form has a field they've filled in and haven't sent yet;
- a site-kit form is sending.
It opens a moment after focus leaves the form, or once the form has been sent and cleared. It never gives up: while the visitor stays mid-form, it keeps waiting. Banners show at once, since they don't take focus or hide the page.
Why it matters: a modal popup takes focus and hides the rest of the page from assistive technology while it's open. Opening one mid-form throws a person out of the field they're typing in, and an AI agent browser filling the form for someone (agents act on the accessibility tree) loses every field it was about to fill.
Accessibility
Popups, toasts and banners are built so people and agent browsers can find them and their controls by name:
- A popup is a modal dialog (
role="dialog",aria-modal="true") named by its heading. It takes focus when it opens, keeps Tab inside, closes on Escape or a click outside it, and puts focus back where it was. - A toast is a named, non-modal dialog, and a banner is a named region. Both close on Escape and leave focus where it is.
- Close buttons are named "Close", whatever they show.
Tracking
Each popup's impression (once half of it is on screen) and button presses are
counted in Sonor, against the same visitor ID (_sk_vid) analytics uses.
Drawing one popup yourself
PopupBlocks is the renderer SitePopups uses, for drawing one popup from
its blocks (a preview, a custom placement):
import { PopupBlocks } from '@sonordev/site-kit/website/popups'
<PopupBlocks
blocks={[{ type: 'heading', text: 'Spring cleanups are booking' }]}
placement="popup"
onClose={() => setOpen(false)}
/>inline renders it in place, without the overlay or focus handling.
Engage (retired)
Engage was retired in Sonor, and its popups with it. Popups made in Engage
Studio (a design_json without blocks) aren't drawn by site-kit 7.2 or later;
make them again in Website → Popups & Banners. @sonordev/site-kit/engage
still builds through 7.x: its EngageWidget draws SitePopups and the
website chat.