# Compass Web SDK: Catch Up Card

Source: https://dev.arcxp.com/content-recommendations/sdk/catch-up-card/

Render and configure the arc-compass-catchup-card Experience from the Compass Web SDK: a 'while you were away' deck that recaps what has changed across a returning reader's followed storylines — including its attributes, theming tokens, analytics events, and consent behavior.

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.

> **Note:**
> 
> This page covers the Catch Up card specifically. Installing the SDK, initializing `ArcCompass`, resolving consent, and setting reader identity and the item/site meta tags are shared setup, documented once on the canonical [Compass Web SDK: Integration Guide](https://dev.arcxp.com/content-recommendations/sdk/integration-guide/). Complete that integration first — the card renders nothing until the SDK is initialized and consent is granted.

## 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 at a time; the reader advances until they reach an “I’m caught up” confirmation and close back to your page.

The one exception to “single fetch” is the “You might also be interested in” list on the final _caught-up_ view. It is served by a **separate**recommendations request, issued only if the reader reaches the end of the takeover, and you configure it with the same attributes as the recommendations Experiences — see [Attributes](#attributes).

The card is **personalized per reader** and decides its own catch-up content: which storylines moved, in what order, and what to feature. You do not pass it a query or a section — 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:

```html
<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](#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

Every attribute is optional and is read **once, when the element first mounts** — changing it afterward has no effect. None of them changes the catch-up content itself, which the card decides per reader.

### The card

| Attribute  | Values     | Effect                                                                                                                                                                                                                                                                                                                                                                                   |
| ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 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. |
| autoplay   | off        | Stops the collapsed card’s featured story from advancing by itself: it stays on the first story until the reader picks another. Only off (in any letter case) turns it off; any other value, or none, keeps autoplay, which starts paused for readers who prefer reduced motion.                                                                                                         |

### The “You might also be interested in” list

The list on the caught-up view is a separate recommendations request, and you configure it with the same attributes and values as the [recommendations Experiences](https://dev.arcxp.com/content-recommendations/sdk/recommendations-components/#attributes). The request is issued whether or not you set any of them, and the list renders only when it returns items.

| Attribute     | Effect on the list                                                                                                                                                                                   |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| surface-id    | Forwarded with the list’s recommendations request, so Confirmed-CTR reporting can tell this surface apart. The older name recs-surface-id still works; if you set both, a non-empty surface-id wins. |
| count         | How many items to request and show, capped to 50. Omit it to request and show four.                                                                                                                  |
| heading       | Replaces the list’s heading. Omit it or leave it empty to keep “You might also be interested in,” translated into the reader’s language.                                                             |
| dates         | relative (default) shows each item’s relative publish date; off hides it.                                                                                                                            |
| authors       | on (default) shows each item’s author; off hides it.                                                                                                                                                 |
| premium-label | A word shown beside the lock on a premium item, for example Subscriber. Omit it and the badge is the lock alone.                                                                                     |

```html
<arc-compass-catchup-card
  surface-id="catchup-suggestions"
  count="3"
></arc-compass-catchup-card>
```

## Theming

The card renders inside a **shadow root** and is themed through the `--arc-compass-*` CSS custom properties shared by every SDK-rendered Experience. The token table, a copy-paste brand block, and the light and dark recipe live once, in the [Compass Web SDK: Theming Reference](https://dev.arcxp.com/content-recommendations/sdk/theming/).

The Catch Up Card is the only Experience that paints a surface of its own — the full-screen takeover — so it is the one that reads the `surface`/`on-surface`pair. Set that pair and the `accent`/`on-accent` pair and you are done: every neutral in the card derives from them, so it stays coherent on a light _or_ dark page.

```css
arc-compass-catchup-card {
  --arc-compass-font-family: Georgia, serif;
  --arc-compass-font-family-secondary: "Helvetica Neue", Arial, sans-serif;
  --arc-compass-accent: #b00020;
}
```

> **Caution:**
> 
> The takeover covers the viewport, so `--arc-compass-surface` **must be opaque** — a translucent value lets page pixels bleed through. If your page background is not the default light, set the `surface`/`on-surface` pair explicitly (the takeover cannot read your page’s own background). The inline collapsed card is transparent and inherits your page, so it needs no configuration for this.

### Styling internals with `::part()`

When a token cannot express the change you need, restyle a named element with `::part()`. The card exposes these parts, covering the collapsed card and the takeover it opens:

| Part          | Element                                                                                 |
| ------------- | --------------------------------------------------------------------------------------- |
| card          | The card’s outer <section> — collapsed, and again after the reader is caught up         |
| deck          | The <header> holding the badge and the intro line                                       |
| hero          | The featured story: its image, the carousel dots, and the caption                       |
| headline      | The featured story’s title, or “Read the full story” when it has none                   |
| cta           | The “Catch me up” button, when there is something to open                               |
| footer        | The card’s <footer>: the story count and the cta                                        |
| viewer        | The takeover — the full-screen dialog “Catch me up” opens                               |
| progress      | The row of progress dashes at the top of the takeover                                   |
| viewer-footer | The takeover’s bottom bar: “Previous,” “Up Next,” and “I’m caught up” on the last story |

```css
arc-compass-catchup-card::part(card) {
  border-radius: 0; /* square corners to match a flat page */
}

arc-compass-catchup-card::part(cta) {
  border-radius: 999px; /* pill-shaped "Catch me up" */
}
```

`headline` is on **two** elements: the caption under the hero on wide containers, and the title that replaces it at 640px and narrower. Only one shows at a time, so one `::part(headline)` rule styles whichever is on screen. Do not set `display` on it — that would show both titles at once or hide the only one. The narrow title is also dimmed with `opacity`, so add `opacity: 1` to the same rule if a `color` you set reads lighter there.

`viewer`, `progress`, and `viewer-footer` belong to a full-screen dialog whose layout the card owns. Change their color, background, borders, radius, padding, and type freely, but leave `display`, `position`, `inset`, `z-index`, and `overflow` alone: overriding those can leave the takeover under your site header, or cut off inside its own frame, while the page behind it still cannot scroll.

Part names are versioned public surface; everything else inside the shadow root is internal. The general `::part()` rules and the Tailwind recipe are in the [Theming Reference](https://dev.arcxp.com/content-recommendations/sdk/theming/#styling-internals-with-part).

## 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](https://dev.arcxp.com/content-recommendations/sdk/consent-identity/).

### Events

After the reader consents, 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 **after 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:

```html
<div class="catchup-slot">
  <arc-compass-catchup-card surface-id="catchup-suggestions"></arc-compass-catchup-card>
</div>
```

```css
.catchup-slot {
  min-height: 360px;
}
.catchup-slot.settled {
  min-height: 0;
}
```

```js
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.

> **Caution:**
> 
> **Also release the slot when consent is denied or never answered.** These lifecycle events fire only _after_ the reader has consented (see [Consent gating](#consent-gating)) — a card the reader has not consented to renders nothing and dispatches none of the three, so a reader who never grants consent would otherwise be left with a permanent gap. Nothing in the SDK collapses it for you, so handle the two non-granted outcomes yourself:
> 
> - **Denied** — your CMP reports the decision; collapse the reserved slot from that callback.
> - **Never answered** — the reader ignores the banner, so _no_ callback ever fires. Fall back to a timeout that collapses the slot if it is still reserved (see the recommendation components’ [consent caution](https://dev.arcxp.com/content-recommendations/sdk/recommendations-components/#reserving-space-above-the-fold) for a code sketch).

## 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 view, and a resume hook so a reader who follows a story out and comes back re-opens where they were.

```html
<!-- 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 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("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 XP during provisioning — there is no self-serve toggle. Contact your Arc XP representative to enable it. After Catch Up is 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](https://dev.arcxp.com/content-recommendations/sdk/integration-guide/) — install, initialize, consent, identity, and meta tags (required setup).
- [Compass Web SDK: Consent & Identity](https://dev.arcxp.com/content-recommendations/sdk/consent-identity/) — the consent gate and reader identity.
- [Compass Web SDK: Events & Reliability](https://dev.arcxp.com/content-recommendations/sdk/events-reliability/) — how the SDK batches and delivers events.
- [Compass Web SDK: Theming Reference](https://dev.arcxp.com/content-recommendations/sdk/theming/) — the tokens, brand block, and light and dark recipe shared by every Experience.
