Skip to content

Cart

The /cart page. Lines on the left, a summary column alongside carrying gift wrap, the free-shipping bar, discounts, the subtotal, checkout and an optional shipping estimator.

Most add-to-carts in Signal land in the cart drawer rather than here, so treat this page as the considered view — the one a shopper opens to edit an order, read a note field or estimate shipping.

SettingTypeDefaultWhat it does
HeadingTextYour bagThe page <h1>
Empty headingTextNothing here yetHeading when the cart is empty
Empty button labelTextShop the dropButton under it, linking to all products
Allow an order noteCheckboxtrueAllow an order note
Note labelTextOrder noteLabel on the note field
Tax noteTextTax and shipping calculated at checkout.Fine print above the checkout button
Free shipping unlockedTextFree shipping — unlockedShown once the threshold is met
Free shipping remainingTextfrom free shippingFollows the remaining amount, e.g. “$40.00 from free shipping”
Checkout labelTextCheckoutSubmit button label
Continue labelTextContinue shoppingLink under the button
Show a shipping estimatorCheckboxtrueShow a shipping estimator so shoppers can check delivery cost before checkout

The section also accepts @app blocks, rendered in their own row under the lines — reviews, loyalty widgets, trust badges.

ElementSource
Thumbnailitem.image at 400w, linking to the product
Titleitem.product.title
Variantitem.variant.title, suppressed when it is blank or Default Title
Subscriptionitem.selling_plan_allocation.selling_plan.name when the line is a subscription
Line item propertiesEvery entry in item.properties, as Name: value
DiscountsEach line_level_discount_allocations entry as Title −amount in the accent colour
Unit priceitem.original_price with “each”
Line totalitem.final_line_price, with the original struck through when a discount applied

The +, − and Remove controls all post to /cart/change.js with a line number and a quantity, handled by the theme’s global cart engine. On the cart page they additionally carry data-reload, so the page reloads after a successful change — the totals, the free-shipping bar and the discount rows are all server-rendered here, and a reload is the honest way to keep them consistent.

Errors are surfaced as a toast, with distinct copy for a network failure versus a store-side refusal, and the control is re-enabled rather than left spinning.

With show_note on, the cart page shows a textarea under the lines. It saves itself:

  • typing debounces for 500ms then posts to /cart/update.js
  • blurring the field flushes immediately
  • submitting the cart form (clicking Checkout) holds the submit just long enough to land a pending note, then lets it through — so a note typed and immediately checked out is never lost

The same behaviour drives the note field in the cart drawer.

Shared with the drawer, and driven entirely by global settings under Theme settings → Cart:

SettingWhat it does
Gift wrap productThe wrap itself. Nothing renders unless this is set and the product is available
gift_wrap_label (default Add gift wrapping)The checkbox label; the wrap’s price is appended automatically
gift_message_enabled (default on)Adds a 200-character message field, revealed once wrapping is ticked

The wrap is a real product, not a free-floating attribute — so it is priced, taxed, counted in inventory and fulfilled like anything else, rather than being a flag that never reaches the packing bench. Ticking the box adds that product’s line; unticking finds and removes it.

The checkbox state is derived by looking for the wrap’s variant id in the cart, so swapping the wrap product in settings does not leave a stale line ticked. If the add or remove fails, the checkbox is put back where the cart actually is.

The gift message is stored as the cart attribute Gift message — which is what shows on the order in admin — and saved on a 500ms pause rather than per keystroke.

Driven by the global Cart → Free shipping threshold setting. When that setting is empty or zero, the bar does not render at all. When it is set, the bar shows either ship_unlocked or remaining + ship_away, with a progress track clamped at 100%.

With show_estimator on, a <details> panel offers country, province and postal code, then calls /cart/shipping_rates.json and lists each rate with its price.

  • The province field is populated from Shopify’s own country data and stays hidden for countries that have no provinces.
  • A 422 from Shopify is read rather than replaced with a generic failure, so an invalid postcode produces Shopify’s own explanation.
  • With no rates for the address, a dedicated “no rates” message is shown rather than an empty list.
  • Output is announced via role="status" and aria-live="polite".

Above the subtotal, each cart-level discount renders as its own row. The subtotal uses cart.total_price. Below it: the tax_note, the checkout submit, and the continue-shopping link.