Cart drawer
A slide-in panel rendered on every page of the store, not by a section group. The header’s bag icon opens it, and so does every successful add-to-cart in the theme.
Because it is a real section, you can still customize it in the theme editor — it just is not reorderable, since it is an overlay rather than page furniture.
Section settings
Section titled “Section settings”| Setting | Type | Default | What it does |
|---|---|---|---|
| Empty heading | Text | Your bag is empty | Heading when the bag is empty |
| Empty button label | Text | Shop the drop | Button under it, linking to all products |
| Allow an order note | Checkbox | true | Allow an order note |
| Note label | Text | Add a note | The <summary> label on the collapsible note |
| Tax note | Text | Tax and shipping calculated at checkout. | Fine print above checkout |
| Checkout label | Text | Checkout | Checkout button label |
| View bag label | Text | View bag | Link to the full cart page |
| Free shipping unlocked | Text | Free shipping — unlocked | Shown once the threshold is met |
| Free shipping remaining | Text | from free shipping | Follows the remaining amount |
| Suggest an add-on | Checkbox | true | Suggest one add-on, using Shopify’s product recommendations for what is already in the bag |
| Suggestion heading | Text | Goes with this | Heading over the suggestion |
What a line shows
Section titled “What a line shows”Thumbnail, product title, variant title (suppressed when blank or Default Title), the selling plan name on a subscription line, a unit price when the product has unit pricing, each line-level discount as Title −amount, then a quantity stepper with a Remove control and the line total.
Every row carries data-key from the Shopify line key. That is what lets the drawer morph rather than rebuild — see below.
The free-shipping bar
Section titled “The free-shipping bar”Driven by the global Cart → Free shipping threshold setting; nothing renders when it is unset or zero.
It stays on the page rather than being removed, because the drawer’s script patches it in place as the cart changes. But an empty bag has nothing to make progress towards, so the bar starts hidden whenever the cart is empty and is revealed once there is something in it. The footer (totals, checkout) is hidden on an empty bag for the same reason.
How updates stay in sync
Section titled “How updates stay in sync”-
Any add-to-cart or quantity change posts to Shopify (
/cart/add.js,/cart/change.js). -
The drawer is then re-fetched through the Section Rendering API —
GET /?sections=cart-drawer— so whatever the theme renders is what the shopper sees — there is no second version of the markup to keep in step. -
The fresh markup is morphed into place rather than swapped with
outerHTML. Replacing the node dropped keyboard focus, the list’s scroll offset and any transition in flight, and forced the drawer to be re-opened afterwards. The incoming markup always saysdata-open="false"— it was rendered for a fresh page — so the live open state is carried across explicitly. -
If the sections request fails or takes longer than 2.5 seconds, the theme aborts it and renders the drawer from
/cart.jsclient-side instead, so the UI always settles.
A few details the drawer handles because they are easy to get wrong:
- Only one drawer at a time. Adding to the bag from the wishlist drawer used to open the cart behind it — same stacking level, later element in the document wins — so the confirmation was never seen. Opening any drawer now closes the others.
- Scroll locking is counted, not boolean, because overlays stack (quick view over a drawer) and the first one closing must not unlock while another is still open. The scrollbar’s width is added back as padding, so the page does not jump sideways. Every
<dialog>releases its lock onclose, however it was closed. - The slide-in animation is flushed before opening. A node inserted and flipped open in the same frame has no rendered closed state to animate from, so the panel used to snap into place.
The note
Section titled “The note”With show_note on, a collapsible <details> holds a textarea. It saves itself on a 500ms pause, flushes on blur, and a pending note is landed before the checkout form is allowed to submit. Same mechanism as the cart page.
Gift wrap
Section titled “Gift wrap”The same gift-wrap snippet the cart page uses, driven by the global Cart settings — see Gift wrap. The drawer and the page render it with different ids so both can coexist on the cart page without clashing.
The suggestion
Section titled “The suggestion”With show_upsell on, the drawer renders an empty, hidden slot which the engine fills after the drawer has opened, so it never delays the drawer or reserves space for something that may not arrive.
It fetches /cart.js, takes the first product in the bag, asks /recommendations/products.json for related products, and picks the first one that is available and not already in the cart. If nothing qualifies, the slot stays hidden.
The suggestion’s own button adds a single unit by variant id, which runs through the same engine and re-renders the drawer.
Checkout
Section titled “Checkout”The footer carries the subtotal, each cart-level discount as its own row, the tax_note, a real POST to the cart URL with name="checkout", and a View bag link to the full cart page.
Accelerated checkout buttons are not rendered in the drawer; they live on the product page via the accelerated checkout block.
Accessibility
Section titled “Accessibility”The drawer is role="dialog" with aria-modal="true" and a label. Escape closes any open drawer, the backdrop closes the one it belongs to, and each close control closes its own drawer rather than the first one in the document. The bag count in the header is updated on every change.
Related
Section titled “Related”- Cart — the full page
- Wishlist drawer
- Header