Notes for theme developers

Cartal runs as a theme app extension: one app embed that renders the progress bar, the gift button and the upsell row inside your cart drawer and cart page. It never edits theme files. This page covers the hooks a theme can use to fit it in.

Where each setting lives

Every setting has exactly one home: either the Cartal app or the Cart progress app embed in the theme editor. The bar always sits at the top of the cart.

SettingHome
Rewards, amounts, reward names and iconsApp (Rewards)
Bar messages (while earning, unlocked, all unlocked)App (Rewards Settings)
Discount combinations and currency overridesApp (Rewards Settings)
Upsells, sources, empty cart suggestions, collapse when the cart fillsApp (Upsells)
BundlesApp (Bundles)
Show reward markers, also show on the cart pageTheme editor (App embeds, Cart progress)
Bar colors (fill, track, text, marker); blank uses the themeTheme editor (App embeds, Cart progress)
Sizes, fonts, spacing, animation, gift button look, upsell card layoutTheme editor (App embeds, Cart progress)

The cart icon attribute

When Always show the cart icon is on in the Cartal app embed, Cartal setsdata-cart-pilot-cart-icon="always" on <html> from an inline script in the head, before the page paints. Cartal never restyles theme headers itself. Stock Horizon and Dawn always show the cart icon, so they need nothing.

A theme that hides its cart icon while the cart is empty should keep it visible when the attribute is set, so shoppers can open the drawer and see empty cart suggestions:

html[data-cart-pilot-cart-icon="always"] .header__cart-icon--empty {
  display: revert;
  visibility: visible;
}

If the cart button also refuses to open an empty drawer, gate that check on the same attribute:

const alwaysOpen = document.documentElement.dataset.cartPilotCartIcon === "always";
if (cartIsEmpty && !alwaysOpen) return;

Cart refresh events

Cartal changes the cart on its own when it adds, swaps, removes or caps a free gift, and when a shopper adds an upsell or a bundle. After each change it asks the theme to re-render its cart and header count. What it sends, in order of detection:

A theme with its own cart UI can listen for any of these, or just cart:refresh, and re-render. Cartal ignores events whose detail.sourceId, detail.source or detail.data.sourceis "cart-pilot", so re-dispatching its event does not cause a loop.

The gift-added event

After a shopper confirms a free gift and it is in the cart, Cartal dispatches on document:

document.addEventListener("cart-pilot:gift-added", (e) => {
  const { variantId, productId, lineKey, tierId } = e.detail; // productId and lineKey may be null
});

It also sets data-cart-pilot-new for 1.5 seconds on the matching cart line (found by[data-key], [data-line-key], [data-cart-item-key],[data-item-key] or [data-line-item-key], else by variant id), which gets a subtle background flash. Themes can override [data-cart-pilot-new].

Gift button and panel

The gift button (.cbp__gift-btn) spans the full width of the bar with a 44px minimum height. It carriesdata-attn while a gift is unlocked but not chosen, and data-chosen once one is. When a reward offers a choice of gifts, the button opens a panel (.cbp-gs, role="dialog") inside the cart drawer, or a centered sheet on the cart page. Escape, the back button and the backdrop close it without changes.

Theme editor settingSetting idReaches the bar as
Button style: Filled (default), Outline, Softgift_btn_styledata-gift-style="outline" or "soft"
Button color (blank: the progress fill)gift_btn_color--cbp-gift
Button text color (blank: picked for contrast)gift_btn_text--cbp-gift-text
Show a gift icon (default on)gift_icondata-gift-icon-off when off
Attention effect: None, Pulse (default), Shinegift_attentiondata-gift-fx="none" or "shine"

CSS variables and attributes

Embed settings reach the progress bar and upsell row as CSS custom properties and data attributes, set only when changed, so defaults leave your theme's values alone.

VariableControls
--cbp-message-sizeMessage text size
--cbp-label-sizeMilestone label size
--cbp-pad-y, --cbp-pad-xVertical and horizontal padding
--cbp-highlightAmount and reward highlight color
--cbp-earned-size, --cbp-earned-colorUnlocked line size and color
--cbp-heightBar thickness (1 to 8px)
--cbp-marker-sizeMilestone marker size (8 to 24px)
--cbr-gapSpace between upsell cards
--cbr-count-widthCard width when Cards per row is set

Building a theme and something does not fit? Contact support.