Widgets & Llama Cart

Custom CSS

The two places to write custom CSS, when to use each, and how widget style isolation shapes your selectors.

When the visual customizer controls aren't enough, the app gives you raw CSS. There are two places to write it, and they have very different reach. This guide explains both surfaces, how each widget's style isolation changes the selectors you write, where the Quick View modal has to be styled, and a library of copy-paste recipes you can drop in today.

Before you reach for CSS, remember most styling is already exposed as visual controls — colors, border radius, spacing, button labels — in each campaign's appearance settings. Under the hood, those controls persist to the app's --lcu-* CSS custom-property tokens (for example --lcu-background, --lcu-border-radius, --lcu-button-bg, --lcu-drawer-width, and --lcu-text-color), so most look-and-feel changes never need a selector at all. Use raw CSS only for the things those controls don't cover.

The two CSS surfaces

The app has exactly two places to write custom CSS. The only difference that matters is scope: one applies everywhere, the other applies to a single widget.

SurfaceWhere it livesScopeReach
Global StylesSettingsGlobal stylesStore-wideEvery app widget, every Llama Cart block, and checkout elements — all at once
Custom CSS (per widget)The Custom Styles section inside each campaign customizer and each Llama Cart blockThat one widget/block onlyOnly the widget you're editing

Global Styles (Settings)

Open Settings in the sidebar and scroll to the Global styles section — it sits at the bottom of the page, after Cart page, Global, Global translatable, and Checkout. It's a full VS Code-style CSS editor: as you type, syntax errors are highlighted in red so you can spot a missing brace or stray character before you save.

Anything you write here is applied globally — across every campaign widget, every Llama Cart block, and checkout elements. This is the right surface when you want a rule to apply consistently everywhere the app appears, instead of pasting the same CSS into widget after widget.

/* Applied store-wide from Settings > Global styles */
.lcu__button {
  background-color: #1a1a1a;
  border-radius: 8px;
}

Custom CSS (per widget / per block)

Every storefront widget also carries its own Custom CSS field, found in the Custom Styles section at the bottom of:

  • each campaign customizer (Product recommendations, Gift with purchase, and the scrollable/list/slider layouts), and
  • each Llama Cart block inside the Customizer.

CSS you write in a widget's Custom CSS field is scoped to that one widget only. It never touches any other campaign or block. Use it for a one-off tweak that should not bleed into the rest of your store.

Rule of thumb: global = everywhere, per-widget = just this one. If you find yourself pasting the same CSS into more than one widget, move it to Global Styles instead and delete the per-widget copies.

How style isolation changes your selectors

This is the single most important thing to understand before writing a selector. Every storefront widget renders inside its own isolated container. That isolation runs both ways:

  • Your theme's CSS can't reach in and change a widget's appearance.
  • The widget's CSS can't leak out into your theme.

The practical consequence: you must target the app's own class names (like .lcu__button), not your theme's classes. A selector that overrides a theme element — say, your theme's .product-card rule — will not apply inside the widget, because the theme's styles never enter the isolated container in the first place. Likewise, a rule you write for the widget won't change anything elsewhere on the page.

Don't try to "win" against your theme with a high-specificity selector or !important aimed at theme classes. Isolation means there's nothing to win against — the theme rule isn't present inside the widget. Target the app's classes directly and your rule applies cleanly.

Because both Global Styles and the per-widget field ultimately target the same app-rendered markup, the selectors you write are the same in both places. The only thing that changes is how many widgets the rule reaches.

Where to style the Quick View modal

The Quick View modal — the product preview that opens when a shopper clicks the eye icon — is a special case. It renders outside the widget's isolated container, so a rule placed in a widget's per-widget Custom CSS field will not reach it.

The app makes this explicit. Next to the Show Quick View Button toggle in the customizer, you'll see the hint:

To customize the style of the quick view modal, navigate to SettingsGlobal Style.

Style the Quick View modal from SettingsGlobal styles, never from a widget's per-widget Custom CSS field. CSS for the modal placed in the per-widget field has no effect because the modal renders outside that widget's container.

Saving: nothing reaches the storefront until you Save

Both editors follow the same save model. Your CSS only goes live after you click Save in the sticky save bar at the top of the page.

  • Global Styles: edit, then click Save in the sticky save bar on the Settings page. This save bar covers every Settings section, including the CSS editor.
  • Per-widget Custom CSS: edit, then save the customizer/Customizer the same way you save any other widget change.

Unsaved CSS never reaches your storefront. If you leave the page without saving, your edits are lost — there is no autosave.

Recipe library

Copy these into the appropriate surface. Each comment notes whether it belongs in Global Styles or a per-widget Custom CSS field. Adjust the values to match your brand.

Brand the "Add to cart" button (global)

/* Settings > Global styles — applies to every app "Add to cart" button */
.lcu__button {
  background-color: #1a1a1a;
  border-radius: 8px;
}

Increase a widget's heading size (per widget)

/* This widget's Custom CSS — make the heading larger and bolder */
.lcu__product-title {
  font-size: 22px;
  font-weight: 700;
}

Tighten product card spacing (per widget)

/* This widget's Custom CSS — reduce the gap inside each product card */
.lcu__template__item {
  padding: 8px;
}

Style the Llama Cart drawer width and colors (global)

/* Settings > Global styles — the same rule reaches every Llama Cart version and market */
.lcu__drawer {
  width: 420px;
  background-color: #ffffff;
  color: #1a1a1a;
}

Hide a price element (per widget or global)

/* Hide the price shown on the widget's product cards */
.lcu__product-price {
  display: none;
}

Restyle the Quick View modal (global only)

/* Settings > Global styles — the modal renders outside the widget container */
.lcu__quick-view__content {
  border-radius: 12px;
  max-width: 640px;
}

The class names above are examples of the app's own markup. To find the exact class on the element you want to change, inspect it in your browser's developer tools on a live storefront page, then copy the app class it shows. Always target the app's classes, not your theme's.

Edge cases and complex scenarios

Global vs per-widget specificity

When the same property is set in two places, the more specific surface wins. A widget's own Custom CSS, and the campaign/block's own visual settings (its color pickers, border radius, button text, and so on), override what you set in Global Styles for that widget.

If a global rule "isn't working" on one widget, check that widget's own Custom CSS field and its visual settings — one of them is almost certainly overriding the global rule for that widget specifically. Either remove the per-widget override, or accept that this widget is intentionally different.

Use this to your advantage: set the look you want everywhere in Global Styles, and override only the one campaign or block that needs to be different.

Markets and Llama Cart versions each carry their own CSS

The Llama Cart can run up to three versions (A, B, and C) and, with Shopify Markets access, a separate configuration per market. Each of those is an independent copy of the cart — and each block inside each one has its own Custom CSS field.

That means a per-block tweak only affects the version (and market) you edited. The other versions and markets keep their own CSS.

Global Styles is the only surface that covers every Llama Cart version and every market at once. If a rule should apply no matter which version is live or which market a shopper is in, put it in SettingsGlobal styles rather than in individual blocks. See Llama Cart versions & customization for how versions and markets work.

Syntax errors don't block Save — but invalid CSS is silently ignored

The Global Styles editor highlights syntax errors in red, but those red marks do not stop you from saving. The app saves whatever you typed. On the storefront, invalid CSS is simply ignored — the rule with the error has no effect, and there's no error message for the shopper.

A red highlight is your only warning. If a rule isn't applying and the rest of your CSS is fine, look for a syntax error (a missing ;, an unbalanced { or }, or a typo in a property name) in or just before that rule — the browser will drop the broken declaration without complaint.

Don't style the Shopify-owned checkout DOM

Checkout upsell widgets render inside Shopify's checkout, which does not accept free-form CSS. Its appearance is driven by Shopify's design tokens (named values like Accent, Subdued, Info, and the title/size/color dropdowns), not by selectors you write.

Targeting the checkout's own DOM with custom CSS won't work and isn't supported. Configure the checkout widget's look from its dropdown controls in the campaign customizer instead. See Checkout upsells (deep dive) for the title, product card, and button appearance settings.

Troubleshooting: "my CSS isn't applying"

Work down this checklist in order — the cause is almost always one of these.

Did you Save?

Confirm you clicked Save in the sticky save bar after editing. Unsaved CSS never reaches the storefront. Reload the page and re-open the editor to verify your CSS is actually stored.

Are you on the right surface?

If the rule should apply everywhere, it belongs in SettingsGlobal styles. If it should apply to one widget, it belongs in that widget's Custom CSS field. The Quick View modal must be styled from Global Styles — a per-widget rule won't reach it.

Does your selector target the isolated container?

Each widget renders in its own isolated container, so target the app's own classes (inspect the live element to find them), not your theme's classes. A selector aimed at a theme class won't apply inside the widget.

Is a per-widget setting overriding it?

Per-widget Custom CSS and the campaign/block's visual settings override Global Styles for that widget. If one widget ignores a global rule, check that widget's own Custom CSS and its appearance controls.

Is there a syntax error?

A red highlight in the editor doesn't block Save, and invalid CSS is silently ignored on the storefront. Check the rule (and the one just before it) for a missing brace, missing semicolon, or misspelled property.

On this page