Exit intent — spec

Table of Contents

1. Purpose

The exit-intent overlay is the "before you leave" surface: a blended-grid unit shown when the reader appears to be leaving. Modelled on Fortune's implementation of the pattern (Bounce Exchange (Wunderkind), n.d.), with three deliberate divergences — per-card provenance, holdout suppression, and no back-button traps — that are captured as R28..R33 in this spec.

The overlay fires on desktop when the cursor moves within 20 px of the viewport top, and on mobile when upward scroll velocity crosses 1.5 px/ms. Both proxies are approximations; the true "exit" event is unobservable in the browser.

ORACLE: src/wal_sh/adtech/exit_intent/browser.cljs (browser only — the module is DOM-native and has no separate core.cljc).

2. Requirements

R28
Never trap the back button. The overlay is dismissible; back-button navigation is never intercepted. R28 is permanent, not pending. Rationale: the pattern of back-button hijacking is the single most-blocked exit-intent behaviour by browser policy and ad blockers.
R29
Eligibility gate — engagedTime ≥ 15 s AND scrollDepth ≥ 50 %. A bounce never sees the overlay. Reads window._srIntent.eligible (see intent-signals).
R30
Fires at most once per session; dismissal suppresses for 7 days via localStorage._sr_ei_dismissed. The overlay is not a nag surface.
R31
Suppressed in the holdout arm. Reads window._srAttribution.Audit.arm. This is what makes the overlay's effect measurable rather than asserted — the incrementality delta between exposed and holdout is a real lift or a real drop.
R32
Every card carries its own provenance label (organic or sponsored). The source pattern hides disclosure behind a container-level ad-choices icon; ours prints on the card.
R33
Dismissible via Escape, backdrop click, close button; focus returns to the previously-focused element on close.

3. Contract signature

Browser (browser.cljs):

window._srExitIntent = {
  install:   (fn [] -> nil)                       ; wire listeners
  fire:      (fn [] -> nil)                       ; force-fire (test/debug)
  dismiss:   (fn [] -> nil)                       ; test hook
  eligible?: (fn [] -> bool)                      ; composite gate result
}

Config (private, not surfaced):

{:top-threshold-px    20
 :dismiss-days        7
 :card-count          6
 :organic-ratio       0.5                         ; 3 organic + 3 sponsored per grid
 :scroll-vel-thresh   1.5                         ; px/ms upward — mobile proxy
 :dismiss-storage-key "_sr_ei_dismissed"}

Gate composition:

gate? = armed?
     ∧ ¬shown?
     ∧ ¬dismissed?          ;; localStorage window
     ∧ ¬holdout?             ;; R31
     ∧ intent-eligible?      ;; R29

Grid card sources:

  • Organic: document.querySelectorAll("a[href^'research']")=, take card-count · organic-ratio, wrap with :kind "organic" :source "wal.sh".
  • Sponsored: fetches from window._sponsoredResearch (Format. sample-mixed); each card carries :kind "sponsored" :source the advertiser name.

On fire, appends an exit_intent touchpoint to the ledger (channel "exit_intent", campaign "before-you-leave", claimant "wal.sh", provenance "observed").

4. Triggers

Desktop: mousemove listener; fires when event.clientY < 20 and we haven't already shown. Debounced to a single fire per session.

Mobile: scroll listener; tracks (y, t) at each event, computes upward velocity (last-y - y) / (t - last-t) in px/ms; fires when vel > 1.5 and y > last-y. Approximation only — no browser event matches "user about to leave" on mobile.

Both are pure-approximation triggers. The actual exit event is unobservable, so we substitute proxies known to correlate.

5. Related literature

  • Bounce Exchange (Wunderkind) is the commercial-scale antecedent (Bounce Exchange (Wunderkind), n.d.); the "before you leave" pattern is their signature surface.
  • Zeng, Kohno & Roesner on deceptive ads (Zeng, Kohno, and Roesner 2020) documents the failure mode this spec's divergences (R28, R32) are designed to avoid — deceptive disclosure and back-button hijacking.
  • Ali et al. on delivery discrimination (Ali et al. 2019) motivates R32: per-card provenance is the only way an outside audit can distinguish an owned recommendation from a paid one.
  • ANA 2023 MFA study (Association of National Advertisers 2023) on the transparency crisis in ad delivery is the context for the holdout suppression (R31) — the only reliable way to detect lift is a suppression arm.
  • Intent-signal gating: Chartbeat's engagedTime model (Chartbeat, n.d.) is the antecedent for R29.
  • IntersectionObserver + focus-restoration: MDN (MDN Web Docs, n.d.).

6. Cross-references

7. Open questions

  • Frequency capping across visits. Current window is 7 days from dismissal. A returning-visitor exit-intent (i.e. dismissed on visit 1, re-shown on visit 5) is not supported and probably shouldn't be — nag pressure is what makes these surfaces universally blocked. But a per-visitor cadence is worth measuring.
  • Mobile trigger. The upward-scroll-velocity proxy fires false- positive on any fast scroll-up-to-reread. A better proxy would fold in page.dwell and only fire once dwell exceeds a threshold; captured but not implemented.
Ali, Muhammad, Piotr Sapiezynski, Miranda Bogen, Aleksandra Korolova, Alan Mislove, and Aaron Rieke. 2019. “Discrimination through Optimization: How Facebook’s Ad Delivery Can Lead to Biased Outcomes.” In Proceedings of the Acm on Human-Computer Interaction. Vol. 3. CSCW. https://doi.org/10.1145/3359301.
Association of National Advertisers. 2023. “Programmatic Media Supply Chain Transparency Study.” ANA. https://www.ana.net/miccontent/show/id/rr-2023-06-ana-programmatic-transparency-study.
Bounce Exchange (Wunderkind). n.d. “Exit-Intent Technology.” https://www.wunderkind.co/.
Chartbeat. n.d. “Engaged Time: The Metric That Matters.” Chartbeat Docs. https://support.chartbeat.com/docs/getting-started/engaged-time.
MDN Web Docs. n.d. “Intersection Observer Api.” https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API.
Zeng, Eric, Tadayoshi Kohno, and Franziska Roesner. 2020. “Bad News: Clickbait and Deceptive Ads on News and Misinformation Websites.” In Workshop on Technology and Consumer Protection (Conpro). https://badads.cs.washington.edu/.