{%- comment -%} MIT License · (c) 2026 Lintel · https://lintelagency.com/resources/shopify-sections {%- endcomment -%} {% doc %} Product card shared by the library's product sections: image (second image on hover), optional vendor, price with compare-at price, sale and sold-out badges, and an optional quick add. Quick add is a plain Shopify product form that posts the only variant to /cart/add; products with options link to the product page. Colors come from the parent section's --lib-* custom properties. With `template: true` it prints empty slots (data-slot) for a script to fill from product JSON, so cards built in the browser look exactly like cards built in Liquid. @param {product} [product] - The product; blank prints a placeholder card (theme editor only) @param {boolean} [template] - Print empty slots for a script instead of a product @param {string} [title_tag] - Tag for the product title (default 'h3') @param {string} [ratio] - Image shape: 'square' (default), 'portrait', 'tall' or 'landscape' @param {string} [sizes] - sizes attribute for the image @param {boolean} [eager] - Load the image immediately @param {boolean} [show_vendor] - Show the vendor above the title @param {boolean} [show_second_image] - Show the second image on hover @param {boolean} [show_quick_add] - Show the quick add button or the choose options link @param {string} [quick_add_label] - Quick add button text @param {string} [choose_label] - Link text for products with options @param {string} [sold_out_label] - Badge and button text for sold-out products @param {string} [sale_label] - Badge text for products on sale @param {string} [sale_price_label] - Screen-reader label before the sale price @param {string} [regular_price_label] - Screen-reader label before the compare-at price @param {string} [id_prefix] - Unique prefix for the title id (the quick add button points at it) @example {% render 'lib-product-card', product: product, ratio: 'portrait', show_quick_add: true, quick_add_label: 'Add to cart', id_prefix: section.id %} {% enddoc %} {%- liquid assign tag = title_tag | default: 'h3' assign shape = ratio | default: 'square' assign title_id = id_prefix | append: '-' | append: product.id assign on_sale = false if product.compare_at_price > product.price assign on_sale = true endif assign has_img2 = false if show_second_image and product.media.size > 1 and product.media[1].preview_image != blank assign has_img2 = true endif -%} {%- if template -%}
{%- if show_vendor -%}

{%- endif -%} <{{ tag }} class="lib-card__title">

{%- elsif product != blank -%}
{%- render 'lib-image', image: product.featured_image, class: 'lib-card__img', sizes: sizes, eager: eager, placeholder: 'product-1' -%} {%- if has_img2 -%} {%- endif -%} {%- if product.available == false and sold_out_label != blank -%} {{ sold_out_label | escape }} {%- elsif on_sale and sale_label != blank -%} {{ sale_label | escape }} {%- endif -%}
{%- if show_vendor and product.vendor != blank -%}

{{ product.vendor | escape }}

{%- endif -%} <{{ tag }} class="lib-card__title" id="{{ title_id }}"> {{ product.title | escape }}

{%- if on_sale -%} {{ sale_price_label | escape }} {{ product.price | money }} {{ regular_price_label | escape }} {{ product.compare_at_price | money }} {%- else -%} {{ product.price | money }} {%- endif -%}

{%- if show_quick_add -%}
{%- if product.has_only_default_variant -%} {%- form 'product', product, class: 'lib-card__form' -%} {%- endform -%} {%- else -%} {{- choose_label | escape -}} {%- endif -%}
{%- endif -%}
{%- else -%}
{{ 'product-1' | placeholder_svg_tag: 'lib-card__img' }}
<{{ tag }} class="lib-card__title">Example product title

{{ 4800 | money }}

{%- endif -%} {% stylesheet %} .lib-card { position: relative; display: flex; flex: 1; flex-direction: column; min-width: 0; border-radius: var(--lib-radius-card, 12px); color: var(--lib-text, #1f1b17); } [data-card='card'] .lib-card { background: var(--lib-card, #faf6f1); } .lib-card__media { position: relative; overflow: hidden; aspect-ratio: 1; border-radius: var(--lib-radius-card, 12px); background: color-mix(in srgb, var(--lib-text, #1f1b17) 6%, transparent); } .lib-card__media[data-ratio='portrait'] { aspect-ratio: 4 / 5; } .lib-card__media[data-ratio='tall'] { aspect-ratio: 2 / 3; } .lib-card__media[data-ratio='landscape'] { aspect-ratio: 4 / 3; } [data-card='card'] .lib-card__media { border-end-start-radius: 0; border-end-end-radius: 0; } .lib-card__img { display: block; width: 100%; height: 100%; object-fit: cover; transition: transform 0.6s cubic-bezier(0.2, 0.7, 0.2, 1); } svg.lib-card__img { fill: color-mix(in srgb, var(--lib-text, #1f1b17) 22%, transparent); } .lib-card__img2 { position: absolute; inset: 0; opacity: 0; transition: opacity 0.4s ease; } @media (hover: hover) { .lib-card:hover .lib-card__img2 { opacity: 1; } .lib-card:hover .lib-card__img { transform: scale(1.03); } } .lib-card:focus-within .lib-card__img2 { opacity: 1; } .lib-card.is-sold-out .lib-card__media img { opacity: 0.6; } .lib-card__badge { position: absolute; z-index: 2; top: 0.75rem; left: 0.75rem; padding: 0.3rem 0.7rem; border-radius: 999px; background: var(--lib-accent, #1f1b17); color: var(--lib-accent-text, #fff); font-size: 0.75rem; font-weight: 600; letter-spacing: 0.03em; line-height: 1.2; pointer-events: none; } .lib-card__badge[hidden] { display: none; } .lib-card__badge--sold-out { background: var(--lib-bg, #fff); color: var(--lib-text, #1f1b17); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--lib-text, #1f1b17) 20%, transparent); } .lib-card__body { display: flex; flex: 1; flex-direction: column; gap: 0.3rem; padding-top: 0.9rem; } [data-card='card'] .lib-card__body { padding: 0.9rem 1rem 1rem; } .lib-card__vendor { margin: 0; font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase; opacity: 0.7; } .lib-card__vendor:empty { display: none; } .lib-card__title { margin: 0; font-size: 1rem; font-weight: 600; line-height: 1.3; } .lib-card__link { color: inherit; text-decoration: none; } /* The title link covers the whole card; the quick add button sits above it. */ .lib-card__link::after { content: ''; position: absolute; inset: 0; z-index: 1; border-radius: var(--lib-radius-card, 12px); } .lib-card__link:hover { text-decoration: underline; text-underline-offset: 0.2em; } .lib-card__link:focus-visible { outline: none; } .lib-card__link:focus-visible::after { outline: 2px solid var(--lib-accent, #1f1b17); outline-offset: 3px; } .lib-card__price { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem; margin: 0; font-size: 0.9375rem; } .lib-card__price-now--sale { font-weight: 600; } .lib-card__price-was { opacity: 0.7; font-size: 0.875rem; } .lib-card__price-was[hidden] { display: none; } .lib-card__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } .lib-card__cta { position: relative; z-index: 2; margin-top: auto; padding-top: 0.75rem; } .lib-card__form { margin: 0; } .lib-card__add { display: inline-flex; align-items: center; justify-content: center; width: 100%; min-height: 2.75rem; padding: 0.6rem 1rem; border: 1.5px solid var(--lib-text, #1f1b17); border-radius: min(var(--lib-radius-card, 12px), 999px); background: transparent; color: var(--lib-text, #1f1b17); font: inherit; font-size: 0.875rem; font-weight: 600; line-height: 1.2; text-align: center; text-decoration: none; cursor: pointer; transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease; } .lib-card__add:hover:not(:disabled) { background: var(--lib-accent, #1f1b17); border-color: var(--lib-accent, #1f1b17); color: var(--lib-accent-text, #fff); } .lib-card__add:focus-visible { outline: 2px solid var(--lib-accent, #1f1b17); outline-offset: 2px; } .lib-card__add:disabled { opacity: 0.45; cursor: default; } @media (prefers-reduced-motion: reduce) { .lib-card__img, .lib-card__img2, .lib-card__add { transition: none; } .lib-card:hover .lib-card__img { transform: none; } } {% endstylesheet %}