Pure TailwindMIT licensed

Tailwind Minimalist eCommerce Product Card

Restraint as the technique: no border, no shadow at rest, a Cormorant display name over a Jost micro-label, and a text CTA whose underline draws itself in from the left. The whole card is whitespace and type — and the one piece Tailwind has no utility for, the animated background-size underline, is a single arbitrary background value.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="tpc-06 w-full min-h-svh grid place-items-center bg-tpc06-bg font-tpc06-sans px-5 py-14" aria-label="Minimalist eCommerce product card demo">
  <article class="group w-80 max-w-full p-5 rounded-md bg-transparent transition-[background-color,box-shadow] duration-400 hover:bg-tpc06-card hover:shadow-[0_24px_60px_-34px_oklch(0.2_0.01_80/0.28)] focus-within:bg-tpc06-card focus-within:shadow-[0_24px_60px_-34px_oklch(0.2_0.01_80/0.28)] motion-reduce:transition-none">
    <div role="img" aria-label="Product photo: fine gold hoop earrings" class="aspect-[4/5] bg-linear-[160deg,var(--color-tpc06-ph),var(--color-tpc06-ph2)] bg-[url('https://images.unsplash.com/photo-1535632066927-ab7c9ab60908?w=700&h=880&fit=crop&q=70')] bg-cover bg-center"></div>
    <div class="pt-5.5 px-1 pb-1.5 text-center">
      <h3 class="font-tpc06-display text-[22px] font-normal tracking-[0.02em] text-tpc06-ink">Fine Gold Hoops</h3>
      <p class="mt-2 text-[13px] font-light tracking-[0.12em] text-tpc06-mut tabular-nums">$320</p>
      <a href="#fine-gold-hoops" class="inline-block mt-4.5 py-2 text-[11px] font-medium tracking-[0.22em] uppercase no-underline text-tpc06-ink bg-[linear-gradient(currentColor,currentColor)] bg-[position:0_100%] bg-[length:0_1px] bg-no-repeat transition-[background-size] duration-400 ease-out hover:bg-[length:100%_1px] focus-visible:bg-[length:100%_1px] focus-visible:outline-1 focus-visible:outline-dashed focus-visible:outline-tpc06-ink focus-visible:outline-offset-6 motion-reduce:transition-none">View product</a>
    </div>
  </article>
</section>
@import "tailwindcss";

@theme {
  --color-tpc06-bg: #faf9f6;
  --color-tpc06-bg: oklch(0.98 0.005 85);
  --color-tpc06-card: #ffffff;
  --color-tpc06-card: oklch(1 0 0);
  --color-tpc06-ink: #1a1a1a;
  --color-tpc06-ink: oklch(0.22 0.002 85);
  --color-tpc06-mut: #6e685e;
  --color-tpc06-mut: oklch(0.5 0.012 85);
  --color-tpc06-ph: #f0ece4;
  --color-tpc06-ph: oklch(0.94 0.01 85);
  --color-tpc06-ph2: #e6ded1;
  --color-tpc06-ph2: oklch(0.9 0.015 85);
  --font-tpc06-display: 'Cormorant Garamond', Georgia, serif;
  --font-tpc06-sans: 'Jost', system-ui, sans-serif;
}

.tpc-06 {
  width: 100%;
  min-height: 100svh;
}

.tpc-06 ::selection {
  background: var(--color-tpc06-ink);
  color: #fff;
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-tpc06-bg: #faf9f6;
  --color-tpc06-bg: oklch(0.98 0.005 85);
  --color-tpc06-card: #ffffff;
  --color-tpc06-card: oklch(1 0 0);
  --color-tpc06-ink: #1a1a1a;
  --color-tpc06-ink: oklch(0.22 0.002 85);
  --color-tpc06-mut: #6e685e;
  --color-tpc06-mut: oklch(0.5 0.012 85);
  --color-tpc06-ph: #f0ece4;
  --color-tpc06-ph: oklch(0.94 0.01 85);
  --color-tpc06-ph2: #e6ded1;
  --color-tpc06-ph2: oklch(0.9 0.015 85);
  --font-tpc06-display: 'Cormorant Garamond', Georgia, serif;
  --font-tpc06-sans: 'Jost', system-ui, sans-serif;
}

.tpc-06 {
  width: 100%;
  min-height: 100svh;
}

.tpc-06 ::selection {
  background: var(--color-tpc06-ink);
  color: #fff;
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;500;600&family=Jost:wght@300;400;500&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • The background shorthand in the CSS original (linear-gradient(...) 0 100%/0 1px no-repeat) splits into four explicit utilities. Longer, and strictly better: each axis of the trick is now separately editable and the animated one is named in transition-[background-size].
  • bg-[length:…] and bg-[position:…] show why v4's data-type hints exist — bg-[0_1px] alone cannot be disambiguated from a colour or an image.
  • The original's --muted: #8a8378 failed contrast on #faf9f6. Re-expressing it as an oklch token made the lightness explicit, and it moved to oklch(0.5 …) to clear 4.5:1.
  • focus-visible:outline-dashed replaces the original's outline: 1px dotted — the same intent, but as a real utility with an offset token rather than a hand-written outline.
  • Enhancement over the CSS original: the card now also responds to focus-within, so tabbing to the CTA materialises the same white card a mouse hover does, and the name moved to a display serif for a genuinely editorial read.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Minimalist eCommerce Product Card
Source: https://codefronts.com/components/tailwind-css-product-cards/tailwind-minimalist-ecommerce-product-card/

Restraint as the technique: no border, no shadow at rest, a Cormorant display name over a Jost micro-label, and a text CTA whose underline draws itself in from the left. The whole card is whitespace and type — and the one piece Tailwind has no utility for, the animated background-size underline, is a single arbitrary background value.
## HTML
```html
<section class="tpc-06 w-full min-h-svh grid place-items-center bg-tpc06-bg font-tpc06-sans px-5 py-14" aria-label="Minimalist eCommerce product card demo">
  <article class="group w-80 max-w-full p-5 rounded-md bg-transparent transition-[background-color,box-shadow] duration-400 hover:bg-tpc06-card hover:shadow-[0_24px_60px_-34px_oklch(0.2_0.01_80/0.28)] focus-within:bg-tpc06-card focus-within:shadow-[0_24px_60px_-34px_oklch(0.2_0.01_80/0.28)] motion-reduce:transition-none">
    <div role="img" aria-label="Product photo: fine gold hoop earrings" class="aspect-[4/5] bg-linear-[160deg,var(--color-tpc06-ph),var(--color-tpc06-ph2)] bg-[url('https://images.unsplash.com/photo-1535632066927-ab7c9ab60908?w=700&h=880&fit=crop&q=70')] bg-cover bg-center"></div>
    <div class="pt-5.5 px-1 pb-1.5 text-center">
      <h3 class="font-tpc06-display text-[22px] font-normal tracking-[0.02em] text-tpc06-ink">Fine Gold Hoops</h3>
      <p class="mt-2 text-[13px] font-light tracking-[0.12em] text-tpc06-mut tabular-nums">$320</p>
      <a href="#fine-gold-hoops" class="inline-block mt-4.5 py-2 text-[11px] font-medium tracking-[0.22em] uppercase no-underline text-tpc06-ink bg-[linear-gradient(currentColor,currentColor)] bg-[position:0_100%] bg-[length:0_1px] bg-no-repeat transition-[background-size] duration-400 ease-out hover:bg-[length:100%_1px] focus-visible:bg-[length:100%_1px] focus-visible:outline-1 focus-visible:outline-dashed focus-visible:outline-tpc06-ink focus-visible:outline-offset-6 motion-reduce:transition-none">View product</a>
    </div>
  </article>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tpc06-bg: #faf9f6;
  --color-tpc06-bg: oklch(0.98 0.005 85);
  --color-tpc06-card: #ffffff;
  --color-tpc06-card: oklch(1 0 0);
  --color-tpc06-ink: #1a1a1a;
  --color-tpc06-ink: oklch(0.22 0.002 85);
  --color-tpc06-mut: #6e685e;
  --color-tpc06-mut: oklch(0.5 0.012 85);
  --color-tpc06-ph: #f0ece4;
  --color-tpc06-ph: oklch(0.94 0.01 85);
  --color-tpc06-ph2: #e6ded1;
  --color-tpc06-ph2: oklch(0.9 0.015 85);
  --font-tpc06-display: 'Cormorant Garamond', Georgia, serif;
  --font-tpc06-sans: 'Jost', system-ui, sans-serif;
}

.tpc-06 {
  width: 100%;
  min-height: 100svh;
}

.tpc-06 ::selection {
  background: var(--color-tpc06-ink);
  color: #fff;
}
```

How this works

The underline is a background, not a border. A border cannot animate its width from the left; a background gradient can, because background-size is animatable:

<a class="bg-[linear-gradient(currentColor,currentColor)]
         bg-[position:0_100%] bg-[length:0_1px] bg-no-repeat
         transition-[background-size] duration-400
         hover:bg-[length:100%_1px] focus-visible:bg-[length:100%_1px]">

currentColor means the rule never needs a colour of its own — retint the text and the underline follows.

Hierarchy without weight. The product name is 400-weight at 20px in a display serif; the price is 300-weight and smaller; the CTA is 11px with enormous tracking. Nothing is bold, and the reading order is still unambiguous — which is the actual skill this pattern is teaching.

The card appears on hover. At rest there is no card: transparent background, no shadow, no border. group-hover: fades in a white fill and a very wide, very faint shadow, so the object materialises under the cursor instead of lifting.

Make it yours

  • Whitespace is the dial: the media's aspect-[4/5] and the body's pt-5.5. More space reads more expensive.
  • Keep weights at 300–500 and let tracking-* carry the tone. A bold weight anywhere breaks the effect.
  • Swap the underline for a sliding arrow: replace the background utilities with an inline → span and group-hover:translate-x-1.
  • Palette: --color-tpc06-ink and --color-tpc06-mut only. Saturated accents are what makes minimalist cards look cheap.
  • Centre or left-align the body block — centred reads editorial, left-aligned reads catalog.

Gotchas — read before shipping

  • Minimal still needs contrast. --color-tpc06-mut is tuned to stay above 4.5:1 on the warm off-white; lightening it further fails WCAG.
  • A text CTA must still be a real <a> or <button> with a visible focus state — underlined text with no focus style is a keyboard dead end.
  • bg-[length:…] and bg-[position:…] need the explicit data-type prefix. Bare bg-[0_1px] is ambiguous and compiles to nothing.
  • Resist adding badges, shadows and borders 'to help'. The value of this card is entirely what is left out.
  • Don't transition all here: the link has a gradient background, and transitioning it repaints the gradient on every frame.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Animated background-size underlines and subtle shadows are universally supported; the floor is Tailwind v4's generated sheet.

Techniques used in this demo

Theme tokensset in @theme, used by a utility

HTML & JSno Tailwind involved

Search CodeFronts

Loading…