Kosmesis
Components

Invoice Card

A single invoice summary row. Purely presentational — no Morphos equivalent.

Installation

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

Install the following dependencies:

npm install @morphos/icons
pnpm add @morphos/icons
yarn add @morphos/icons
bun add @morphos/icons

Copy and paste the following code into your project.

invoice-card.tsx
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import { Icon } from "@morphos/icons";import { cn } from "@/lib/utils";export type InvoiceStatus = "paid" | "pending" | "overdue";export interface InvoiceCardProps {  number: string;  date: string;  amount: string;  status?: InvoiceStatus;  onDownload?: () => void;  class?: string;}@Component()export class InvoiceCard extends StatelessComponent<InvoiceCardProps> {  render() {    const { number, date, amount, status = "paid", onDownload, class: cls } = this.props;    return (      <div        data-slot="invoice-card"        class={cn("flex items-center justify-between gap-4 rounded-lg border bg-card p-4 text-card-foreground", cls)}      >        <div class="flex flex-col gap-0.5">          <span class="text-sm font-medium">{`Invoice ${number}`}</span>          <span class="text-xs text-muted-foreground">{date}</span>        </div>        <div class="flex items-center gap-3">          <span class="text-sm font-medium">{amount}</span>          <span            data-status={status}            class={cn(              "rounded-full px-2 py-0.5 text-xs font-medium capitalize",              "data-[status=paid]:bg-primary/10 data-[status=paid]:text-primary",              "data-[status=pending]:bg-muted data-[status=pending]:text-muted-foreground",              "data-[status=overdue]:bg-destructive/10 data-[status=overdue]:text-destructive",            )}          >            {status}          </span>          {onDownload && (            <button              type="button"              aria-label="Download invoice"              class="rounded-md p-1.5 text-muted-foreground hover:bg-accent hover:text-accent-foreground"              onClick={onDownload}            >              <Icon name="Download" size={16} />            </button>          )}        </div>      </div>    );  }}

Install the following dependencies:

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

Copy and paste the following code into your project.

invoice-card.tsx
import { StatelessComponent } from "@praxisjs/core";import { cx, Stylesheet, Styled, tokenVars } from "@praxisjs/css";import { Component } from "@praxisjs/decorators";import { Icon } from "@morphos/icons";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);class InvoiceCardStyles extends Stylesheet {  $root = this.css({    display: "flex",    alignItems: "center",    justifyContent: "space-between",    gap: "1rem",    borderRadius: "0.5rem",    border: `1px solid ${t.border}`,    backgroundColor: t.card,    color: t.cardForeground,    padding: "1rem",  });  $meta = this.css({ display: "flex", flexDirection: "column", gap: "0.125rem" });  $number = this.css({ fontSize: "0.875rem", fontWeight: 500 });  $date = this.css({ fontSize: "0.75rem", color: t.mutedForeground });  $right = this.css({ display: "flex", alignItems: "center", gap: "0.75rem" });  $amount = this.css({ fontSize: "0.875rem", fontWeight: 500 });  $badge = this.css({    borderRadius: "9999px",    padding: "0.125rem 0.5rem",    fontSize: "0.75rem",    fontWeight: 500,    textTransform: "capitalize",  })    .on('&[data-status="paid"]', { backgroundColor: `color-mix(in oklab, ${t.primary} 10%, transparent)`, color: t.primary })    .on('&[data-status="pending"]', { backgroundColor: t.muted, color: t.mutedForeground })    .on('&[data-status="overdue"]', {      backgroundColor: `color-mix(in oklab, ${t.destructive} 10%, transparent)`,      color: t.destructive,    });  $download = this.css({ borderRadius: "0.375rem", padding: "0.375rem", color: t.mutedForeground, cursor: "pointer" }).on(    "&:hover",    { backgroundColor: t.accent, color: t.accentForeground },  );}export type InvoiceStatus = "paid" | "pending" | "overdue";export interface InvoiceCardProps {  number: string;  date: string;  amount: string;  status?: InvoiceStatus;  onDownload?: () => void;  class?: string;}@Component()export class InvoiceCard extends StatelessComponent<InvoiceCardProps> {  @Styled(InvoiceCardStyles) $s!: InvoiceCardStyles;  render() {    const { number, date, amount, status = "paid", onDownload, class: cls } = this.props;    return (      <div data-slot="invoice-card" class={cx(this.$s.$root, cls)}>        <div class={this.$s.$meta}>          <span class={this.$s.$number}>{`Invoice ${number}`}</span>          <span class={this.$s.$date}>{date}</span>        </div>        <div class={this.$s.$right}>          <span class={this.$s.$amount}>{amount}</span>          <span data-status={status} class={this.$s.$badge}>            {status}          </span>          {onDownload && (            <button type="button" aria-label="Download invoice" class={this.$s.$download} onClick={onDownload}>              <Icon name="Download" size={16} />            </button>          )}        </div>      </div>    );  }}

Examples

About

Purely presentational — no Morphos equivalent. The download button only renders when onDownload is given.

Usage

import { InvoiceCard } from "@/components/ui/invoice-card";

<InvoiceCard number="INV-2044" date="Jul 12, 2026" amount="$49.00" status="paid" onDownload={download} />

Props

PropTypeDefault
numberstring
datestring
amountstring
status"paid" | "pending" | "overdue""paid"
onDownload() => void
classstring

On this page