13 / 18
All 18 in 18 Tailwind CSS Product Cards
- 01Tailwind Product Card with Hover-Reveal Add to Cart
- 02Pure-Tailwind 3D Flip Product Card with Spec Back
- 03Tailwind Contained Image Zoom Product Card
- 04Tailwind Mouse-Tracking 3D Tilt Product Card
- 05Tailwind Slide-Up Details Panel Product Card
- 06Tailwind Minimalist eCommerce Product Card
- 07Tailwind Glassmorphism Product Card
- 08Tailwind Dark-Mode Gadget Product Card with Accent Glow
- 09Tailwind Neon Glowing Conic-Border Product Card
- 10Tailwind Material-Design Elevation Product Card
- 11Tailwind Horizontal Product Card with Container Queries
- 12Tailwind Auto-Fit Product Catalog Grid
- 13Tailwind Sale Ribbon & Coupon Badge Product Card
- 14Tailwind Sneaker Card with Colourway & Size Picker
- 15Tailwind Digital Download Product Card
- 16Tailwind Food Delivery Card with Quantity Stepper
- 17Tailwind Real Estate Listing Card with Favourite Toggle
- 18Tailwind Subscription Pricing Card with Billing Switch
Tailwind Sale Ribbon & Coupon Badge Product Card
The clearance-page card: a diagonal ribbon wrapped across the top-right corner, a 'Best Seller' pill top-left, and a struck-through original beside the sale price. Every piece is positioning and a rotation, so the whole ribbon is absolute -right-[42px] top-[18px] w-[170px] rotate-45 and the card's overflow-hidden does the clipping.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tpc-13 w-full min-h-svh grid place-items-center bg-tpc13-bg font-tpc13-sans px-5 py-12" aria-label="Sale ribbon product card demo">
<article class="group relative w-[min(320px,100%)] rounded-2xl overflow-hidden bg-tpc13-card shadow-[0_16px_40px_-22px_oklch(0.25_0.03_260/0.45)]">
<span aria-hidden="true" class="absolute top-[18px] -right-[42px] z-2 w-[170px] rotate-45 text-center py-1.75 bg-tpc13-sale text-tpc13-onacc text-[12px] font-extrabold tracking-[0.06em] shadow-[0_6px_14px_-6px_oklch(0.2_0.02_260/0.4)] transition-shadow duration-300 group-hover:shadow-[0_10px_22px_-6px_oklch(0.2_0.02_260/0.55)] motion-reduce:transition-none">SALE −50%</span>
<span class="absolute top-3.5 left-3.5 z-2 px-3 py-1.25 rounded-full bg-tpc13-ink text-tpc13-onacc text-[11px] font-bold tracking-[0.04em]">Best Seller</span>
<div role="img" aria-label="Product photo: Halcyon bluetooth speaker" class="aspect-square bg-linear-[135deg,var(--color-tpc13-ph),var(--color-tpc13-ph2)] bg-[url('https://images.unsplash.com/photo-1589003077984-894e133dabab?w=700&h=700&fit=crop&q=70')] bg-cover bg-center"></div>
<div class="px-5 pt-4.5 pb-5">
<h3 class="text-[17px] font-semibold text-tpc13-ink text-pretty">Halcyon Bluetooth Speaker</h3>
<p class="mt-2 text-[13px] text-tpc13-mut" aria-label="Rated 4.8 out of 5 from 2,140 reviews"><span aria-hidden="true" class="text-tpc13-star tracking-[0.05em]">★★★★★</span> 4.8 (2,140)</p>
<div class="mt-3 flex items-baseline gap-2.5">
<span class="text-[15px] text-tpc13-was line-through tabular-nums">$120</span>
<span class="text-[24px] font-extrabold text-tpc13-sale tabular-nums">$60</span>
<span class="sr-only">50 percent off</span>
</div>
<p class="mt-3 inline-flex items-center gap-2 px-3 py-1.5 rounded-lg border border-dashed border-tpc13-edge font-tpc13-mono text-[11px] font-bold tracking-[0.12em] text-tpc13-ghostink">CODE: SAVE20</p>
<button type="button" class="mt-3.5 w-full min-h-11 h-11.5 rounded-xl border-0 cursor-pointer bg-tpc13-acc text-tpc13-onacc text-[15px] font-semibold transition-colors duration-150 hover:bg-tpc13-acc2 focus-visible:outline-3 focus-visible:outline-tpc13-ring focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
</section><section class="tpc-13 w-full min-h-svh grid place-items-center bg-tpc13-bg font-tpc13-sans px-5 py-12" aria-label="Sale ribbon product card demo">
<article class="group relative w-[min(320px,100%)] rounded-2xl overflow-hidden bg-tpc13-card shadow-[0_16px_40px_-22px_oklch(0.25_0.03_260/0.45)]">
<span aria-hidden="true" class="absolute top-[18px] -right-[42px] z-2 w-[170px] rotate-45 text-center py-1.75 bg-tpc13-sale text-tpc13-onacc text-[12px] font-extrabold tracking-[0.06em] shadow-[0_6px_14px_-6px_oklch(0.2_0.02_260/0.4)] transition-shadow duration-300 group-hover:shadow-[0_10px_22px_-6px_oklch(0.2_0.02_260/0.55)] motion-reduce:transition-none">SALE −50%</span>
<span class="absolute top-3.5 left-3.5 z-2 px-3 py-1.25 rounded-full bg-tpc13-ink text-tpc13-onacc text-[11px] font-bold tracking-[0.04em]">Best Seller</span>
<div role="img" aria-label="Product photo: Halcyon bluetooth speaker" class="aspect-square bg-linear-[135deg,var(--color-tpc13-ph),var(--color-tpc13-ph2)] bg-[url('https://images.unsplash.com/photo-1589003077984-894e133dabab?w=700&h=700&fit=crop&q=70')] bg-cover bg-center"></div>
<div class="px-5 pt-4.5 pb-5">
<h3 class="text-[17px] font-semibold text-tpc13-ink text-pretty">Halcyon Bluetooth Speaker</h3>
<p class="mt-2 text-[13px] text-tpc13-mut" aria-label="Rated 4.8 out of 5 from 2,140 reviews"><span aria-hidden="true" class="text-tpc13-star tracking-[0.05em]">★★★★★</span> 4.8 (2,140)</p>
<div class="mt-3 flex items-baseline gap-2.5">
<span class="text-[15px] text-tpc13-was line-through tabular-nums">$120</span>
<span class="text-[24px] font-extrabold text-tpc13-sale tabular-nums">$60</span>
<span class="sr-only">50 percent off</span>
</div>
<p class="mt-3 inline-flex items-center gap-2 px-3 py-1.5 rounded-lg border border-dashed border-tpc13-edge font-tpc13-mono text-[11px] font-bold tracking-[0.12em] text-tpc13-ghostink">CODE: SAVE20</p>
<button type="button" class="mt-3.5 w-full min-h-11 h-11.5 rounded-xl border-0 cursor-pointer bg-tpc13-acc text-tpc13-onacc text-[15px] font-semibold transition-colors duration-150 hover:bg-tpc13-acc2 focus-visible:outline-3 focus-visible:outline-tpc13-ring focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
</section>@import "tailwindcss";
@theme {
--color-tpc13-bg: #f1f2f6;
--color-tpc13-bg: oklch(0.95 0.005 265);
--color-tpc13-card: #ffffff;
--color-tpc13-card: oklch(1 0 0);
--color-tpc13-ink: #111827;
--color-tpc13-ink: oklch(0.23 0.03 265);
--color-tpc13-mut: #5b6478;
--color-tpc13-mut: oklch(0.5 0.025 265);
--color-tpc13-ghostink: #3f4657;
--color-tpc13-ghostink: oklch(0.42 0.025 265);
--color-tpc13-was: #7d879a;
--color-tpc13-was: oklch(0.62 0.02 265);
--color-tpc13-edge: #c8cdd8;
--color-tpc13-edge: oklch(0.84 0.012 265);
--color-tpc13-ph: #e4e7ee;
--color-tpc13-ph: oklch(0.92 0.008 265);
--color-tpc13-ph2: #d3d8e4;
--color-tpc13-ph2: oklch(0.87 0.014 265);
--color-tpc13-sale: #c2341f;
--color-tpc13-sale: oklch(0.55 0.18 28);
--color-tpc13-acc: #3a55b0;
--color-tpc13-acc: oklch(0.5 0.15 262);
--color-tpc13-acc2: #2f4693;
--color-tpc13-acc2: oklch(0.44 0.14 262);
--color-tpc13-star: #d8a01f;
--color-tpc13-star: oklch(0.75 0.14 82);
--color-tpc13-ring: #a9b8e6;
--color-tpc13-ring: oklch(0.78 0.08 262);
--color-tpc13-onacc: #ffffff;
--color-tpc13-onacc: oklch(1 0 0);
--font-tpc13-sans: 'Archivo', system-ui, sans-serif;
--font-tpc13-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tpc-13 {
width: 100%;
min-height: 100svh;
}
.tpc-13 ::selection {
background: var(--color-tpc13-sale);
color: #fff;
}@import "tailwindcss";
@theme {
--color-tpc13-bg: #f1f2f6;
--color-tpc13-bg: oklch(0.95 0.005 265);
--color-tpc13-card: #ffffff;
--color-tpc13-card: oklch(1 0 0);
--color-tpc13-ink: #111827;
--color-tpc13-ink: oklch(0.23 0.03 265);
--color-tpc13-mut: #5b6478;
--color-tpc13-mut: oklch(0.5 0.025 265);
--color-tpc13-ghostink: #3f4657;
--color-tpc13-ghostink: oklch(0.42 0.025 265);
--color-tpc13-was: #7d879a;
--color-tpc13-was: oklch(0.62 0.02 265);
--color-tpc13-edge: #c8cdd8;
--color-tpc13-edge: oklch(0.84 0.012 265);
--color-tpc13-ph: #e4e7ee;
--color-tpc13-ph: oklch(0.92 0.008 265);
--color-tpc13-ph2: #d3d8e4;
--color-tpc13-ph2: oklch(0.87 0.014 265);
--color-tpc13-sale: #c2341f;
--color-tpc13-sale: oklch(0.55 0.18 28);
--color-tpc13-acc: #3a55b0;
--color-tpc13-acc: oklch(0.5 0.15 262);
--color-tpc13-acc2: #2f4693;
--color-tpc13-acc2: oklch(0.44 0.14 262);
--color-tpc13-star: #d8a01f;
--color-tpc13-star: oklch(0.75 0.14 82);
--color-tpc13-ring: #a9b8e6;
--color-tpc13-ring: oklch(0.78 0.08 262);
--color-tpc13-onacc: #ffffff;
--color-tpc13-onacc: oklch(1 0 0);
--font-tpc13-sans: 'Archivo', system-ui, sans-serif;
--font-tpc13-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tpc-13 {
width: 100%;
min-height: 100svh;
}
.tpc-13 ::selection {
background: var(--color-tpc13-sale);
color: #fff;
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<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.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tpc13-bg: #f1f2f6;
--color-tpc13-bg: oklch(0.95 0.005 265);
--color-tpc13-card: #ffffff;
--color-tpc13-card: oklch(1 0 0);
--color-tpc13-ink: #111827;
--color-tpc13-ink: oklch(0.23 0.03 265);
--color-tpc13-mut: #5b6478;
--color-tpc13-mut: oklch(0.5 0.025 265);
--color-tpc13-ghostink: #3f4657;
--color-tpc13-ghostink: oklch(0.42 0.025 265);
--color-tpc13-was: #7d879a;
--color-tpc13-was: oklch(0.62 0.02 265);
--color-tpc13-edge: #c8cdd8;
--color-tpc13-edge: oklch(0.84 0.012 265);
--color-tpc13-ph: #e4e7ee;
--color-tpc13-ph: oklch(0.92 0.008 265);
--color-tpc13-ph2: #d3d8e4;
--color-tpc13-ph2: oklch(0.87 0.014 265);
--color-tpc13-sale: #c2341f;
--color-tpc13-sale: oklch(0.55 0.18 28);
--color-tpc13-acc: #3a55b0;
--color-tpc13-acc: oklch(0.5 0.15 262);
--color-tpc13-acc2: #2f4693;
--color-tpc13-acc2: oklch(0.44 0.14 262);
--color-tpc13-star: #d8a01f;
--color-tpc13-star: oklch(0.75 0.14 82);
--color-tpc13-ring: #a9b8e6;
--color-tpc13-ring: oklch(0.78 0.08 262);
--color-tpc13-onacc: #ffffff;
--color-tpc13-onacc: oklch(1 0 0);
--font-tpc13-sans: 'Archivo', system-ui, sans-serif;
--font-tpc13-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tpc-13 {
width: 100%;
min-height: 100svh;
}
.tpc-13 ::selection {
background: var(--color-tpc13-sale);
color: #fff;
}@import "tailwindcss";
@theme {
--color-tpc13-bg: #f1f2f6;
--color-tpc13-bg: oklch(0.95 0.005 265);
--color-tpc13-card: #ffffff;
--color-tpc13-card: oklch(1 0 0);
--color-tpc13-ink: #111827;
--color-tpc13-ink: oklch(0.23 0.03 265);
--color-tpc13-mut: #5b6478;
--color-tpc13-mut: oklch(0.5 0.025 265);
--color-tpc13-ghostink: #3f4657;
--color-tpc13-ghostink: oklch(0.42 0.025 265);
--color-tpc13-was: #7d879a;
--color-tpc13-was: oklch(0.62 0.02 265);
--color-tpc13-edge: #c8cdd8;
--color-tpc13-edge: oklch(0.84 0.012 265);
--color-tpc13-ph: #e4e7ee;
--color-tpc13-ph: oklch(0.92 0.008 265);
--color-tpc13-ph2: #d3d8e4;
--color-tpc13-ph2: oklch(0.87 0.014 265);
--color-tpc13-sale: #c2341f;
--color-tpc13-sale: oklch(0.55 0.18 28);
--color-tpc13-acc: #3a55b0;
--color-tpc13-acc: oklch(0.5 0.15 262);
--color-tpc13-acc2: #2f4693;
--color-tpc13-acc2: oklch(0.44 0.14 262);
--color-tpc13-star: #d8a01f;
--color-tpc13-star: oklch(0.75 0.14 82);
--color-tpc13-ring: #a9b8e6;
--color-tpc13-ring: oklch(0.78 0.08 262);
--color-tpc13-onacc: #ffffff;
--color-tpc13-onacc: oklch(1 0 0);
--font-tpc13-sans: 'Archivo', system-ui, sans-serif;
--font-tpc13-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tpc-13 {
width: 100%;
min-height: 100svh;
}
.tpc-13 ::selection {
background: var(--color-tpc13-sale);
color: #fff;
}<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=Archivo:wght@400;500;600;700;800&family=JetBrains+Mono:wght@500;700&display=swap">
<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=Archivo:wght@400;500;600;700;800&family=JetBrains+Mono:wght@500;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The whole ribbon rule — position, offsets, width, rotation, centring — becomes one class list on the band itself. This is the port where reading the markup tells you more than reading the CSS did: the two numbers that control ribbon length sit side by side.
z-2is v4's arbitrary-free z-index scale; the CSS original'sz-index:2needed no translation.- The fake ribbon 'fold' pseudo-elements from the original were dropped rather than ported. They were two more rules for an effect the flat band already reads as, and
::before/::aftercontent is exactly where utility markup stops being clearer. - Enhancement over the CSS original: the dashed coupon chip the original listed as a customisation is shipped, the discount gained an
sr-onlypercentage so AT users get the saving as text, and the ribbon's shadow now deepens withgroup-hover:instead of sitting inert.
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 Sale Ribbon & Coupon Badge Product Card
Source: https://codefronts.com/components/tailwind-css-product-cards/tailwind-sale-ribbon-coupon-badge-product-card/
The clearance-page card: a diagonal ribbon wrapped across the top-right corner, a 'Best Seller' pill top-left, and a struck-through original beside the sale price. Every piece is positioning and a rotation, so the whole ribbon is absolute -right-[42px] top-[18px] w-[170px] rotate-45 and the card's overflow-hidden does the clipping.
## HTML
```html
<section class="tpc-13 w-full min-h-svh grid place-items-center bg-tpc13-bg font-tpc13-sans px-5 py-12" aria-label="Sale ribbon product card demo">
<article class="group relative w-[min(320px,100%)] rounded-2xl overflow-hidden bg-tpc13-card shadow-[0_16px_40px_-22px_oklch(0.25_0.03_260/0.45)]">
<span aria-hidden="true" class="absolute top-[18px] -right-[42px] z-2 w-[170px] rotate-45 text-center py-1.75 bg-tpc13-sale text-tpc13-onacc text-[12px] font-extrabold tracking-[0.06em] shadow-[0_6px_14px_-6px_oklch(0.2_0.02_260/0.4)] transition-shadow duration-300 group-hover:shadow-[0_10px_22px_-6px_oklch(0.2_0.02_260/0.55)] motion-reduce:transition-none">SALE −50%</span>
<span class="absolute top-3.5 left-3.5 z-2 px-3 py-1.25 rounded-full bg-tpc13-ink text-tpc13-onacc text-[11px] font-bold tracking-[0.04em]">Best Seller</span>
<div role="img" aria-label="Product photo: Halcyon bluetooth speaker" class="aspect-square bg-linear-[135deg,var(--color-tpc13-ph),var(--color-tpc13-ph2)] bg-[url('https://images.unsplash.com/photo-1589003077984-894e133dabab?w=700&h=700&fit=crop&q=70')] bg-cover bg-center"></div>
<div class="px-5 pt-4.5 pb-5">
<h3 class="text-[17px] font-semibold text-tpc13-ink text-pretty">Halcyon Bluetooth Speaker</h3>
<p class="mt-2 text-[13px] text-tpc13-mut" aria-label="Rated 4.8 out of 5 from 2,140 reviews"><span aria-hidden="true" class="text-tpc13-star tracking-[0.05em]">★★★★★</span> 4.8 (2,140)</p>
<div class="mt-3 flex items-baseline gap-2.5">
<span class="text-[15px] text-tpc13-was line-through tabular-nums">$120</span>
<span class="text-[24px] font-extrabold text-tpc13-sale tabular-nums">$60</span>
<span class="sr-only">50 percent off</span>
</div>
<p class="mt-3 inline-flex items-center gap-2 px-3 py-1.5 rounded-lg border border-dashed border-tpc13-edge font-tpc13-mono text-[11px] font-bold tracking-[0.12em] text-tpc13-ghostink">CODE: SAVE20</p>
<button type="button" class="mt-3.5 w-full min-h-11 h-11.5 rounded-xl border-0 cursor-pointer bg-tpc13-acc text-tpc13-onacc text-[15px] font-semibold transition-colors duration-150 hover:bg-tpc13-acc2 focus-visible:outline-3 focus-visible:outline-tpc13-ring focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tpc13-bg: #f1f2f6;
--color-tpc13-bg: oklch(0.95 0.005 265);
--color-tpc13-card: #ffffff;
--color-tpc13-card: oklch(1 0 0);
--color-tpc13-ink: #111827;
--color-tpc13-ink: oklch(0.23 0.03 265);
--color-tpc13-mut: #5b6478;
--color-tpc13-mut: oklch(0.5 0.025 265);
--color-tpc13-ghostink: #3f4657;
--color-tpc13-ghostink: oklch(0.42 0.025 265);
--color-tpc13-was: #7d879a;
--color-tpc13-was: oklch(0.62 0.02 265);
--color-tpc13-edge: #c8cdd8;
--color-tpc13-edge: oklch(0.84 0.012 265);
--color-tpc13-ph: #e4e7ee;
--color-tpc13-ph: oklch(0.92 0.008 265);
--color-tpc13-ph2: #d3d8e4;
--color-tpc13-ph2: oklch(0.87 0.014 265);
--color-tpc13-sale: #c2341f;
--color-tpc13-sale: oklch(0.55 0.18 28);
--color-tpc13-acc: #3a55b0;
--color-tpc13-acc: oklch(0.5 0.15 262);
--color-tpc13-acc2: #2f4693;
--color-tpc13-acc2: oklch(0.44 0.14 262);
--color-tpc13-star: #d8a01f;
--color-tpc13-star: oklch(0.75 0.14 82);
--color-tpc13-ring: #a9b8e6;
--color-tpc13-ring: oklch(0.78 0.08 262);
--color-tpc13-onacc: #ffffff;
--color-tpc13-onacc: oklch(1 0 0);
--font-tpc13-sans: 'Archivo', system-ui, sans-serif;
--font-tpc13-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tpc-13 {
width: 100%;
min-height: 100svh;
}
.tpc-13 ::selection {
background: var(--color-tpc13-sale);
color: #fff;
}
```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 Sale Ribbon & Coupon Badge Product Card
Source: https://codefronts.com/components/tailwind-css-product-cards/tailwind-sale-ribbon-coupon-badge-product-card/
The clearance-page card: a diagonal ribbon wrapped across the top-right corner, a 'Best Seller' pill top-left, and a struck-through original beside the sale price. Every piece is positioning and a rotation, so the whole ribbon is absolute -right-[42px] top-[18px] w-[170px] rotate-45 and the card's overflow-hidden does the clipping.
## HTML
```html
<section class="tpc-13 w-full min-h-svh grid place-items-center bg-tpc13-bg font-tpc13-sans px-5 py-12" aria-label="Sale ribbon product card demo">
<article class="group relative w-[min(320px,100%)] rounded-2xl overflow-hidden bg-tpc13-card shadow-[0_16px_40px_-22px_oklch(0.25_0.03_260/0.45)]">
<span aria-hidden="true" class="absolute top-[18px] -right-[42px] z-2 w-[170px] rotate-45 text-center py-1.75 bg-tpc13-sale text-tpc13-onacc text-[12px] font-extrabold tracking-[0.06em] shadow-[0_6px_14px_-6px_oklch(0.2_0.02_260/0.4)] transition-shadow duration-300 group-hover:shadow-[0_10px_22px_-6px_oklch(0.2_0.02_260/0.55)] motion-reduce:transition-none">SALE −50%</span>
<span class="absolute top-3.5 left-3.5 z-2 px-3 py-1.25 rounded-full bg-tpc13-ink text-tpc13-onacc text-[11px] font-bold tracking-[0.04em]">Best Seller</span>
<div role="img" aria-label="Product photo: Halcyon bluetooth speaker" class="aspect-square bg-linear-[135deg,var(--color-tpc13-ph),var(--color-tpc13-ph2)] bg-[url('https://images.unsplash.com/photo-1589003077984-894e133dabab?w=700&h=700&fit=crop&q=70')] bg-cover bg-center"></div>
<div class="px-5 pt-4.5 pb-5">
<h3 class="text-[17px] font-semibold text-tpc13-ink text-pretty">Halcyon Bluetooth Speaker</h3>
<p class="mt-2 text-[13px] text-tpc13-mut" aria-label="Rated 4.8 out of 5 from 2,140 reviews"><span aria-hidden="true" class="text-tpc13-star tracking-[0.05em]">★★★★★</span> 4.8 (2,140)</p>
<div class="mt-3 flex items-baseline gap-2.5">
<span class="text-[15px] text-tpc13-was line-through tabular-nums">$120</span>
<span class="text-[24px] font-extrabold text-tpc13-sale tabular-nums">$60</span>
<span class="sr-only">50 percent off</span>
</div>
<p class="mt-3 inline-flex items-center gap-2 px-3 py-1.5 rounded-lg border border-dashed border-tpc13-edge font-tpc13-mono text-[11px] font-bold tracking-[0.12em] text-tpc13-ghostink">CODE: SAVE20</p>
<button type="button" class="mt-3.5 w-full min-h-11 h-11.5 rounded-xl border-0 cursor-pointer bg-tpc13-acc text-tpc13-onacc text-[15px] font-semibold transition-colors duration-150 hover:bg-tpc13-acc2 focus-visible:outline-3 focus-visible:outline-tpc13-ring focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tpc13-bg: #f1f2f6;
--color-tpc13-bg: oklch(0.95 0.005 265);
--color-tpc13-card: #ffffff;
--color-tpc13-card: oklch(1 0 0);
--color-tpc13-ink: #111827;
--color-tpc13-ink: oklch(0.23 0.03 265);
--color-tpc13-mut: #5b6478;
--color-tpc13-mut: oklch(0.5 0.025 265);
--color-tpc13-ghostink: #3f4657;
--color-tpc13-ghostink: oklch(0.42 0.025 265);
--color-tpc13-was: #7d879a;
--color-tpc13-was: oklch(0.62 0.02 265);
--color-tpc13-edge: #c8cdd8;
--color-tpc13-edge: oklch(0.84 0.012 265);
--color-tpc13-ph: #e4e7ee;
--color-tpc13-ph: oklch(0.92 0.008 265);
--color-tpc13-ph2: #d3d8e4;
--color-tpc13-ph2: oklch(0.87 0.014 265);
--color-tpc13-sale: #c2341f;
--color-tpc13-sale: oklch(0.55 0.18 28);
--color-tpc13-acc: #3a55b0;
--color-tpc13-acc: oklch(0.5 0.15 262);
--color-tpc13-acc2: #2f4693;
--color-tpc13-acc2: oklch(0.44 0.14 262);
--color-tpc13-star: #d8a01f;
--color-tpc13-star: oklch(0.75 0.14 82);
--color-tpc13-ring: #a9b8e6;
--color-tpc13-ring: oklch(0.78 0.08 262);
--color-tpc13-onacc: #ffffff;
--color-tpc13-onacc: oklch(1 0 0);
--font-tpc13-sans: 'Archivo', system-ui, sans-serif;
--font-tpc13-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tpc-13 {
width: 100%;
min-height: 100svh;
}
.tpc-13 ::selection {
background: var(--color-tpc13-sale);
color: #fff;
}
```How this works
A ribbon is a rotated box plus a clip.
<article class="relative overflow-hidden rounded-2xl">
<span class="absolute top-[18px] -right-[42px] z-2 w-[170px] rotate-45 text-center">SALE −50%</span>The band is wider than the corner it crosses and hangs off the right edge; the card's overflow-hidden trims both ends flush. Change the width or the offset and the ribbon's apparent length changes — they are the two numbers to tune, and here they sit inches from each other in the same class list.
The badge system is one recipe. Pill, ribbon and coupon chip all share rounded-full (or rounded-lg), a tight uppercase tracking and an 11px size, so three different promo mechanics read as one family.
The discount is text, not decoration. The ribbon is aria-hidden flavour; the actual saving lives in the price line, where a screen reader finds it: an original with line-through, the sale price, and a labelled percentage.
Make it yours
- Ribbon colour:
--color-tpc13-sale. Red reads clearance, green reads new, black reads premium drop. - Mirror it to the top-left with
-left-[42px] -rotate-45. - Flat tag instead: drop the
rotate-45and useleft-0 top-4 rounded-r-full. - Coupon chip: swap the code text, or delete the chip entirely — nothing else depends on it.
- Keep ribbon copy under about 12 characters;
w-[170px]is the budget.
Gotchas — read before shipping
- Without
overflow-hiddenon the card the rotated band sticks out past the corner and breaks the grid it sits in. overflow-hiddenalso clips any hover lift shadow drawn inside the card — put the shadow on the card itself, as here.- Badges must not be the only carrier of the discount. The struck price and the percentage text are what AT users get.
- Rotated text in a narrow band is the first thing to break at 200% zoom — test the ribbon copy at large text sizes.
z-2matters: the ribbon has to sit above the media layer, not under it.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
A rotated absolute band clipped by overflow is universally supported; the floor quoted is Tailwind v4's own generated sheet.