Kosmesis
Components

Product Card

A product tile with image, name, and price. Purely presentational — no Morphos equivalent.

Installation

npx kosmesis add product-card
pnpm dlx kosmesis add product-card
yarn dlx kosmesis add product-card
bunx kosmesis add product-card

Copy and paste the following code into your project.

product-card.tsx
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { cn } from "@/lib/utils";export interface ProductCardProps {  image: string;  name: string;  price: string;  originalPrice?: string;  class?: string;  children?: Children;}@Component()export class ProductCard extends StatelessComponent<ProductCardProps> {  render() {    const { image, name, price, originalPrice, class: cls, children } = this.props;    return (      <div data-slot="product-card" class={cn("flex flex-col gap-3 rounded-xl border bg-card p-3 text-card-foreground", cls)}>        <div class="aspect-square overflow-hidden rounded-lg bg-muted">          <img src={image} alt={name} class="size-full object-cover" />        </div>        <div class="flex flex-col gap-1 px-1">          <p class="truncate text-sm font-medium">{name}</p>          <div class="flex items-baseline gap-2">            <span class="text-sm font-semibold">{price}</span>            {originalPrice && <span class="text-xs text-muted-foreground line-through">{originalPrice}</span>}          </div>        </div>        {children}      </div>    );  }}

Install the following dependencies:

npm install @praxisjs/css
pnpm add @praxisjs/css
yarn add @praxisjs/css
bun add @praxisjs/css

Copy and paste the following code into your project.

product-card.tsx
import { StatelessComponent } from "@praxisjs/core";import { cx, Stylesheet, Styled, tokenVars } from "@praxisjs/css";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);class ProductCardStyles extends Stylesheet {  $root = this.css({    display: "flex",    flexDirection: "column",    gap: "0.75rem",    borderRadius: "0.75rem",    border: `1px solid ${t.border}`,    backgroundColor: t.card,    color: t.cardForeground,    padding: "0.75rem",  });  $image = this.css({ aspectRatio: "1 / 1", overflow: "hidden", borderRadius: "0.5rem", backgroundColor: t.muted });  $img = this.css({ width: "100%", height: "100%", objectFit: "cover" });  $body = this.css({ display: "flex", flexDirection: "column", gap: "0.25rem", padding: "0 0.25rem" });  $name = this.css({ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", fontSize: "0.875rem", fontWeight: 500 });  $priceRow = this.css({ display: "flex", alignItems: "baseline", gap: "0.5rem" });  $price = this.css({ fontSize: "0.875rem", fontWeight: 600 });  $originalPrice = this.css({ fontSize: "0.75rem", color: t.mutedForeground, textDecoration: "line-through" });}export interface ProductCardProps {  image: string;  name: string;  price: string;  originalPrice?: string;  class?: string;  children?: Children;}@Component()export class ProductCard extends StatelessComponent<ProductCardProps> {  @Styled(ProductCardStyles) $s!: ProductCardStyles;  render() {    const { image, name, price, originalPrice, class: cls, children } = this.props;    return (      <div data-slot="product-card" class={cx(this.$s.$root, cls)}>        <div class={this.$s.$image}>          <img src={image} alt={name} class={this.$s.$img} />        </div>        <div class={this.$s.$body}>          <p class={this.$s.$name}>{name}</p>          <div class={this.$s.$priceRow}>            <span class={this.$s.$price}>{price}</span>            {originalPrice && <span class={this.$s.$originalPrice}>{originalPrice}</span>}          </div>        </div>        {children}      </div>    );  }}

Examples

About

Purely presentational — no Morphos equivalent. children render below the price — typically an add-to-cart Button, a Rating, or an icon-only Toggle used as a wishlist button.

Usage

import { ProductCard } from "@/components/ui/product-card";

<ProductCard image="/headphones.jpg" name="Wireless headphones" price="$89.00" originalPrice="$120.00">
  <Button size="sm">Add to cart</Button>
</ProductCard>

Props

PropTypeDefault
imagestring
namestring
pricestring
originalPricestring
classstring

On this page