Product Item A

Component that represents a product item.

Preview

Code

import styles from "./product-item-a.module.css"; type Props = { className?: string; imageSrc?: string; imageAspect?: "default" | "square"; title: string; category?: string; price?: string; rating?: string; description?: React.ReactNode; tags?: string[]; href?: string; isFavorite?: boolean; iconCart?: React.ReactNode; iconFavoriteOn?: React.ReactNode; iconFavoriteOff?: React.ReactNode; variant?: "default" | "dark" | "simple"; onClickAddToCart?: () => void; onClickFavorite?: () => void; }; export default function ProductItemA({ className, imageSrc, imageAspect, title, category, description, price, tags, rating, href, isFavorite, iconCart, iconFavoriteOn, iconFavoriteOff, variant, onClickAddToCart, onClickFavorite, }: Props) { return ( <div className={[styles["wrapper"], styles[`variant-${variant}`], className].join( " " )} > <div className={styles["media"]}> {/* You may need to replace the a tag with a Link if you use Next.js */} <a href={href}> {/* If you use Next.js, replace 'img' with 'Image' element */} <img className={[styles["image"], styles[`image-aspect-${imageAspect}`]].join(" ")} src={imageSrc} alt={title} width={360} height={360} /> </a> <div className={styles["image-controls"]}> {/* Add to cart button */} <button type="button" className={styles["icon-button"]} onClick={() => { // add on click behavior here eg: onClickAddToCart && onClickAddToCart(); }} > {iconCart || <IconCart />} </button> {/* Favorite button */} <button type="button" className={styles["icon-button"]} onClick={() => { // add on click behavior here eg: onClickFavorite && onClickFavorite(); }} > {isFavorite ? iconFavoriteOn || <IconHeartFill /> : iconFavoriteOff || <IconHeart />} </button> </div> </div> <div className={styles["content"]}> {/* You may replace the a tag with a Link if you use NextJS */} <a href={href}> {category && <h6 className={styles["category"]}>{category}</h6>} <h3 className={styles["title"]}>{title}</h3> {description && <p className={styles["desc"]}>{description}</p>} {price && <div className={styles["price"]}>{price}</div>} </a> </div> <div className={styles["footer"]}> <div className={styles["f-left"]}> {tags && ( <div className={styles["tags"]}> {tags?.map((tag) => ( <span key={tag} className={styles["tag"]}> {tag} </span> ))} </div> )} </div> <div className={styles["f-right"]}> {rating && ( <div className={styles["rating"]}> <span className={styles["rating-icon"]}> {iconFavoriteOn || <IconHeart />} </span> <span className={styles["rating-text"]}>{rating}</span> </div> )} </div> </div> </div> ); } // IconCart component function IconCart({ className }: { className?: string }) { return ( <svg xmlns="http://www.w3.org/2000/svg" className={className} viewBox="0 0 512 512" style={{ width: "1em", height: "1em", }} > <circle cx="176" cy="416" r="32" /> <circle cx="400" cy="416" r="32" /> <path d="M456.8,120.78A23.92,23.92,0,0,0,438.24,112H133.89l-6.13-34.78A16,16,0,0,0,112,64H48a16,16,0,0,0,0,32H98.58l45.66,258.78A16,16,0,0,0,160,368H416a16,16,0,0,0,0-32H173.42l-5.64-32H409.44A24.07,24.07,0,0,0,433,284.71l28.8-144A24,24,0,0,0,456.8,120.78Z" /> </svg> ); } // IconHeart component function IconHeart({ className }: { className?: string }) { return ( <svg xmlns="http://www.w3.org/2000/svg" className={className} viewBox="0 0 512 512" style={{ width: "1em", height: "1em", }} > <path d="M352.92 80C288 80 256 144 256 144s-32-64-96.92-64c-52.76 0-94.54 44.14-95.08 96.81-1.1 109.33 86.73 187.08 183 252.42a16 16 0 0018 0c96.26-65.34 184.09-143.09 183-252.42-.54-52.67-42.32-96.81-95.08-96.81z" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="32" /> </svg> ); } // IconHeartFill component function IconHeartFill({ className }: { className?: string }) { return ( <svg xmlns="http://www.w3.org/2000/svg" className={className} viewBox="0 0 512 512" style={{ width: "1em", height: "1em", }} > <path fill="currentColor" d="M256 448a32 32 0 01-18-5.57c-78.59-53.35-112.62-89.93-131.39-112.8-40-48.75-59.15-98.8-58.61-153C48.63 114.52 98.46 64 159.08 64c44.08 0 74.61 24.83 92.39 45.51a6 6 0 009.06 0C278.31 88.81 308.84 64 352.92 64c60.62 0 110.45 50.52 111.08 112.64.54 54.21-18.63 104.26-58.61 153-18.77 22.87-52.8 59.45-131.39 112.8a32 32 0 01-18 5.56z" /> </svg> ); }

Design

Figma design file:

Documentation

Properties

Props of the component:

  • className (string): Specifies the CSS class of the component.
  • imageSrc (string): Specifies the URL of the image.
  • imageAspect ("default" | "square"): Specifies the aspect ratio of the image.
  • title (string): Specifies the text that will be used as the title.
  • category (string): Specifies the text that will be used as the category.
  • price (string): Specifies the text that will be used as the price.
  • rating (string): Specifies the text that will be used as the string.
  • description (string or ReactNode): Specifies the text or component that will be used as the description.
  • tags (array of string): Specifies text tags.
  • href (string): Specifies the URL if the component is a link.
  • isFavorite (boolean): Specifies the item is marked as favorite.
  • iconCart (ReactNode): Specifies the cart icon component.
  • iconFavoriteOn (ReactNode): Specifies the "favorite on" icon component.
  • iconFavoriteOff (ReactNode): Specifies the "favorite off" icon component.
  • variant ("default" | "dark" | "simple" or a customized value): Specifies the color or theme variant of the component. Check out the "Sample CSS customization" below for an example of how to use it.
  • onClickAddToCart (function): Fires when the add to cart button is clicked.
  • onClickFavorite (function): Fires when the favorite button is clicked.
Sample CSS customization
.variant-dark{ --fg-color: #ffffff; --bg-color: #424242; }