Compass Web SDK: Catch Up Card
The Catch Up card — <arc-compass-catchup-card>, one of the SDK-rendered
Experiences shipped in @arcxp/compass-sdk — is a “while you were away” deck for
returning readers. It recaps what has changed across the storylines a reader
follows since their last visit, as a collapsed card that expands into a
full-screen, Stories-style takeover.
Like every SDK-rendered Experience, it is a custom element you drop into your markup. The SDK fetches its content, renders it inside a shadow root, and gates the whole thing on reader consent — you place one tag and style it with CSS custom properties. There is no data to fetch and no card component to build.
What the card is
The card has two surfaces, both built from a single catch-up fetch:
- The collapsed card — a compact deck with a featured story that autoplays through the reader’s moved storylines, a list of everything in this catch-up, and a “Catch me up” control. This is the entry point.
- The takeover — a full-screen reader opened from “Catch me up.” It pages through the stories one screen at a time; the reader advances until they reach an “I’m caught up” confirmation and close back to your page.
The optional recs-surface-id attribute is the exception to “single fetch”: it
adds a “You might also be interested in” list to the final caught-up screen,
served by a separate recommendations request that is issued only if the reader
reaches the end of the takeover. Budget for that extra call when you use it — see
Attributes.
The card is personalized per reader and decides its own content: which storylines moved, in what order, and what to feature. You do not pass it a query, a section, or a count — it reads the consenting reader’s identity (established by the SDK) and requests their catch-up on mount.
Content in V1
The card presents real editorial content — the real headline, the real promo image, and story text extracted from the underlying content — for each item it shows. It is a recap of what your newsroom published, laid out for a returning reader.
Minimal render
With the SDK initialized and consent resolved (see the Integration Guide), the card is a single tag:
<arc-compass-catchup-card></arc-compass-catchup-card>That is the entire integration. On mount, the element requests the current reader’s catch-up and renders the collapsed card. If the reader has nothing to catch up on — or has not consented — it renders nothing and collapses to zero height (see Lifecycle and reserving space).
Place it where a returning reader will meet it: the top of the homepage, a section front, or a personalized landing surface.
Attributes
Both attributes are optional and are read once, when the element first mounts —
changing them afterward has no effect. The card takes no other inputs: there is
no surface-id, count, or filter attribute on this Experience, because it does
not take a query.
| Attribute | Values | Effect |
|---|---|---|
recs-surface-id | any string | Enables the “You might also be interested in” list on the final caught-up screen, fed by a separate recommendations request against this surface. Omit it and that list simply does not render; the rest of the card is unaffected. |
open-story | any string | Opens the takeover directly on a specific story — a storyline key or the content item’s ID. Use it to restore a reader’s place: the takeover’s open state is not part of the URL, so a reader who follows a story out and returns lands on a freshly collapsed card unless you re-open it. A value that matches no story in the current catch-up is ignored, leaving the collapsed card. |
<arc-compass-catchup-card recs-surface-id="catchup-suggestions"></arc-compass-catchup-card>Theming
The card renders inside a shadow root: your page’s CSS cannot reach its
internals, and its internals are not part of the API. You theme it through a set
of --arc-compass-* CSS custom properties, which cascade through the shadow
boundary — set them on the tag, a wrapping selector, or :root.
| Property | Default | What it controls |
|---|---|---|
--arc-compass-surface | Canvas (system) | Background where the card owns the surface — the full-screen takeover. Must be opaque. |
--arc-compass-on-surface | currentColor | Ink on the surface: text, plus the neutral ramp (muted text, hairlines, subtle fills) derived from it |
--arc-compass-accent | currentColor | Primary accent — links, active states, and filled controls (“Catch me up,” “I’m caught up”) |
--arc-compass-on-accent | #fff | Foreground on an accent fill (button text/icon). Set it when your accent is light |
--arc-compass-accent-secondary | currentColor | Supporting accent |
--arc-compass-font-family | inherited page font | Primary typeface — headlines and running text |
--arc-compass-font-family-secondary | inherited page font | Secondary typeface — kickers, badges, labels, and meta |
Set the surface/on-surface pair and the accent/on-accent pair and you are
done: every neutral in the card is derived from those colours, so the component
stays coherent on a light or dark page.
arc-compass-catchup-card { --arc-compass-font-family: Georgia, serif; --arc-compass-font-family-secondary: "Helvetica Neue", Arial, sans-serif; --arc-compass-accent: #b00020;}The SDK never ships or @font-faces a typeface — load your fonts as you already
do and point the tokens at them. Set only --arc-compass-font-family and the
card uses that one face throughout.
Dark themes
The card’s colours come from your tokens, not the reader’s OS. To theme a dark
edition, give it a dark surface, a light on-surface, and an on-accent that
reads on your accent — that pair defines the background, the text, and every
derived neutral:
.night-edition arc-compass-catchup-card { --arc-compass-surface: #14161c; /* opaque page background */ --arc-compass-on-surface: #eef1f6; /* light ink */ --arc-compass-accent: #f2b705; /* amber */ --arc-compass-on-accent: #14161c; /* dark text on amber, not white */ color-scheme: dark; /* matches native chrome, e.g. the takeover scrollbar */}You do not need prefers-color-scheme or system colours to make the card’s
UI dark — everything derives from the two colour pairs above. color-scheme
only governs native browser chrome such as the takeover’s scrollbar.
No ::part() hooks
Unlike the recommendations Experiences, the Catch Up card exposes no
::part() hooks. Its themeable surface is the --arc-compass-* custom
properties above and nothing else. The markup between them, class names, and any
internal custom properties (anything prefixed --_) are internal and may change
in any release. If the tokens cannot express a change you need, use the recipe
integration rather than reaching into the shadow root.
Analytics and consent
Consent gating
The card is gated on the same single consent decision as the rest of the SDK. Until the reader has consented, nothing renders and no events are sent — the card is silent, not an error. See Compass Web SDK: Consent & Identity.
Events
Once consented, the card emits three analytics events through the SDK’s normal event pipeline:
| Event | When it fires |
|---|---|
catch_up_shown | The card is shown to the reader (a qualified impression, or when they open the takeover). |
catch_up_click | The reader clicks through to one of the stories in the catch-up. |
catch_up_dismiss | The reader reaches the “I’m caught up” end state and finishes the takeover. An early close (the ✕, Escape, or navigating away) does not fire it — despite the name, there is no event for an early dismissal. |
Each event carries an attributionContext — the card’s exposureId, plus the
moved segment’s attributionId on catch_up_shown and catch_up_click — tagged
source: "fy-catchup". That pairing lets a catch_up_click be confirmed against
its catch_up_shown (a Catch-Up CTR loop), and one catch_up_shown fires per
material segment rather than one per card. The SDK emits all of this itself:
you do not tag anything, populate attributionContext, or call track() for the
card.
Lifecycle and reserving space
The card never shows a spinner, a skeleton, or an error message. It renders its
content or it renders nothing, collapsing to zero height. That means there is no
visual signal for your code to read — the SDK’s lifecycle events are that
signal. They bubble, so you can listen on a container and read event.target:
| Event | Meaning |
|---|---|
arc-compass:rendered | The card is in the DOM. |
arc-compass:empty | Nothing to catch up on for this reader — a successful answer, not a failure. |
arc-compass:error | Something went wrong; the element is collapsed. |
arc-compass:catchup-done | The reader finished the takeover (“I’m caught up”). May fire later, or never. |
Exactly one of rendered / empty / error fires per mount once the reader
has consented — the card renders behind the consent gate, so until consent
resolves to granted (and never, if it is denied) it stays silent and dispatches
none of the three. catchup-done is separate: it describes a reader finishing,
fires later than the terminal event, and may not fire at all (a reader who never
opens the takeover never completes it). Like the others, these events carry no
detail.
If the card sits above the fold, reserve its space yourself and release it on the terminal event so an empty or failed response leaves no gap:
<div class="catchup-slot"> <arc-compass-catchup-card recs-surface-id="catchup-suggestions"></arc-compass-catchup-card></div>.catchup-slot { min-height: 360px;}.catchup-slot.settled { min-height: 0;}document.addEventListener("arc-compass:empty", release);document.addEventListener("arc-compass:error", release);function release(event) { event.target.closest(".catchup-slot")?.classList.add("settled");}Release on empty and error only — on rendered the content is already
filling the slot.
A realistic integration
Putting it together: a themed card, above-the-fold space reserved and released on the terminal events and on the two non-granted consent outcomes (so a reader who denies or ignores consent is not left with a permanent gap), a suggestions surface on the caught-up screen, and a resume hook so a reader who follows a story out and comes back re-opens where they were.
<!-- The card is created in the script below so `open-story` is set *before* the element is inserted — the attribute is read once when the card mounts and is not observed afterward. If you don't need resume, drop the script and place <arc-compass-catchup-card recs-surface-id="catchup-suggestions"> here. --><div class="catchup-slot"></div>
<style> .catchup-slot { min-height: 360px; } .catchup-slot.settled { min-height: 0; } arc-compass-catchup-card { --arc-compass-font-family: "Publico", Georgia, serif; --arc-compass-font-family-secondary: "Franklin", Arial, sans-serif; --arc-compass-accent: #b00020; --arc-compass-on-accent: #ffffff; /* Set the surface pair so the full-screen takeover matches your page. */ --arc-compass-surface: #ffffff; --arc-compass-on-surface: #1a1a1a; }</style>
<script> document.addEventListener("arc-compass:empty", release); document.addEventListener("arc-compass:error", release); function release(event) { event.target.closest(".catchup-slot")?.classList.add("settled"); }
// Those events never fire when consent is denied or never answered, so also // collapse the reserved slot in both of those cases (see the consent caution // above). The SDK has no consent-denied event — define a handler and wire it // to your CMP's denial callback (the exact API differs per CMP). function collapseReservedSlots() { document.querySelectorAll(".catchup-slot").forEach((s) => s.classList.add("settled")); } // yourCmp.onDenied(collapseReservedSlots); // ← wire this up to your consent manager // Never answered: after a grace period, collapse whatever is left — but only // while consent is still *unresolved*. A granted decision (even one whose card // is still loading) or a denial is already handled above, so bail if the reader // has since answered; otherwise you risk yanking a slot a slow-but-granted // render is about to fill. setTimeout(() => { if (yourCmp.consentAnswered) return; // ← your CMP's "reader has responded" flag document .querySelectorAll(".catchup-slot:not(.settled)") .forEach((s) => s.classList.add("settled")); }, 10000);
// Optional: restore the reader's place after they return from a story. // `open-story` is read once, when the card mounts (its connectedCallback — the // moment it is inserted into the DOM) and is not observed afterward, so setting // it on an element already in the page has no effect. The only requirement is // that it be present *before* the element is appended; it does not matter // whether ArcCompass.init() has run yet. Creating the element, setting the // attribute, then appending guarantees the value is there at mount. // // The read side needs a value to read, so persist it elsewhere in your app: // when the reader opens a story from the catch-up (your own routing knows which // one), stash its id under this key, and clear it once they are caught up — // sessionStorage.setItem("catchup:open-story", storyId); // when a story opens // sessionStorage.removeItem("catchup:open-story"); // when caught up const resumeKey = sessionStorage.getItem("catchup:open-story"); const card = document.createElement("arc-compass-catchup-card"); card.setAttribute("recs-surface-id", "catchup-suggestions"); if (resumeKey) card.setAttribute("open-story", resumeKey); document.querySelector(".catchup-slot")?.append(card);
// Clear the resume key once the reader finishes the takeover, so the next visit // opens a fresh collapsed card instead of re-opening the story they already // caught up on. `arc-compass:catchup-done` fires on "I'm caught up" (see // Lifecycle and reserving space); it bubbles, so listening on the card works. card.addEventListener( "arc-compass:catchup-done", () => sessionStorage.removeItem("catchup:open-story"), { once: true }, );</script>MVP limitations
- Single-site. The card serves one site’s catch-up per placement.
- Enablement is provisioning-driven. Catch Up is turned on for your site by
Arc during provisioning — there is no self-serve toggle. Contact your Arc XP
representative to enable it. Once enabled, the card begins returning content for
qualifying readers; before then it renders nothing (an
arc-compass:empty).
See also
- Compass Web SDK: Integration Guide — install, initialize, consent, identity, and meta tags (required setup).
- Compass Web SDK: Consent & Identity — the consent gate and reader identity.
- Compass Web SDK: Events & Reliability — how the SDK batches and delivers events.