Components
Invoice Card
A single invoice summary row. Purely presentational — no Morphos equivalent.
Installation
npx kosmesis add invoice-cardpnpm dlx kosmesis add invoice-cardyarn dlx kosmesis add invoice-cardbunx kosmesis add invoice-cardInstall the following dependencies:
npm install @morphos/iconspnpm add @morphos/iconsyarn add @morphos/iconsbun add @morphos/iconsCopy and paste the following code into your project.
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/iconspnpm add @praxisjs/css @morphos/iconsyarn add @praxisjs/css @morphos/iconsbun add @praxisjs/css @morphos/iconsCopy and paste the following code into your project.
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
| Prop | Type | Default |
|---|---|---|
number | string | — |
date | string | — |
amount | string | — |
status | "paid" | "pending" | "overdue" | "paid" |
onDownload | () => void | — |
class | string | — |