Developers
Start building
Themes/Customize the cart button

Customize the cart button

Where the header cart button lives, and every supported way a theme can restyle, move, or replace it.

On this page

The cart button in the header is a platform component. It owns the mini cart (a popover on large screens, a side sheet on small ones), the live item count, the hand-off to checkout, and accessibility (screen reader announcements, focus handling). A theme gets it as a ready-made piece of chrome and decides where it sits and how it looks, without any cart logic of its own.

Where it is

The header is a section group in your theme's layout/header.json: a header section whose bar region holds blocks such as logo, menu, search, language, account and cart. The cart block draws the cart button. In the layout file it looks like this:

{
  "id": "cart-mkdl2e",
  "type": "cart",
  "settings": {
    "display": "icon_count",
    "opens": "drawer"
  }
}
SettingValuesWhat it does
displayicon, icon_count, icon_label_count, icon_label_totalThe icon alone, with the count, with the label and count, or with the label and total
opensdrawer, pageOpens the mini cart, or links straight to /cart

In code the same button is CartButton from @poltura/theme-sdk/cart, and the mini cart body is MiniCart.

Ways to customize it

Try these in order. Each step changes more than the one before it.

1. Colours and radius: no code

The button uses only the theme's design tokens, so it follows the theme:

What you seeToken it reads
Icon and label hover colour, badge glow--primary
Idle icon colour--muted-foreground
Hover / open background--muted
Hover / open border--border
Corner rounding--radius

Change these in the theme settings in the Customizer (the merchant's colours) or in your theme's src/styles/tokens.css and tokens.dark.css, and the cart button follows along with the rest of the storefront.

2. Position and layout: move the block

The cart block is an element like any other. Merchants drag it in the Customizer's header tree. A theme sets the default position by placing it in layout/header.json, for example in a right-aligned stack next to account. The block's style settings (spacing, visibility per screen size) apply to its root.

3. Scoped CSS: restyle beyond tokens

The trigger carries the stable .cart-button class, its count .cart-count, its label .cart-button-label, and the mini cart surface .cart-panel. See Chrome stable classes. These classes sit on the mini cart button. With opens: page the block draws a plain link to the cart page instead.

src/styles/theme.css
:where(.cart-button) {
  border: 1px solid var(--primary);
  border-radius: 9999px;
}

4. Label copy: theme texts

The visible label and the announcements come from the platform's cart texts (cart.title, cart.itemCount, cart.empty). A theme's texts/<language>.json overrides platform texts with the same key, so a theme can rename the button per language:

texts/en.json
{ "cart": { "title": "Bag" } }

5. Re-render the block: CartButton props

Override the cart block to draw the platform button with different props. It keeps the mini cart, the count and the accessibility work. Your override decides what to draw, so the merchant's Shown as and Opens settings apply only if you read them from settings:

src/blocks/cart.tsx
import { CartButton } from '@poltura/theme-sdk/cart';
import { overrideBlock } from '@poltura/theme-sdk/elements';

function CartBlock() {
  return (
    <div className='inline-flex items-center'>
      <CartButton
        icon='bag'
        countPlacement='overlay'
        showLabel={false}
        classes={{ count: 'bg-foreground text-background' }}
      />
    </div>
  );
}

export const cart = overrideBlock('cart', CartBlock);
PropValuesWhat it does
iconcart, bagThe glyph
showLabelbooleanThe word "Cart" beside the icon; when omitted, it shows from lg up
countPlacementinline, overlayCount beside the icon, or as a badge over it
labelLayoutinline, stackLabel beside or under the icon
classesicon, label, countClasses added to each part

6. Full replacement: own the pixels

If the design needs a completely different trigger, render your own and read the live cart state from useCart:

import { useCart } from '@poltura/theme-sdk/cart';
import { useTranslations } from '@poltura/theme-sdk/i18n';
import { Link } from '@poltura/theme-sdk/routing';

export function CartLink() {
  const { cart } = useCart();
  const t = useTranslations('cart');
  const count = cart?.itemCount ?? 0;
  return (
    <Link href='/cart' aria-label={count > 0 ? t('itemCount', { count }) : t('title')}>
      {t('title')} {count > 0 ? count : null}
    </Link>
  );
}

useCart().miniCart (open, setOpen) opens and closes the mini cart, and <MiniCart /> draws its body inside a surface of your own (surface='popover' or 'sheet').

A custom trigger gives up the platform's popover and sheet, their focus handling and the built-in announcements, and you then own all of that. Prefer steps 1 to 5 unless the design needs a different interaction.

What the platform keeps

Cart changes, checkout, payments and pricing logic never move into a theme. A theme reads state and calls actions through @poltura/theme-sdk/cart, and composes chrome from @poltura/theme-sdk/chrome. That boundary is what lets a theme keep working through platform updates.

See your changes

  • Save in the code editor, and the editor's Preview shows the storefront next to your code.
  • Publish builds a new version of the theme and puts it live on the storefront.

Next

Last updated