Product comparison
A Compare control on every product card collects pieces into a tray at the bottom of the screen. Open it and you get a matrix of up to four products side by side, built from what Shopify actually knows about them.
Turning it on
Section titled “Turning it on”Theme settings → Quick view & wishlist
| Setting | Type | Default | What it does |
|---|---|---|---|
| Enable product comparison | Checkbox | On | The compare control on cards, the tray, and the matrix dialog |
How a shopper uses it
Section titled “How a shopper uses it”- Compare appears in the top-left corner of each product card, opposite the wishlist heart.
- Tapping it adds the piece and raises a tray along the bottom of the window showing thumbnails of what’s collected.
- Tapping Compare (3) in the tray opens the matrix.
- Clear empties the selection; the × on each thumbnail removes one.
The selection is held by the shopper’s browser, so it survives moving between pages — they can collect three pieces across two collection pages and a search result, then open the matrix.
The tray is hidden entirely when nothing is selected, and the control reflects its own state with aria-pressed, so a piece already in the tray reads as selected.
The four-piece cap
Section titled “The four-piece cap”Four is the maximum. Adding a fifth shows a toast reading “Compare up to 4 pieces at once” and the selection is left alone — the earliest pick isn’t silently dropped.
The cap and the message come from the same place, so the two can never disagree. If you translate compare.full, keep the {{ max }} variable in it.
Four is a layout limit, not an arbitrary one: five columns of product imagery on a phone-width matrix stops being comparable.
What the matrix shows
Section titled “What the matrix shows”A header row of product images and names linking to each product, then one row per attribute:
| Row | Source |
|---|---|
| Price | product.price, formatted in the cart currency |
| Availability | product.available → “In stock” / “Sold out” |
| Type | product.type |
| Made by | product.vendor |
| (each option name) | Every option any of the compared products has, with its values joined |
Option rows are collected in first-seen order across all four products, so comparing a jacket with Size and Colour against a pack with Capacity gives you three option rows and the pack simply shows a dash under Size.
Nothing is invented
Section titled “Nothing is invented”Two rules keep the matrix honest:
- A row is only drawn when at least one compared product has a value for it. An attribute nobody has never becomes an empty band of dashes.
- Shopify’s
Title / Default Titlepseudo-option is filtered out. Every single-variant product carries it; it’s internal plumbing, not a real choice, and rendering it produced a useless “Title: Default Title” row.
Missing values render as a dim em dash rather than an empty cell, so the grid stays readable.
How it works
Section titled “How it works”Both the tray and the dialog are rendered empty in the page and filled by script from /products/{handle}.js. A shopper who never compares anything downloads nothing beyond that markup. Fetched products are cached for the life of the page, so opening and closing the matrix doesn’t re-request them.
The matrix is a real <dialog>, so the browser supplies the focus trap, the top layer and Escape-to-close. Opening it locks page scroll; closing releases it.
Grid columns are driven by a --cmp-cols custom property set to the number of products actually resolved, so a tray with one dead handle in it still lays out correctly.
Locale keys
Section titled “Locale keys”Under compare in locales/en.default.json:
short (the card control), title, open, clear, add_label, remove, full, price, availability, type, vendor, in_stock, sold_out, view.
The row labels — price, availability, type, vendor — are read by the script from the layout’s translated string bundle, so translating them translates the matrix.