Popups
How SG-Core fires, scopes, and measures popups.
Popups in SG-Core is the engine-level system that turns each operator-defined popup into a runtime behavior. Where SG-Admin owns the operator-facing module (creating popups, setting trigger rules, picking scope), SG-Core owns the runtime: when triggers evaluate, how scope decides whether to show, where engagement events flow.
What is this for?
Read this page when you need the engine-side definition: how a popup's trigger fires on a page load, how scope rules resolve to a yes/no display decision, what shape the engagement events take, and where they land in Analytics.
Good use cases
- You need to understand why a popup that is enabled isn't firing on a specific page.
- You are scoping an integration that needs to respond to popup engagement events.
- You are checking whether a planned trigger condition is supported.
- You hit a question like "Why did the popup fire twice for the same visitor?"
What NOT to use this for
- Operator how-to (creating popups, setting triggers, configuring scope) — that lives in SG-Admin Popups.
- Per-popup engagement-report reading — that lives in Analytics.
- Per-release shipped change — open the Changelog.
How this connects to other features
- SG-Admin Popups — the operator-facing module that creates and configures popups.
- Pages & Posts — the engine surface popups are scoped against.
- SG-Admin Analytics — where popup engagement events surface as reports.
- Forms — popups frequently host a form; the form pipeline runs alongside the popup engagement pipeline.
Trigger evaluator
Every page load hands the trigger evaluator a list of popups eligible for the current page. The evaluator walks each popup's trigger rule against the runtime context — time on page, scroll depth, exit intent, time of day, visitor return state, prior-engagement state, and the per-popup suppression cooldown. When a rule matches, the evaluator emits a should-show event for that popup.
The evaluator runs in the visitor's browser, not on the server. Server-side scoping decides which popups are eligible for the page; the trigger evaluator decides when one of those eligible popups gets shown. The split keeps server load light (no per-trigger work server-side) and the trigger work close to the runtime that observes scroll, time-on-page, and intent.
A trigger rule at the engine layer carries:
- Rule type — time-on-page, scroll-depth, exit-intent, time-of-day, visitor-return.
- Threshold — the value that has to be met or crossed (e.g. 30 seconds, 50% scroll, between 09:00 and 17:00).
- Suppression cooldown — how long after a dismissal or a successful interaction the popup stays suppressed for the same visitor.
- Visitor-state guard — first-time visitor only, returning visitor only, or both.
Scoping resolver
Before any trigger evaluation runs, the scoping resolver decides which popups are even eligible for the current page. Scope rules at the engine layer can match by URL pattern (exact path, prefix, regex), by post-type (only Pages, only Posts, only Custom Object types of a given kind), by template binding (only pages that use the Landing template), by tag or category (only Posts in the Promotion category), and by combinations of those.
The resolver runs server-side at page-render time and writes the eligible-popup list into the page response. The browser-side trigger evaluator then has its working set without needing a second round-trip.
A scope rule at the engine layer carries:
- Match type — URL pattern / post-type / template binding / tag-or-category / combination.
- Match value — the pattern or identifier to match against.
- Inclusion / exclusion — show on this scope (default) or explicitly never show on this scope (suppress).
When more than one scope rule applies to a popup, the resolver evaluates them in order; the first explicit exclusion wins, otherwise the popup is eligible.
Engagement pipeline
Every meaningful interaction with a popup emits an engagement event: shown, dismissed, primary-action-clicked, secondary-action-clicked, form-submitted (when the popup hosts a form). Each event carries the popup identifier, the page it fired on, the visitor session reference, and a timestamp.
The events flow into the Analytics event-log pipeline — the same pipeline that captures page views, form submissions, and other site-level events. The Analytics module aggregates them into per-popup reports: impressions, dismissal rate, primary-CTR, secondary-CTR, and (when paired with a form) submission rate.
The events do not depend on the popup being part of any campaign; the pipeline runs for every popup configuration. Operator-side, that means engagement reporting works the moment a popup is enabled. Engine-side, that means there is one event-emit path, not one per popup type.
Suppression and frequency
The engine maintains per-visitor, per-popup suppression state. After a dismissal or a primary-action click, the popup goes into a cooldown for the duration the popup configuration specifies. During that window, the trigger evaluator skips the popup even if its trigger rule otherwise matches.
The suppression state is keyed off the visitor's browser identifier (cookie- or local-storage-backed). It is not server-side state; clearing the visitor's browser storage resets the cooldown.
Cap behavior
A popup can also carry a global frequency cap independent of per-visitor suppression — for example "show at most 3 times per visitor over 30 days." When the cap is set, the engine increments a per-visitor counter on each show and stops showing the popup once the counter hits the cap.
Modal overlay system
The runtime that renders a popup once the trigger fires is the modal overlay system. It handles the visual chrome (the backdrop, the dialog frame, the close affordance, the keyboard-focus trap), the animation, and the mount point in the DOM tree.
The same runtime renders every popup variant — modal popups (centered, with backdrop) and overlay popups (positioned, no backdrop). Popup-template content (the body the operator authored in SG-Builder) is injected into the dialog frame at mount time. There is no per-popup runtime; the system handles every popup the same way.
Accessibility behaviors
The modal overlay system traps keyboard focus within the dialog while it is open, restores focus to the originating element on close, supports Esc-to-close, and announces the dialog to assistive tech via the appropriate ARIA roles. These behaviors are baked in; no per-popup configuration toggles them.
Constraints and boundaries
Popups in SG-Core is the engine layer. The operator module is in the admin.
What this engine layer does
- Resolves popup scope server-side at page-render time.
- Evaluates triggers in the browser at runtime.
- Renders popups via the modal overlay system.
- Emits engagement events into the Analytics pipeline.
- Maintains per-visitor suppression and frequency-cap state.
What this engine layer does not do
- Author popup content — that is SG-Builder.
- Define which trigger rules and scope conditions exist as operator options — that is the operator UI surface in the admin.
- Render Analytics reports — that is the Analytics module.
Public boundary
The engine layer is publicly documented at this depth (evaluator split, event names, suppression keying). Lower-level details (event-pipeline payload schema, internal storage keys) are not published.
Examples
Example 1 — Newsletter signup with exit-intent + return cooldown
A site adds a popup with an exit-intent trigger, a 14-day suppression cooldown after dismissal, and a 90-day cap. The popup hosts a newsletter signup form. When a visitor moves the cursor toward the browser chrome, the trigger evaluator fires; the modal overlay renders; the form submission flows through the standard Forms pipeline; the engagement events surface in the Popup engagement report.
Example 2 — Promotion popup scoped to a category
A site adds a popup scoped to "Posts in the Holiday-Promotion category." The scoping resolver includes the popup in the eligible list only on Posts that match the category. On other pages, the trigger evaluator never sees it.
Example 3 — Page-specific announcement with global cap
A site adds a popup scoped to a single Page (URL exact match), with a time-on-page trigger of 5 seconds and a global cap of 1 (show once per visitor, ever). After the visitor sees and either interacts with or dismisses the popup, the cap counter increments and the popup never shows again for that visitor.
Comparison with banners and inline notices
Popup scoping and trigger evaluation are sometimes confused with the simpler banner and inline-notice systems SGEN ships. The differences are worth naming because the right tool for a job depends on the trade-off between visibility and friction.
A banner — the persistent strip across the top or bottom of every page — does not run through the trigger evaluator. It renders unconditionally on every page in scope. It carries no engagement events beyond what the click affordance emits. It is the lowest-friction surface and the lowest-attention surface.
An inline notice — the in-content callout dropped into a Page or Post via SG-Builder — also bypasses the trigger evaluator. It renders wherever the content author placed it. It is the lowest-friction surface for context-bound information.
A popup is the higher-friction, higher-attention end of the same spectrum. The trigger evaluator and popup scoping system exist precisely because popups interrupt the visitor and need to do so deliberately. The engine pays the runtime cost of trigger evaluation in exchange for the operator getting precise control over when interruption happens.
Choosing between them
When the message can wait for the visitor to look at it, a banner or inline notice is enough. When the message has to interrupt the visitor's flow — exit-intent capture, hard-cut promotion, compliance acknowledgement — a popup is the right tool, and the cost of running the trigger evaluator and popup scoping pipeline is justified by the deliberate interruption it enables.
The runtime cost is small in absolute terms — the trigger evaluator is a handful of bytes of browser code observing well-bounded signals — but it is non-zero, and that is why popups carry an operator-side configuration surface that banners and inline notices don't.
Performance characteristics
The popup scoping resolver runs in the same render pass as the rest of the page; eligible-popup-list construction adds one query against the popup configuration table per page render. The query is indexed by site ID and includes a small set of joins for scope-rule expansion; cost scales linearly with the number of enabled popups, not with site traffic.
The trigger evaluator's runtime cost lives in the visitor's browser. The evaluator code is small (~3 KB minified, gzip-compressed in transit) and observes signals with passive event listeners. There is no polling; signals arrive on browser-native events (scroll, mousemove for exit-intent, visibility change). Idle visitors pay essentially zero cost.
The engagement event pipeline batches events; emissions are not synchronous round-trips. A visitor who sees and dismisses three popups during a session generates a handful of buffered events that flush to the Analytics endpoint asynchronously, typically piggybacked on the next page navigation.
Common patterns
A handful of popup configurations recur often enough to be worth naming.
Exit-intent newsletter — exit-intent trigger, ~14-day cooldown, hosts a newsletter form. Often global scope (every page).
Promotional banner — time-on-page trigger (~30 seconds), scoped to a campaign category, with a primary CTA linking to the offer page. Usually a global frequency cap (3 shows over 30 days).
Compliance acknowledgement — first-page-load trigger, scope = global, no cooldown, requires explicit acknowledgement (no dismissal). Used for one-time consent confirmations layered on top of the standalone Tracking Consent banner.
Welcome / onboarding — first-visit trigger, scope = home page only, suppression after first dismissal. Hosts a quick tour or a value-prop summary.
Each pattern is a configuration, not a code path. The engine has no notion of "newsletter popup vs promotional banner"; those are descriptions of how operators combine the same primitives.
A/B variants
A popup can carry multiple body variants. When variants are configured, the engine assigns each visitor to one variant deterministically (a hash of the visitor identifier modulo the variant count) and shows the same variant for the duration of the suppression window.
Engagement events tag the variant in the event payload, so per-variant aggregates land in Analytics without requiring the operator to clone the popup. The same trigger evaluator and scoping resolver apply across variants — only the body content differs.
Variant assignment stability
Variant assignment is stable across page navigations within a session and across sessions for the same visitor identifier. A visitor sees one variant, dismisses it, returns next week, and (if the cooldown has lapsed) sees the same variant. This keeps measurement clean: variant comparisons are within-visitor consistent.
When variants reset
Variant assignment is keyed on the same visitor identifier the suppression system uses. Clearing browser storage resets the assignment along with the suppression state. There is no server-side persistence that survives a clean cookie wipe.
Statistical caveat
Because assignment is hash-based and stable per visitor, the comparison between variants reflects how variants perform on the same population over time, not a randomized sample at one moment. Operators reading variant reports should mind that traffic-mix shifts between variants happen automatically as new visitors arrive — the engine does not rebalance variant assignments retroactively.
Adding or removing variants
Adding a variant after a popup has been live shifts assignment for some fraction of the visitor base — the new variant claims its share on next assignment.
Existing visitor assignments are preserved unless their hash now lands on a different variant given the new count. Removing a variant pushes its assigned visitors onto the remaining variants on next visit.
Authoring the popup body
Popup body content is authored in SG-Builder using the same component set every other surface uses. There is no popup-specific component vocabulary; the modal overlay system mounts whatever the operator built into the dialog frame and lets it render.
That choice keeps the authoring surface familiar — the same headings, buttons, images, and form components used on Pages and Posts work in popups — and keeps the runtime simple. The modal overlay system has no per-component knowledge; it hands the dialog frame a slot and the SG-Builder render pipeline fills it.
Practical consequence: if a feature works in SG-Builder's main render path, it works inside a popup. Custom Code blocks, embedded media, multi-column layouts, conditional content based on visitor state — all of it composes the same way.
Render boundary inside the modal
The popup body is rendered as a standard SG-Builder document tree. The modal overlay system wraps it in the dialog chrome but does not constrain what can appear inside. Operators who want narrower content can use the same Section / Column components they would use to constrain content on a Page; the modal does not impose its own grid.
Constraints that travel from the runtime
Two constraints originate in the modal runtime, not the body author. First, the dialog has a fixed mount point in the DOM tree, so body content cannot rely on parent-page CSS context. Second, the dialog can close at any time (Esc, backdrop click, dismiss button), so body content should not assume the visitor will see it through to a finished state.
Integration with Forms
When a popup hosts a form, the form runs the standard Forms pipeline — same submission handling, same email routing, same draft and submission storage, same anti-spam checks. The popup is the surface; the form is the workload.
The two pipelines emit independent events. The popup engagement pipeline emits its own dismissal / interaction events; the Forms pipeline emits its own submission event. When a visitor submits the form, both events fire — Analytics carries the full picture (popup shown, form rendered inside it, form submitted) without needing the operator to wire anything up.
Form configuration changes (new fields, new routing rules, anti-spam toggles) take effect on the next popup render. There is no per-popup form copy; the popup references the form by identifier and renders whatever the current form definition is.
Integration with Tracking Consent
Popups respect the visitor's tracking consent state. When a visitor has not consented to tracking, popups still render and behave normally, but the engagement event pipeline downgrades the events: the popup-shown / dismissed / clicked events still fire for site-internal counts, while visitor-identifying fields (cookie ID, return-visitor flag) are stripped before the event leaves the browser.
This is the same downgrade contract every event-emitting feature in SGEN follows. Operators do not need to configure popups separately for consent compliance; the consent state propagates into the event payload at emit time.
For popups whose entire purpose is consent acknowledgement (the compliance-acknowledgement pattern), the popup is in scope before consent is given by definition. The engine handles this through the pre-consent eligibility flag — popups marked as consent-related show even when no consent state has been recorded yet.
Edge behaviors
A handful of behaviors are worth surfacing because they answer recurring "what happens if..." questions.
What happens if two popups are eligible on the same page and both triggers fire? The engine shows them in registration order. The second waits until the first is dismissed, unless its trigger has lapsed by then. Visitors don't see two popups stacked.
What happens if a popup's scope changes mid-flight? The scoping resolver picks up the new rule on the next page render. Pages already loaded before the change continue with the prior eligibility. There is no live re-evaluation of in-flight pages.
What happens if a visitor clears cookies between sessions? The suppression and frequency-cap state reset. The popup behaves as if the visitor is new. This is the same behavior every visitor-keyed feature in SGEN exhibits.
What happens if Analytics is disabled or the event pipeline is unreachable? Engagement events are buffered in the browser and retried on the next page load. Popups still render and behave normally; only the reporting catches up later.
Documentation guidance
This page is the engine-side reference. For operator how-to (creating, configuring, managing popups) open SG-Admin Popups. For per-popup engagement reporting open Analytics.
Reading order
Read this page when you need to reason about how a popup behaves once configured — when it fires, where it shows, what it emits, when it is suppressed. Pair with the admin Popups module for the operator-side flow that creates and configures popups.
Related reading
- SG-Admin Popups — operator module.
- Pages & Posts — the engine surface popups are scoped against.
- SG-Admin Analytics — where popup engagement events surface as reports.
- Forms — popup-hosted form submissions flow through the Forms pipeline alongside engagement events.
Vocabulary cross-reference
- Trigger evaluator — the browser-side runtime that watches scroll, time, intent, and other signals and decides when a popup should show.
- Scoping resolver — the server-side step that decides which popups are eligible for the current page, before the trigger evaluator runs.
- Engagement event — one row emitted into the Analytics pipeline whenever a meaningful interaction happens (shown / dismissed / clicked / submitted).
- Suppression cooldown — per-visitor, per-popup window during which the popup will not show even if the trigger matches.
- Frequency cap — per-visitor maximum show count over a time window, independent of the suppression cooldown.
- Modal overlay system — the shared runtime that renders every popup variant, handles accessibility, and manages the dialog mount.
Maintenance discipline
Update this Reference when:
- The trigger evaluator gains a new trigger type (e.g. a new intent signal, a new visitor-state guard).
- The scoping resolver learns a new match type (e.g. a new scope dimension beyond URL/post-type/template/tag).
- The engagement pipeline changes its event surface (new event name, new payload field).
- The suppression keying changes (e.g. moves from cookie-keyed to logged-in-account-keyed for authenticated visitors).
- The modal overlay system changes accessibility or rendering semantics in a way that affects integrators.
Operator-side changes (new SG-Admin UI, new template-builder affordances for popup body, new report views) belong in SG-Admin Popups, not here.
Scope
This Reference covers the platform-level shape of popups: what the surface is responsible for, how it relates to neighboring surfaces, and the structural boundaries that hold across releases. Operator how-to and per-release change land on the linked operator-facing or changelog surfaces, not here.
Where to find it
Open your SG-Admin and navigate via the sidebar group that owns this surface. For platform-level reference (this page), the entry point is the SGEN documentation index at docs.sgen.com. For the operator-facing configuration screen, the entry point is the corresponding SG-Admin module page linked in Related features above.
