Product Cards
Ported from HyperUI’s Product Cards — see Attribution.
Product with image, title and price
Section titled “Product with image, title and price”const _photoA = 'https://images.unsplash.com/photo-1523381210434-271e8be1f52b?auto=format&fit=crop&q=80&w=1160';const _photoB = 'https://images.unsplash.com/photo-1523381140794-a1eef18a37c7?auto=format&fit=crop&q=80&w=1160';
class ProductCards1Example extends StatelessWidget { const ProductCards1Example({super.key});
@override Widget build(BuildContext context) { return Style( 'mx-auto max-w-md p-6', child: Style( 'group block overflow-hidden', cursor: SystemMouseCursors.click, children: [ Style( 'relative h-87.5 sm:h-112.5', children: [ Style( 'absolute inset-0 h-full w-full object-cover opacity-100 group-hover:opacity-0', child: Image.network(_photoA, fit: BoxFit.cover), ), Style( 'absolute inset-0 h-full w-full object-cover opacity-0 group-hover:opacity-100', child: Image.network(_photoB, fit: BoxFit.cover), ), ], ), Style( 'relative bg-white pt-3', children: [ const Style( 'text-sm text-gray-700 group-hover:underline group-hover:underline-offset-4', child: Text('Limited Edition Sports Trainer'), ), const Style( 'mt-1.5 tracking-wide text-gray-900', child: Text(r'$189.99'), ), ], ), ], ), ); }}<a href="#" class="group block overflow-hidden"> <div class="relative h-87.5 sm:h-112.5"> <img src="https://images.unsplash.com/photo-1523381210434-271e8be1f52b?auto=format&fit=crop&q=80&w=1160" alt="" class="absolute inset-0 h-full w-full object-cover opacity-100 group-hover:opacity-0" />
<img src="https://images.unsplash.com/photo-1523381140794-a1eef18a37c7?auto=format&fit=crop&q=80&w=1160" alt="" class="absolute inset-0 h-full w-full object-cover opacity-0 group-hover:opacity-100" /> </div>
<div class="relative bg-white pt-3"> <h3 class="text-sm text-gray-700 group-hover:underline group-hover:underline-offset-4"> Limited Edition Sports Trainer </h3>
<p class="mt-1.5 tracking-wide text-gray-900">$189.99</p> </div></a>Product with image, title, price and variant count
Section titled “Product with image, title, price and variant count”const _photoA = 'https://images.unsplash.com/photo-1600185365483-26d7a4cc7519?auto=format&fit=crop&q=80&w=1160';const _photoB = 'https://images.unsplash.com/photo-1600185365926-3a2ce3cdb9eb?auto=format&fit=crop&q=80&w=1160';
class ProductCards2Example extends StatelessWidget { const ProductCards2Example({super.key});
@override Widget build(BuildContext context) { return Style( 'mx-auto max-w-md p-6', child: Style( 'group block overflow-hidden', cursor: SystemMouseCursors.click, children: [ Style( 'relative h-87.5 sm:h-112.5', children: [ Style( 'absolute inset-0 h-full w-full object-cover opacity-100 group-hover:opacity-0', child: Image.network(_photoA, fit: BoxFit.cover), ), Style( 'absolute inset-0 h-full w-full object-cover opacity-0 group-hover:opacity-100', child: Image.network(_photoB, fit: BoxFit.cover), ), ], ), Style( 'relative bg-white pt-3', children: [ const Style( 'text-sm text-gray-700 group-hover:underline group-hover:underline-offset-4', child: Text('Limited Edition Sports Trainer'), ), Style( 'mt-1.5 flex items-center justify-between text-gray-900', children: const [ Style('tracking-wide', child: Text(r'$189.99')), Style( 'text-xs tracking-wide uppercase', child: Text('6 Colors'), ), ], ), ], ), ], ), ); }}<a href="#" class="group block overflow-hidden"> <div class="relative h-87.5 sm:h-112.5"> <img src="https://images.unsplash.com/photo-1600185365483-26d7a4cc7519?auto=format&fit=crop&q=80&w=1160" alt="" class="absolute inset-0 h-full w-full object-cover opacity-100 group-hover:opacity-0" />
<img src="https://images.unsplash.com/photo-1600185365926-3a2ce3cdb9eb?auto=format&fit=crop&q=80&w=1160" alt="" class="absolute inset-0 h-full w-full object-cover opacity-0 group-hover:opacity-100" /> </div>
<div class="relative bg-white pt-3"> <h3 class="text-sm text-gray-700 group-hover:underline group-hover:underline-offset-4"> Limited Edition Sports Trainer </h3>
<div class="mt-1.5 flex items-center justify-between text-gray-900"> <p class="tracking-wide">$189.99</p>
<p class="text-xs tracking-wide uppercase">6 Colors</p> </div> </div></a>Product with image, title, price and description
Section titled “Product with image, title, price and description”const _photoUrl = 'https://images.unsplash.com/photo-1592921870789-04563d55041c?auto=format&fit=crop&q=80&w=1160';
class ProductCards3Example extends StatelessWidget { const ProductCards3Example({super.key});
@override Widget build(BuildContext context) { return Style( 'mx-auto max-w-md p-6', child: Style( 'group block', cursor: SystemMouseCursors.click, children: [ Style( 'h-87.5 w-full object-cover sm:h-112.5', child: Image.network(_photoUrl, fit: BoxFit.cover), ), Style( 'mt-3 flex justify-between text-sm', children: [ Style.empty( children: [ const Style( 'text-gray-900 group-hover:underline group-hover:underline-offset-4', child: Text('Small Headphones'), ), const Style( 'mt-1.5 text-xs text-pretty text-gray-500', child: Text( 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Quasi nobis, quia ' 'soluta quisquam voluptatem nemo.', ), ), ], ), const Style('text-gray-900', child: Text(r'$299')), ], ), ], ), ); }}<a href="#" class="group block"> <img src="https://images.unsplash.com/photo-1592921870789-04563d55041c?auto=format&fit=crop&q=80&w=1160" alt="" class="h-87.5 w-full object-cover sm:h-112.5" />
<div class="mt-3 flex justify-between text-sm"> <div> <h3 class="text-gray-900 group-hover:underline group-hover:underline-offset-4"> Small Headphones </h3>
<p class="mt-1.5 text-xs text-pretty text-gray-500"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Quasi nobis, quia soluta quisquam voluptatem nemo. </p> </div>
<p class="text-gray-900">$299</p> </div></a>Product with rounded image, title and price
Section titled “Product with rounded image, title and price”const _photoUrl = 'https://images.unsplash.com/photo-1592921870789-04563d55041c?auto=format&fit=crop&q=80&w=1160';
class ProductCards4Example extends StatelessWidget { const ProductCards4Example({super.key});
@override Widget build(BuildContext context) { return Style( 'mx-auto max-w-md p-6', child: Style( 'group block', cursor: SystemMouseCursors.click, children: [ Style( 'aspect-square w-full rounded-sm object-cover', child: Image.network(_photoUrl, fit: BoxFit.cover), ), Style( 'mt-3', children: [ const Style( 'font-medium text-gray-900 group-hover:underline group-hover:underline-offset-4', child: Text('Simple Watch'), ), const Style('mt-1 text-sm text-gray-700', child: Text(r'$150')), ], ), ], ), ); }}<a href="#" class="group block"> <img src="https://images.unsplash.com/photo-1592921870789-04563d55041c?auto=format&fit=crop&q=80&w=1160" alt="" class="aspect-square w-full rounded-sm object-cover" />
<div class="mt-3"> <h3 class="font-medium text-gray-900 group-hover:underline group-hover:underline-offset-4"> Simple Watch </h3>
<p class="mt-1 text-sm text-gray-700">$150</p> </div></a>Product with image, title, price and variant options
Section titled “Product with image, title, price and variant options”const _photoUrl = 'https://images.unsplash.com/photo-1592921870789-04563d55041c?auto=format&fit=crop&q=80&w=1160';
class ProductCards5Example extends StatelessWidget { const ProductCards5Example({super.key});
@override Widget build(BuildContext context) { return Style( 'mx-auto max-w-md p-6', child: Style( 'group block', cursor: SystemMouseCursors.click, children: [ Style( 'h-87.5 w-full object-cover sm:h-112.5', child: Image.network(_photoUrl, fit: BoxFit.cover), ), Style( 'mt-1.5', children: [ const Style('text-xs text-gray-500', child: Text('Space Grey')), Style( 'mt-1.5 flex gap-1', children: [ Style.empty( children: [ Style.empty( child: const Style('sr-only', child: Text('Color')), ), Style( 'flex flex-wrap justify-center gap-1', children: [ Style.empty( children: [ const Style('sr-only', child: SizedBox.shrink()), Style( 'block size-4 cursor-pointer rounded-full bg-[#595759]', cursor: SystemMouseCursors.click, child: const Style( 'sr-only', child: Text('Space Gray'), ), ), ], ), Style.empty( children: [ const Style('sr-only', child: SizedBox.shrink()), Style( 'block size-4 cursor-pointer rounded-full bg-[#d2d3d4]', cursor: SystemMouseCursors.click, child: const Style( 'sr-only', child: Text('Silver'), ), ), ], ), Style.empty( children: [ const Style('sr-only', child: SizedBox.shrink()), Style( 'block size-4 cursor-pointer rounded-full bg-[#d89f97]', cursor: SystemMouseCursors.click, child: const Style( 'sr-only', child: Text('Pink'), ), ), ], ), Style.empty( children: [ const Style('sr-only', child: SizedBox.shrink()), Style( 'block size-4 cursor-pointer rounded-full bg-[#afbfab]', cursor: SystemMouseCursors.click, child: const Style( 'sr-only', child: Text('Pink'), ), ), ], ), Style.empty( children: [ const Style('sr-only', child: SizedBox.shrink()), Style( 'block size-4 cursor-pointer rounded-full bg-[#91a5bb]', cursor: SystemMouseCursors.click, child: const Style( 'sr-only', child: Text('Pink'), ), ), ], ), ], ), ], ), ], ), Style( 'mt-3 flex justify-between text-sm', children: const [ Style( 'text-gray-900 group-hover:underline group-hover:underline-offset-4', child: Text('Small Headphones'), ), Style('text-gray-900', child: Text(r'$299')), ], ), ], ), ], ), ); }}<a href="#" class="group block"> <img src="https://images.unsplash.com/photo-1592921870789-04563d55041c?auto=format&fit=crop&q=80&w=1160" alt="" class="h-87.5 w-full object-cover sm:h-112.5" />
<div class="mt-1.5"> <p class="text-xs text-gray-500">Space Grey</p>
<div class="mt-1.5 flex gap-1"> <form> <fieldset> <legend class="sr-only">Color</legend> </fieldset>
<div class="flex flex-wrap justify-center gap-1"> <div> <input type="checkbox" id="ColorSg" class="sr-only" />
<label for="ColorSg" class="block size-4 cursor-pointer rounded-full bg-[#595759]"> <span class="sr-only"> Space Gray </span> </label> </div>
<div> <input type="checkbox" id="ColorS" class="sr-only" />
<label for="ColorS" class="block size-4 cursor-pointer rounded-full bg-[#d2d3d4]"> <span class="sr-only"> Silver </span> </label> </div>
<div> <input type="checkbox" id="ColorP" class="sr-only" />
<label for="ColorP" class="block size-4 cursor-pointer rounded-full bg-[#d89f97]"> <span class="sr-only"> Pink </span> </label> </div>
<div> <input type="checkbox" id="ColorG" class="sr-only" />
<label for="ColorG" class="block size-4 cursor-pointer rounded-full bg-[#afbfab]"> <span class="sr-only"> Pink </span> </label> </div>
<div> <input type="checkbox" id="ColorSb" class="sr-only" />
<label for="ColorSb" class="block size-4 cursor-pointer rounded-full bg-[#91a5bb]"> <span class="sr-only"> Pink </span> </label> </div> </div> </form> </div>
<div class="mt-3 flex justify-between text-sm"> <h3 class="text-gray-900 group-hover:underline group-hover:underline-offset-4"> Small Headphones </h3>
<p class="text-gray-900">$299</p> </div> </div></a>Product with image, title, price, tag, wishlist and call to action
Section titled “Product with image, title, price, tag, wishlist and call to action”class CurrentColorIcon extends StatelessWidget implements StyleReplacedElementWrapper { const CurrentColorIcon(this.svg, {super.key});
final String svg;
@override Widget build(BuildContext context) { final color = IconTheme.of(context).color ?? const Color(0xFF000000); return SvgPicture.string(svg, theme: SvgTheme(currentColor: color)); }}
const _photoUrl = 'https://images.unsplash.com/photo-1599481238640-4c1288750d7a?auto=format&fit=crop&q=80&w=1160';
const _heartSvg = '''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z"/></svg>''';
class ProductCards6Example extends StatelessWidget { const ProductCards6Example({super.key});
@override Widget build(BuildContext context) { return Style( 'mx-auto max-w-md p-6', child: Style( 'group relative block overflow-hidden', cursor: SystemMouseCursors.click, children: [ Style( 'absolute end-4 top-4 z-10 rounded-full bg-white p-1.5 text-gray-900 transition hover:text-gray-900/75', cursor: SystemMouseCursors.click, children: [ const Style('sr-only', child: Text('Wishlist')), const Style('size-4', child: CurrentColorIcon(_heartSvg)), ], ), Style( 'h-64 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-72', child: Image.network(_photoUrl, fit: BoxFit.cover), ), Style( 'relative border border-gray-100 bg-white p-6', children: [ const Style( 'bg-yellow-400 px-3 py-1.5 text-xs font-medium whitespace-nowrap', display: DisplayType.inline, child: Text('New'), ), const Style( 'mt-4 text-lg font-medium text-gray-900', child: Text('Robot Toy'), ), const Style( 'mt-1.5 text-sm text-gray-700', child: Text(r'$14.99'), ), Style( 'mt-4', child: Style( 'block w-full rounded-sm bg-yellow-400 p-4 text-sm font-medium transition hover:scale-105', cursor: SystemMouseCursors.click, button: true, child: const Text('Add to Cart'), ), ), ], ), ], ), ); }}<a href="#" class="group relative block overflow-hidden"> <button class="absolute end-4 top-4 z-10 rounded-full bg-white p-1.5 text-gray-900 transition hover:text-gray-900/75" > <span class="sr-only">Wishlist</span>
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4" > <path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" /> </svg> </button>
<img src="https://images.unsplash.com/photo-1599481238640-4c1288750d7a?auto=format&fit=crop&q=80&w=1160" alt="" class="h-64 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-72" />
<div class="relative border border-gray-100 bg-white p-6"> <span class="bg-yellow-400 px-3 py-1.5 text-xs font-medium whitespace-nowrap"> New </span>
<h3 class="mt-4 text-lg font-medium text-gray-900">Robot Toy</h3>
<p class="mt-1.5 text-sm text-gray-700">$14.99</p>
<form class="mt-4"> <button class="block w-full rounded-sm bg-yellow-400 p-4 text-sm font-medium transition hover:scale-105" > Add to Cart </button> </form> </div></a>Product with image, title, description, offer and call to action
Section titled “Product with image, title, description, offer and call to action”class ProductCards7Example extends StatelessWidget { const ProductCards7Example({super.key});
@override Widget build(BuildContext context) { return Style( 'mx-auto max-w-md p-6', child: Style( 'relative block rounded-se-3xl border border-gray-100', cursor: SystemMouseCursors.click, children: [ const Style( 'absolute -top-px -right-px rounded-se-3xl rounded-es-3xl bg-rose-600 px-6 py-4 font-medium tracking-widest text-white uppercase', child: Text('Save 10%'), ), Style( 'h-80 w-full rounded-se-3xl object-cover', child: Image.network( 'https://images.unsplash.com/photo-1485955900006-10f4d324d411?auto=format&fit=crop&q=80&w=1160', fit: BoxFit.cover, ), ), Style( 'p-4 text-center', children: [ const Style( 'text-xl font-medium text-gray-900', display: DisplayType.inline, child: Text('Aloe Vera'), ), const Style( 'mt-2 text-pretty text-gray-700', child: Text( 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Amet officia rem vel ' 'voluptatum in eum vitae aliquid at sed dignissimos.', ), ), const Style( 'mt-4 block rounded-md border border-indigo-900 bg-indigo-900 px-5 py-3 text-sm font-medium tracking-widest text-white uppercase transition-colors hover:bg-white hover:text-indigo-900', child: Text('Buy now'), ), ], ), ], ), ); }}<a href="#" class="relative block rounded-se-3xl border border-gray-100"> <span class="absolute -top-px -right-px rounded-se-3xl rounded-es-3xl bg-rose-600 px-6 py-4 font-medium tracking-widest text-white uppercase" > Save 10% </span>
<img src="https://images.unsplash.com/photo-1485955900006-10f4d324d411?auto=format&fit=crop&q=80&w=1160" alt="" class="h-80 w-full rounded-se-3xl object-cover" />
<div class="p-4 text-center"> <strong class="text-xl font-medium text-gray-900"> Aloe Vera </strong>
<p class="mt-2 text-pretty text-gray-700"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Amet officia rem vel voluptatum in eum vitae aliquid at sed dignissimos. </p>
<span class="mt-4 block rounded-md border border-indigo-900 bg-indigo-900 px-5 py-3 text-sm font-medium tracking-widest text-white uppercase transition-colors hover:bg-white hover:text-indigo-900" > Buy now </span> </div></a>Product with image, title, price, wishlist and call to actions
Section titled “Product with image, title, price, wishlist and call to actions”class CurrentColorIcon extends StatelessWidget implements StyleReplacedElementWrapper { const CurrentColorIcon(this.svg, {super.key});
final String svg;
@override Widget build(BuildContext context) { final color = IconTheme.of(context).color ?? const Color(0xFF000000); return SvgPicture.string(svg, theme: SvgTheme(currentColor: color)); }}
const _photoUrl = 'https://images.unsplash.com/photo-1628202926206-c63a34b1618f?auto=format&fit=crop&q=80&w=1160';
const _heartSvg = '''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z"/></svg>''';
class ProductCards8Example extends StatelessWidget { const ProductCards8Example({super.key});
@override Widget build(BuildContext context) { return Style( 'mx-auto max-w-md p-6', child: Style( 'group relative block overflow-hidden', cursor: SystemMouseCursors.click, children: [ Style( 'absolute end-4 top-4 z-10 rounded-full bg-white p-1.5 text-gray-900 transition hover:text-gray-900/75', cursor: SystemMouseCursors.click, children: [ const Style('sr-only', child: Text('Wishlist')), const Style('size-4', child: CurrentColorIcon(_heartSvg)), ], ), Style( 'h-64 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-72', child: Image.network(_photoUrl, fit: BoxFit.cover), ), Style( 'relative border border-gray-100 bg-white p-6', children: [ Style( 'text-gray-700', children: const [ Text(r'$49.99 '), Style('text-gray-600 line-through', child: Text(r'$80')), ], ), const Style( 'mt-1.5 text-lg font-medium text-gray-900', child: Text('Wireless Headphones'), ), const Style( 'mt-1.5 line-clamp-3 text-gray-700', child: Text( 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Labore nobis iure ' 'obcaecati pariatur. Officiis qui, enim cupiditate aliquam corporis iste.', ), ), Style( 'mt-4 flex gap-4', children: [ Style( 'block w-full rounded-sm bg-gray-100 px-4 py-3 text-sm font-medium text-gray-900 transition hover:scale-105', cursor: SystemMouseCursors.click, child: const Text('Add to Cart'), ), Style( 'block w-full rounded-sm bg-gray-900 px-4 py-3 text-sm font-medium text-white transition hover:scale-105', cursor: SystemMouseCursors.click, child: const Text('Buy Now'), ), ], ), ], ), ], ), ); }}<a href="#" class="group relative block overflow-hidden"> <button class="absolute end-4 top-4 z-10 rounded-full bg-white p-1.5 text-gray-900 transition hover:text-gray-900/75" > <span class="sr-only">Wishlist</span>
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4" > <path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" /> </svg> </button>
<img src="https://images.unsplash.com/photo-1628202926206-c63a34b1618f?auto=format&fit=crop&q=80&w=1160" alt="" class="h-64 w-full object-cover transition duration-500 group-hover:scale-105 sm:h-72" />
<div class="relative border border-gray-100 bg-white p-6"> <p class="text-gray-700"> $49.99 <span class="text-gray-600 line-through">$80</span> </p>
<h3 class="mt-1.5 text-lg font-medium text-gray-900">Wireless Headphones</h3>
<p class="mt-1.5 line-clamp-3 text-gray-700"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Labore nobis iure obcaecati pariatur. Officiis qui, enim cupiditate aliquam corporis iste. </p>
<form class="mt-4 flex gap-4"> <button class="block w-full rounded-sm bg-gray-100 px-4 py-3 text-sm font-medium text-gray-900 transition hover:scale-105" > Add to Cart </button>
<button type="button" class="block w-full rounded-sm bg-gray-900 px-4 py-3 text-sm font-medium text-white transition hover:scale-105" > Buy Now </button> </form> </div></a>