docs / patterns / stripe-card
StripeCard
a compact row-card for one entity: a leading stripe keys it to a category, with a heading, mono meta, and optional badge and footer. the stripe means something or stays neutral, never decoration.
intensity · structuresrsc · server
provenance
shaped from sisu-pluslayer
core / patternsintensity
structures · organizes a region, stays out of the contentcomposition
sits in a grid or list of siblings, one entity eachthe stripe color keys a category shared with a ToolbarLegendnever a lone hero card. it is a row in a collectionno interactive children fighting the card's own hover lifta11y
the stripe and the meta separator arearia-hidden · the heading is a real h3jest-axedependencies
Card from the same packagelive demo · try it out
customize
stripeColorkeyed to a category
washa faint wash in the stripe's color
surfacehow it sits on the page
selectedswaps hover lift for the accent ring
usage
import { StripeCard } from "usva/patterns/stripe-card";
import { Badge } from "usva/primitives/badge";
<StripeCard
heading="Algorithms & Data Structures"
metaLeft="CS-201"
metaRight="5 cr"
stripeColor="#a78bfa"
badge={<Badge tone="accent-alt">enrolled</Badge>}
footer="Autumn 2026 · Prof. Turing"
/>props
| prop | type | default | notes |
|---|---|---|---|
| heading | ReactNode | — | the primary title. |
| metaLeft | ReactNode | — | mono meta on the left, e.g. a code. |
| metaRight | ReactNode | — | mono meta on the right, rendered in the accent color. |
| badge | ReactNode | — | top-right slot, usually a Badge. |
| footer | ReactNode | — | bottom bar slot, pinned under the body. |
| stripeColor | string | — | any CSS color, keyed to a category. unset falls back to a neutral token. |
| wash | boolean | false | a faint background wash in the stripe's color, or the accent when the stripe is unset. |
| surface | "elevated" | "flat" | "glass" | "outline" | "elevated" | how the card sits on the page. inherited from Card. |
| selected | boolean | false | swaps the hover lift for the accent glow ring. |
get it
npx shadcn add https://usva.build/r/stripe-card.jsonsource · components/ui/stripe-card.tsxexactly what this command copies
import * as React from "react";
import { cn } from "@/lib/utils";
import { Card, type CardProps } from "./card";
export interface StripeCardProps extends Omit<CardProps, "title"> {
heading: React.ReactNode;
/** Mono meta, left side (e.g. a code). */
metaLeft?: React.ReactNode;
/** Mono meta, right side (e.g. a count). Rendered accent-colored. */
metaRight?: React.ReactNode;
badge?: React.ReactNode;
footer?: React.ReactNode;
/** Leading stripe color. Any CSS color; defaults to a neutral token. */
stripeColor?: string;
/** Paints a faint background wash keyed to `stripeColor` (or the accent). */
wash?: boolean;
selected?: boolean;
}
/**
* A compact entity row-card with a leading color stripe, a heading, mono meta,
* a badge slot, and an optional footer. Flat at rest, lifts on hover.
*
* The stripe is a categorical key: it says which module, project, or status the
* row belongs to. It is not a decorative accent, and not the banned side-stripe.
* Give it meaning or leave it neutral.
*/
export const StripeCard = React.forwardRef<HTMLDivElement, StripeCardProps>(
(
{
className,
heading,
metaLeft,
metaRight,
badge,
footer,
stripeColor,
wash = false,
selected = false,
children,
...props
},
ref,
) => (
<Card
ref={ref}
interactive
highlight={selected ? "ring" : "none"}
className={cn(
"group/stripe relative isolate flex flex-col overflow-hidden",
className,
)}
{...props}
>
{wash && (
<span
aria-hidden="true"
data-stripe-wash=""
className="pointer-events-none absolute inset-0 -z-10"
style={{
backgroundImage: `linear-gradient(105deg, color-mix(in oklab, ${stripeColor ?? "var(--color-accent)"} 20%, transparent), color-mix(in oklab, ${stripeColor ?? "var(--color-accent)"} 6%, transparent) 42%, transparent 78%)`,
}}
/>
)}
<div className="flex items-start justify-between gap-3 p-4">
<div className="flex min-w-0 items-stretch gap-3">
<span
aria-hidden="true"
className={cn(
"w-1 shrink-0 self-stretch rounded-full",
stripeColor ? undefined : "bg-border-strong",
)}
style={stripeColor ? { backgroundColor: stripeColor } : undefined}
/>
<div className="min-w-0">
<h3 className="text-balance text-sm font-semibold leading-snug text-ink">
{heading}
</h3>
{(metaLeft != null || metaRight != null) && (
<p className="mt-1 flex flex-wrap items-center gap-x-2 gap-y-1 font-mono text-xs text-muted">
{metaLeft != null && <span>{metaLeft}</span>}
{metaLeft != null && metaRight != null && (
<span aria-hidden="true">·</span>
)}
{metaRight != null && (
<span className="font-semibold text-accent">{metaRight}</span>
)}
</p>
)}
</div>
</div>
{badge != null && <div className="shrink-0">{badge}</div>}
</div>
{children}
{footer != null && (
<div className="mt-auto border-t border-border bg-surface-2/50 px-4 py-2 text-xs text-muted">
{footer}
</div>
)}
</Card>
),
);
StripeCard.displayName = "StripeCard";