Recently, I was working on a section for a men's t-shirt collection, and I wanted a component that felt both minimal and informative. Here’s how I built a responsive product card using React and CSS Grid, perfect for showcasing items like those found in a typical apparel shop.
First, I defined a simple product object. For this example, I’m using mock data for a classic crew neck tee. The key properties are image URL, title, price, and a short description.
const product = {
img: 'https://via.placeholder.com/300x400',
title: 'Classic Crew Neck Tee',
price: '$29.99',
description: 'Soft cotton blend for all-day comfort.'
};
Next, I created the ProductCard component. The structure is straightforward: an image container, a text area for details, and a call-to-action (CTA) button. I used CSS Grid for the layout to keep it adaptable.
const ProductCard = ({ product }) => (
<div className="product-card">
<div className="product-image">
<img src={product.img} alt={product.title} loading="lazy" />
</div>
<div className="product-info">
<h3>{product.title}</h3>
<p className="price">{product.price}</p>
<p className="description">{product.description}</p>
<button className="cta-button">Shop Now</button>
</div>
</div>
);
For the styling, I used a grid layout that centers the card and gives it a subtle shadow. The image scales nicely with object-fit: cover, and the CTA button has a hover effect for interactivity.
.product-card {
display: grid;
grid-template-rows: 1fr auto;
max-width: 320px;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
transition: transform 0.2s ease;
}
.product-card:hover {
transform: translateY(-4px);
}
.product-image img {
width: 100%;
height: 300px;
object-fit: cover;
}
.product-info {
padding: 1rem;
background: #fff;
}
.price {
font-weight: bold;
color: #2a9d8f;
}
.cta-button {
background: #264653;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 6px;
cursor: pointer;
margin-top: 0.5rem;
}
.cta-button:hover {
background: #1d3557;
}
This component is flexible. You can easily swap the mock data with real API responses, like from a shop’s backend that lists men’s t-shirts. The grid layout ensures it works on mobile and desktop without extra media queries. Whether you’re building a full store or just a featured section, this pattern keeps your UI clean and performant. Try it out and tweak the colors to match your brand!
Top comments (2)
Great breakdown of the product card component! I've been using a similar pattern lately but found that adding a quick size selector dropdown inside the card improved UX significantly for apparel items. For men's t-shirts, customers often want to see size availability at a glance without clicking through.
One thing I'd suggest is adding an
aspect-ratioCSS property to the image container to prevent layout shift during loading. Something likeaspect-ratio: 3/4works well for t-shirt images. Also, if you're fetching real inventory data, consider passing aninStockboolean to conditionally disable the CTA button.For those working with larger collections, memoizing the ProductCard with React.memo can help performance when rendering multiple cards in a grid. Have you experimented with lazy loading images beyond the native
loading="lazy"attribute? I've found that using Intersection Observer with a placeholder skeleton gives a smoother experience on slower connections.The shadow and hover effect are clean - minimal but effective. One tweak I'd make is using
will-change: transformon the card to hint the browser for smoother animations. Overall solid approach for any e-commerce UI.Great approach to building reusable product cards! I've been using a similar pattern for an apparel section I'm working on, but I found that adding a quick Sold out overlay can be really helpful for inventory management. I modified your component to accept an
inStockboolean prop and conditionally render an overlay