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"
}
}| Setting | Values | What it does |
|---|---|---|
display | icon, icon_count, icon_label_count, icon_label_total | The icon alone, with the count, with the label and count, or with the label and total |
opens | drawer, page | Opens 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 see | Token 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.
: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:
{ "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:
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);| Prop | Values | What it does |
|---|---|---|
icon | cart, bag | The glyph |
showLabel | boolean | The word "Cart" beside the icon; when omitted, it shows from lg up |
countPlacement | inline, overlay | Count beside the icon, or as a badge over it |
labelLayout | inline, stack | Label beside or under the icon |
classes | icon, label, count | Classes 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
- Product page: customize add to cart: the buy box on the product page.
- cart reference:
CartButton,MiniCart,useCartand the rest.