Components
Plan Card
A single subscription/account plan card. Purely presentational — no Morphos equivalent.
Installation
npx kosmesis add plan-cardpnpm dlx kosmesis add plan-cardyarn dlx kosmesis add plan-cardbunx kosmesis add plan-cardCopy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { cn } from "@/lib/utils";export interface PlanCardProps { name: string; price?: string; description?: string; current?: boolean; class?: string; children?: Children;}/** Distinct from `PricingPlan` (a comparison-table row): this is a single current-plan summary. */@Component()export class PlanCard extends StatelessComponent<PlanCardProps> { render() { const { name, price, description, current, class: cls, children } = this.props; return ( <div data-slot="plan-card" class={cn("flex flex-col gap-3 rounded-xl border bg-card p-5 text-card-foreground", cls)}> <div class="flex items-center justify-between"> <h3 class="font-semibold">{name}</h3> {current && ( <span class="rounded-full bg-primary/10 px-2 py-0.5 text-xs font-medium text-primary">Current plan</span> )} </div> {price && <p class="text-2xl font-bold">{price}</p>} {description && <p class="text-sm text-muted-foreground">{description}</p>} {children} </div> ); }}Install the following dependencies:
npm install @praxisjs/csspnpm add @praxisjs/cssyarn add @praxisjs/cssbun add @praxisjs/cssCopy 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 type { Children } from "@praxisjs/shared";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);class PlanCardStyles 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: "1.25rem", }); $header = this.css({ display: "flex", alignItems: "center", justifyContent: "space-between" }); $name = this.css({ fontWeight: 600 }); $badge = this.css({ borderRadius: "9999px", backgroundColor: `color-mix(in oklab, ${t.primary} 10%, transparent)`, padding: "0.125rem 0.5rem", fontSize: "0.75rem", fontWeight: 500, color: t.primary, }); $price = this.css({ fontSize: "1.5rem", fontWeight: 700 }); $description = this.css({ fontSize: "0.875rem", color: t.mutedForeground });}export interface PlanCardProps { name: string; price?: string; description?: string; current?: boolean; class?: string; children?: Children;}/** Distinct from `PricingPlan` (a comparison-table row): this is a single current-plan summary. */@Component()export class PlanCard extends StatelessComponent<PlanCardProps> { @Styled(PlanCardStyles) $s!: PlanCardStyles; render() { const { name, price, description, current, class: cls, children } = this.props; return ( <div data-slot="plan-card" class={cx(this.$s.$root, cls)}> <div class={this.$s.$header}> <h3 class={this.$s.$name}>{name}</h3> {current && <span class={this.$s.$badge}>Current plan</span>} </div> {price && <p class={this.$s.$price}>{price}</p>} {description && <p class={this.$s.$description}>{description}</p>} {children} </div> ); }}Examples
About
Purely presentational — no Morphos equivalent. Distinct from PricingPlan (a row in a comparison
table meant to be shown side by side): this is a single "your current plan" summary, e.g. on a
billing settings page. children render below the description (e.g. a usage bar or an upgrade
button).
Usage
import { PlanCard } from "@/components/ui/plan-card";
<PlanCard name="Pro" price="$19/mo" description="For growing teams" current>
<Button variant="outline">Change plan</Button>
</PlanCard>Props
| Prop | Type | Default |
|---|---|---|
name | string | — |
price | string | — |
description | string | — |
current | boolean | false |
class | string | — |