CardsIntermediateApril 11, 2026

Product Card

Full-bleed product card with background image, overlay logo/price, and a CTA button. Switches from overlay mode (tablet/mobile) to a right-panel info layout (wide desktop). Ready to wire to Shopify Storefront API.

View Full Demo →

Los Angeles

TOP-SELLER, LIGHTWEIGHT, STIFF, HIGH CLASS AND COMFORT

$3,040.00 / Coming soon

Los Angeles

TOP-SELLER, LIGHTWEIGHT, STIFF, HIGH CLASS AND COMFORT

$3,040.00 / Coming soon

Detroit

PURE PERFORMANCE TRACK GEOMETRY. STIFF, PRECISE AND FAST

$3,140.00 / Coming soon
demo.jsx
import ProductCardList from "./index.jsx";
import { productCard } from "../demo-data.js";

export default function ProductCardDemo() {
  return <ProductCardList {...productCard} />;
}
index.jsx
import styles from "./styles.module.css";

function ProductCard({ backgroundImage, productUrl, verticalLabel, logoText, description, price }) {
  return (
    <section
      className={styles.card}
      style={{ backgroundImage: `url(${backgroundImage})` }}
    >
      {verticalLabel && (
        <figure className={styles.labelVertical}>
          <img src={verticalLabel} alt="" />
        </figure>
      )}

      <div className={styles.info}>
        {logoText && <p className={styles.logoText}>{logoText}</p>}
        {description && <p className={styles.description}>{description}</p>}
        <span className={styles.price}>{price}</span>
        <div className={styles.actions}>
          <a href={productUrl || "#"} className={styles.cta}>+ INFO</a>
        </div>
      </div>
    </section>
  );
}

export default function ProductCardList({ cards = [] }) {
  return (
    <div className={styles.list}>
      {cards.map((card, i) => (
        <ProductCard key={i} {...card} />
      ))}
    </div>
  );
}
styles.module.css
.list {
  width: 100%;
}

/* ─── Card ─── */

.card {
  position: relative;
  width: 100%;
  height: 100vh;
  background-color: #d3d7da;
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  border-bottom: 1px solid #fff;
}

/* ─── Vertical label (wide desktop only) ─── */

.labelVertical {
  display: none;
}

.labelVertical img {
  height: 200px;
  width: auto;
}

/* ─── Info — overlay mode (default, all sizes ≤ 1279px) ─── */

.info {
  position: absolute;
  inset: 0;
  padding: 2rem 2.5rem;
  display: grid;
  grid-template-areas:
    "logo price"
    "actions actions";
  grid-template-rows: 1fr auto;
  grid-template-columns: 1fr auto;
  align-items: start;
}

.logoText {
  grid-area: logo;
  font-family: cursive;
  font-size: 2rem;
  font-weight: normal;
  line-height: 1.1;
  margin: 0;
}

.description {
  display: none;
}

.price {
  grid-area: price;
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-align: right;
  white-space: nowrap;
  margin: 0;
}

.actions {
  grid-area: actions;
  justify-self: end;
  align-self: end;
}

.cta {
  display: inline-block;
  padding: 0.5rem 1.125rem;
  border: 1px solid #000;
  border-radius: 9999px;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  background: transparent;
  color: #000;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}

.cta:hover {
  background: #000;
  color: #fff;
}

/* ─── Tablet / laptop (481px – 1279px) ─── */

@media (max-width: 1279px) {
  .card {
    height: 560px;
  }
}

/* ─── Mobile (≤ 480px) ─── */

@media (max-width: 480px) {
  .card {
    height: 75vw;
    min-height: 260px;
  }

  .info {
    padding: 1.25rem;
  }

  .logoText {
    font-size: 1.125rem;
  }

  .price {
    font-size: 0.75rem;
  }

  .cta {
    background: #000;
    color: #fff;
    padding: 0.4rem 0.875rem;
    font-size: 0.688rem;
  }

  .cta:hover {
    background: #333;
  }
}

/* ─── Wide desktop — right panel mode (≥ 1280px) ─── */

@media (min-width: 1280px) {
  .card {
    background-size: 62% auto;
    background-position: left center;
  }

  .labelVertical {
    display: block;
    position: absolute;
    left: 20px;
    top: 50%;
    transform: translateY(-50%);
    margin: 0;
  }

  .info {
    position: absolute;
    inset: auto;
    right: 60px;
    top: 50%;
    transform: translateY(-50%);
    max-width: 360px;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
  }

  .logoText {
    font-size: 3.5rem;
    white-space: normal;
    margin: 0;
  }

  .description {
    display: block;
    font-size: 0.8125rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    line-height: 1.3;
    margin: 0;
  }

  .price {
    font-size: 1rem;
    text-align: left;
    white-space: normal;
    margin: 0;
  }

  .actions {
    justify-self: auto;
    align-self: auto;
    width: 100%;
  }

  .cta {
    display: block;
    text-align: center;
    background: #000;
    color: #fff;
    padding: 0.75rem 1.5rem;
    width: 100%;
    box-sizing: border-box;
  }

  .cta:hover {
    background: #333;
    color: #fff;
  }
}

Apr 11, 2026

CARDS

Product Card Grid

3-column product grid with square image cards and a bottom info strip showing title and price. Collapses to 2 columns on mobile. Ready to wire to Shopify Storefront API or any product collection endpoint.

Apr 10, 2026

CARDS

Article Grid

A responsive grid of article cards with image, title, and excerpt. Accepts any number of articles and a configurable column count. Ready to wire to a CMS — swap the static array for a data fetch.

Apr 7, 2025

CARDS

Card Overlay Fade

A semi-transparent dark overlay with backdrop blur appears over a card on hover. Frames the card content and creates depth by softening the background image. Source: itsjay.us work cards.