Product cards
A product card is a smootify-product that shows one product in a list. Covers what a card needs, what it can show (price ranges, option values, badges), adding to cart from a card, cards in a slider and placeholder cards.
A product card shows one product in a list: the entries of the products collection, a search & discovery grid, a slider or a list of best sellers. In Smootify every card is the same element as the product page, smootify-product, holding a smaller design. This page covers what a card needs, what it can show, and how to add to the cart from it.
Note
Before you start, your products must be loaded as the products content collection: see Content from Shopify.
What a card needs
- A
smootify-productelement with the product’s id. In a list of entries, render each card as an element with the classsm-dyn-item, the boundary of the card, with thesmootify-productinside it and itsdata-idset to the entry’sshopifyId. In the lists Smootify fills itself (search & discovery, the search page, best sellers and the other product lists), you write one card and Smootify gives each copy its product. - Everything else is optional. Every attribute of the product page works inside a card: title, prices, conditions, metafields, even a gallery or an add-to-cart form. A card usually needs fewer of them.
---
const p = entry.data;
---
<div class="sm-dyn-item">
<smootify-product data-id={p.shopifyId}>
<a product="link" href={`/products/${entry.id}/`}><img variant="image" src={p.image} alt={p.imageAlt} width={p.imageWidth} height={p.imageHeight}></a>
<a product="url" href={`/products/${entry.id}/`}>{p.name}</a>
<smootify-price><span data-prop="price">{p.priceFormatted}</span></smootify-price>
</smootify-product>
</div>Render the links, the image, the name and the price from the entry. The card works before Smootify loads, and search engines read it; Smootify then writes the live values from Shopify.
A few things work differently on a card:
- it ignores
?variant=in the address, which is only for the page’s own product; - it starts on the first variant that can be bought, or on the first variant when none can;
- when Shopify does not return the product (it is unpublished, or not sold in the visitor’s market), Smootify sets
hiddenon the card, itssm-dyn-item.
What a card can show
Link and image
Four attributes fill the link and the pictures of a card:
| Attribute | Put it on | What it does |
|---|---|---|
product="url" | an <a> | gets the product page’s link, and the product title when the link holds only text |
product="link" | an <a> that wraps the image or the whole card | gets the link only |
variant="image" | an <img> | shows the image of the card’s variant; render the entry’s image in it, and it shows until the product loads |
product="specific-image" | an <img> | shows one image by position: index="0" is the first image, index="1" the second; without index, the second image, such as a picture shown on hover |
Images Smootify writes accept size attributes such as max-width, so the card does not download a full-size photo: see image size.
Prices
smootify-price shows the price of the card’s variant, as on the product page. variant="unit-price" adds its price per unit, such as €1.50/ 100 g (Price per unit). For the range of the product’s variants, use these attributes on text. Here on a shirt in four sizes, with S on sale and sold out:
- Shows
- The lowest and the highest variant price.
- Here
- $29 (S) and $39 (XL).
- Shows
- The price of the cheapest variant that can be bought.
- Here
- $32: S is sold out, so M.
- Shows
- The lowest and the highest crossed-out price among the variants on sale. Hidden when none is on sale.
- Here
- $39 (S) and $45 (M).
- Shows
variable-price: only when the variants have different prices.fixed-price: only when they all cost the same.- Here
- The prices differ, so "From" shows.
Every value is in product values. With the minimum prices, condition="variable-price" gives “From $29 $39”:
<div><span condition="variable-price">From </span><span product="min-price">$0.00</span> <span product="min-compare-at-price">$0.00</span></div>Option values
product="option-values" shows the values of one option, such as the colour dots under a card. Smootify makes one copy of the element per value.
<div product="option-values" data-option-name="Color,Colour"><div option="bg-color"></div></div>- Pick the option with
data-option-name="Color,Colour"(the first of those names the product has, written as in Shopify) ordata-option="2"(the second option; the default is the first). - Inside the element,
option="bg-color"oroption="color"paints the value’s colour as a background or as the text colour,option="image"shows its image andoption="title"its name. A copy without any of these shows the value’s name as text. - The colours and images come from Shopify: see Swatch colours and images.
- The element is removed when the product does not have that option.
- A link with
product="link"inside a copy opens the product with that value already chosen (the first available variant with it). - More in option values.
Option values only show. To let the visitor pick a value on the card, use a picker such as variant-swatches inside the card: it changes the card’s variant, its image and its price.
Badges and labels
- States.
condition="on-sale"(and its oppositenot-on-sale),in-stock,out-of-stock,low-stockand the other product conditions work on cards.condition="is-new"shows a “New” badge while the product was published less than 30 days ago;data-new-days="14"onsmootify-productchanges the days. - Discount.
variant="discount-percentage"shows the discount of the card’s variant. Put it inside an element withcondition="on-sale", since it shows0%otherwise. - Tags. An element with
tagged="bestseller"stays only when the product has that tag, andnot-taggeddoes the opposite: see tag conditions. - Collections and vendor.
product="collections"makes one copy per collection, with its link on a Text link;exclude="Home, Sale"leaves some out.product="first-collection"shows one.product="vendor"on a Text link links to the vendor page.
Hide a card that is already in the cart. Add hide-if-product-in-cart to smootify-product. The card (its sm-dyn-item) is hidden while a cart line holds the product. It is useful for “You may also like” lists. The value false turns it off, in any case, so a component can drive it with a boolean prop. In a slider, the card’s whole slide is hidden.
While it loads. Add skeleton="text", "image" or another loading value to the elements that should shimmer until the product loads.
Add to cart from a card
There are two ways:
| Way | Built with | Adds | Good to know |
|---|---|---|---|
| A direct button | a <button> with data-is="direct-add-to-cart", anywhere inside smootify-product; no form | the card’s variant, quantity 1 | disabled, with the class is-disabled, when the variant cannot be bought; it does not close the cart, so it works on cards inside the cart, such as cart upsells |
| A full form | a smootify-add-to-cart with a <form> inside the card, as on the product page | the quantity, the options and the extra fields of the form | not on cards inside the cart’s form: a <form> cannot sit inside another |
The direct button is a <button> with the attribute data-is="direct-add-to-cart":
<button data-is="direct-add-to-cart"><span data-state="default">Add to cart</span><span data-state="loading">Adding…</span></button>- The elements with
data-state="default"anddata-state="loading"swap while the product is added. Both are optional. - With a picker on the card, the button adds the variant the visitor picked.
- It adds one piece, or the variant’s minimum when it has purchase rules or a B2B quantity rule.
The full form is built as in step 6 of Build the product page. Use it when the card needs a quantity field, options inside the form or extra fields.
A link (<a>) cannot add to the cart. Use a <button> tag.
Cards in a slider
A slider of cards is a [slider-track] element whose children are the cards, each card its slide. The optional [slider-prev] and [slider-next] buttons and a [slider-dots] element sit next to the track. Smootify does not run them here: the site’s own slider script does. Smootify runs them only in the media sliders of the product page and in Magic Box. The track scrolls with scroll snap: its CSS gives it overflow-x: auto and scroll-snap-type. There is no product-slider element on Astro.
There are two ways to fill it:
- With entries. Render one card for each entry of a collection on the server, so the HTML has real content.
- With a Smootify source. Write one template card with
data-idset to the source, such asrelated,best-seller,last-viewedor a specific collection, and Smootify clones the slide for each product: see Product lists.
---
const { products } = Astro.props; // entries of the products collection
---
<div data-carousel>
<div slider-track>
{products.map(({ id, data }) => (
<div class="sm-dyn-item">
<smootify-product data-id={data.shopifyId}>
<a product="link" href={`/products/${id}/`}><img variant="image" src={data.image} alt={data.imageAlt} /></a>
<a product="url" href={`/products/${id}/`}>{data.name}</a>
</smootify-product>
</div>
))}
</div>
<button type="button" slider-prev aria-label="Previous" disabled>‹</button>
<button type="button" slider-next aria-label="Next">›</button>
</div>For a slider of cards, the component wires its own arrows and dots with a small <script>, and a slider of static content works the same way. How these parts are written is in Write components.
Placeholder cards
Placeholder cards, which are a smootify-product with data-id="loader", are not needed on Astro. Render the real cards on the server:
- in search & discovery and on the search page, the card template is the first
smootify-productof the block, whatever itsdata-id. The cards of the list stay as they are until the first results: Smootify keeps them when the results are the same products in the same order, as the first page served from the seed is, and otherwise replaces them in one step, so the page does not jump; - a slider filled by a product list such as best sellers is one template card with the list’s
data-id: Smootify clones its slide for each product. There are no placeholder slides.
More in smootify-product[data-id="loader"].
Before you publish
Check these first
- Every card shows the same product.
data-idholds a fixed id. Set it to the entry’sshopifyId. - A card is missing or empty. Shopify did not return that product: it is not published to the sales channel, or not sold in the visitor’s market. Its card gets
hidden. - The colour dots show names instead of colours. The option’s values have no swatch in Shopify and no option metaobject. See Swatch colours and images.
- The add button does nothing. It is not a
<button>tag. Use<button data-is="direct-add-to-cart">. - “0% off” on products that are not on sale. Put
variant="discount-percentage"inside an element withcondition="on-sale".