Last updated

Cart Feedback (Toasts & Button Loading)

When a shopper adds, updates, or removes a cart item on your theme, the FairShare SDK can surface the result for you — a toast confirms success or reports a failure, and add-to-cart buttons show a spinner while the request is in flight. Both are opt-in (off by default).

This page is for theme authors: how to enable, style, and localize them from your theme.

This is the theme-facing guide. For the full event payloads and JavaScript API, see the SDK reference: Cart Operation Events.

The mental model

  • Your theme's buttons drive cart mutations — usually via data-fluid-add-to-cart (see Product Bundles and the cart docs).
  • Once enabled, the SDK renders the toast and manages button spinners for you when a mutation settles — no per-element wiring.
  • You enable and configure it from your theme's own JavaScript with FairShareSDK.configureCartFeedback(...), and style it with your theme CSS.

Because the toast renders in the light DOM, your theme's CSS applies to it directly — no Shadow DOM to pierce.

Turning it on

The features can be toggled with data-fluid-toast="true" / data-fluid-button-loading="true" attributes on the Fluid <script> tag — but that script is usually a global embed injected into the page, which a theme can't edit. So from a theme, enable and configure them at runtime instead:

<!-- Anywhere in your theme, e.g. layout/theme.liquid -->
<script>
  window.addEventListener("DOMContentLoaded", () => {
    window.FairShareSDK?.configureCartFeedback({
      toast: true,
      buttonLoading: true,
      position: "bottom-right",
      class: "theme-toast",
    });
  });
</script>

Notes:

  • Call it inside DOMContentLoaded — the SDK embed loads asynchronously, so it may not be attached yet when your inline script first runs. The optional chaining (?.) makes it a safe no-op if so.
  • Pass only the fields you want. The config is read lazily (at toast-show / button-click time) and merges over any data-fluid-* script-tag attributes — precedence is configureCartFeedback() > attributes > defaults. So if you do control the embed, the attributes still work as a baseline.
  • The CART_OPERATION_SUCCESS / CART_OPERATION_ERROR events fire regardless — to render your own UI instead, just don't set toast: true and listen to those.

All config keys

KeyTypeDescription
toastbooleanEnable the built-in toast
buttonLoadingbooleanEnable the auto-spinner on declarative add-to-cart / enrollment buttons
positionstringbottom-center (default), bottom-left, bottom-right, top-center, top-left, top-right
classstringClass added to the toast element (style variants in CSS via data-variant)
durationnumberAuto-dismiss delay in ms (default 4000)
iconbooleanShow the variant icon (default true)
successIcon / errorIcon / closeIconstringCustom icon — emoji, text, or SVG markup
messagesobject{ add, update, remove, error } message overrides

Styling the toast

You don't need a class at all — the toast always has id="fluid-toast" and a data-variant attribute, so you can target it directly:

/* assets/custom.css */
#fluid-toast {
  border-radius: var(--border_radius, 8px);
  font-family: var(--font_family);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
#fluid-toast[data-variant="success"] {
  background: var(--primary_color, #0f9d58);
  color: #fff;
}
#fluid-toast[data-variant="error"] {
  background: #d93025;
  color: #fff;
}

Prefer your own class? Pass class to configureCartFeedback({ class: "theme-toast" }) and style .theme-toast[data-variant="error"] instead. Either way, style each variant off the data attributes the toast exposes:

Data attribute on the toastValuesTarget it for
data-variantsuccess / errorSuccess vs. error styling
data-positionthe configured positionPosition-specific tweaks
data-stateopen / closedEnter/exit styling

Translating the copy

Pass messages to override the defaults per category. In a Liquid theme the inline script is rendered server-side, so you can pull the copy from your locale files and the toast follows the shopper's language.

Run each string through the json filter instead of wrapping it in quotes yourself. | json emits a fully escaped JavaScript string literal — its own quotes included — so a translation that contains an apostrophe, a quote, or a backslash can't break the script. Note there are no surrounding " in the snippet below; json supplies them:

<script>
  window.addEventListener("DOMContentLoaded", () => {
    window.FairShareSDK?.configureCartFeedback({
      toast: true,
      messages: {
        add: {{ 'cart.added' | t | json }},
        update: {{ 'cart.updated' | t | json }},
        remove: {{ 'cart.removed' | t | json }},
        error: {{ 'cart.error' | t | json }},
      },
    });
  });
</script>

| t is the Liquid translation filter and | json escapes the result for the JavaScript string — swap | t for your theme's localization helper if it differs. The cart.* keys are an example, not built-ins: add them (and their translations) to your theme's locales/*.json files first, and keep the namespace consistent between the snippet and the files. A key that isn't defined renders a visible "translation missing" string into the toast rather than falling back to the default.

Message keyDefault
addAdded to cart
updateCart updated
removeRemoved from cart
errorSomething went wrong. Please try again.

Loading buttons

With buttonLoading: true, buttons wired with data-fluid-add-to-cart or data-fluid-add-enrollment-pack show a spinner automatically while their request runs — no per-button theme code. Add data-fluid-loading-text to swap the label while loading:

<button data-fluid-add-to-cart="{{ product.variant_id }}" data-fluid-loading-text="Adding…">
  {{ 'product.add_to_cart' | t }}
</button>

The spinner inherits the button's text color, so it matches your theme without extra CSS.

If your theme runs a cart mutation from its own JavaScript (custom payloads, dynamic bundles), wrap the call so the spinner is handled for you — this works even without buttonLoading enabled:

button.addEventListener("click", (e) => {
  window.FairShareSDK.withButtonLoading(e.currentTarget, () =>
    window.FairShareSDK.addCartItems(variantId, { quantity: 1 }),
  );
});

See Cart Operation Events → Button Loading Indicator for setButtonLoading and the full API.

Putting it together

A theme that enables, styles, localizes, and gets loading add-to-cart buttons:

<!-- layout/theme.liquid -->
<button data-fluid-add-to-cart="{{ product.variant_id }}" data-fluid-loading-text="{{ 'product.adding' | t }}">
  {{ 'product.add_to_cart' | t }}
</button>

<script>
  window.addEventListener("DOMContentLoaded", () => {
    window.FairShareSDK?.configureCartFeedback({
      toast: true,
      buttonLoading: true,
      position: "bottom-right",
      class: "theme-toast",
      messages: {
        add: {{ 'cart.added' | t | json }},
        error: {{ 'cart.error' | t | json }},
      },
    });
  });
</script>
/* assets/custom.css */
.theme-toast { border-radius: var(--border_radius, 8px); }
.theme-toast[data-variant="success"] { background: var(--primary_color); color: #fff; }
.theme-toast[data-variant="error"]   { background: #d93025; color: #fff; }