Customer card
Who a customer is, what they are worth and how to reach them, an Avatar, their name and email, their orders and total spent, and their tags. A server composition; it ships no JavaScript.
From Figma 05 Commerce, Customer Card (754:530; an orphan master with no live component, seen
in Stage 7275:14124). Uses Card, Avatar,
Badge, Text and
NumberValue.
Examples
Customer
Figma 05's Customer Card (754:530, an orphan master): who they are, what they are worth and how to reach them. The email is a link; the figures are a description list. A server composition: it ships no JavaScript. Without a photo the Avatar shows the initials; with one, pass its src.
Jonah Okafor
jonah@example.com- Orders
- 14
- Total spent
- $1,284.00
- Avg. order
- $91.71
import type { JSX } from 'react';
import { Avatar } from '@nizivo/react/avatar';
import { Badge, type BadgeTone } from '@nizivo/react/badge';
import { Card } from '@nizivo/react/card';
import { NumberValue } from '@nizivo/react/number-value';
import { Text } from '@nizivo/react/text';
/** Money as the store's currency: Intl formats it, never string concatenation. */
const USD = { style: 'currency', currency: 'USD' } as const;
interface Customer {
name: string;
email: string;
orders: number;
spent: number;
tags: { label: string; tone: BadgeTone }[];
}
const CUSTOMER: Customer = {
name: 'Jonah Okafor',
email: 'jonah@example.com',
orders: 14,
spent: 1284,
tags: [
{ label: 'VIP', tone: 'brand' },
{ label: 'Subscriber', tone: 'neutral' },
],
};
/** One figure: a `<dt>` label over its `<dd>` value. */
function Stat({ label, children }: { label: string; children: JSX.Element }): JSX.Element {
return (
<div className="nzp-customer-card-stat">
<Text render={<dt />} variant="label-m" tone="secondary">
{label}
</Text>
<Text render={<dd />} variant="numeric-m">
{children}
</Text>
</div>
);
}
export function CustomerCardExample(): JSX.Element {
const average = CUSTOMER.spent / CUSTOMER.orders;
return (
<Card.Root
className="nzp-customer-card"
render={<section aria-labelledby="customer-card-name" />}
>
<div className="nzp-customer-card-identity">
<Avatar name={CUSTOMER.name} size="xl" decorative />
<div className="nzp-customer-card-name">
<Card.Title id="customer-card-name">{CUSTOMER.name}</Card.Title>
<a className="nzp-customer-card-email" href={`mailto:${CUSTOMER.email}`}>
{CUSTOMER.email}
</a>
</div>
</div>
<dl className="nzp-customer-card-stats">
<Stat label="Orders">
<NumberValue value={CUSTOMER.orders} />
</Stat>
<Stat label="Total spent">
<NumberValue value={CUSTOMER.spent} format={USD} />
</Stat>
<Stat label="Avg. order">
<NumberValue value={average} format={USD} />
</Stat>
</dl>
<ul className="nzp-customer-card-tags" aria-label="Tags">
{CUSTOMER.tags.map((tag) => (
<li key={tag.label}>
<Badge tone={tag.tone} dot={false}>
{tag.label}
</Badge>
</li>
))}
</ul>
</Card.Root>
);
}
Stylesheet
@layer nz.tokens, nz.themes, nz.reset, nz.base, nz.components, nz.patterns, nz.utilities;
@layer nz.patterns {
.nzp-customer-card {
max-inline-size: 23.75rem;
}
.nzp-customer-card-identity {
display: flex;
align-items: center;
gap: var(--nz-space-3);
}
.nzp-customer-card-name {
display: flex;
flex-direction: column;
gap: var(--nz-space-0-5);
min-inline-size: 0;
}
.nzp-customer-card-email {
overflow-wrap: anywhere;
color: var(--nz-text-brand);
font: var(--nz-font-body-m);
}
/* three figures in a row; they wrap on a narrow card */
.nzp-customer-card-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(6rem, 1fr));
gap: var(--nz-space-3);
margin: 0;
}
.nzp-customer-card-stat {
display: flex;
flex-direction: column;
gap: var(--nz-space-0-5);
padding: var(--nz-space-2) var(--nz-space-3);
border-radius: var(--nz-radius-md);
background: var(--nz-bg-subtle);
}
.nzp-customer-card-stat > dd {
margin: 0;
}
.nzp-customer-card-tags {
display: flex;
flex-wrap: wrap;
gap: var(--nz-space-1-5);
margin: 0;
padding: 0;
list-style: none;
}
}
Notes
- The Avatar is decorative, because the name is printed beside it, and shows the initials when
there is no photo, which Figma's raw ellipse cannot. Pass
srconly for a photo that is known to load. - How to reach them. Figma's doc text promises it and draws no action; here the email is a
mailto:link. Add a call or message action beside it if your app has one. - The figures are a description list: each label is a
<dt>, each figure a<dd>, so they are read as pairs. Money isNumberValuewith the store's currency, and the counts go throughIntl.NumberFormat. - Contrast. Figma's stat labels are
text/tertiaryonbg/subtle, 4.16:1; here they aretext/secondary. - Tags are a list of Badges without a dot: the words are the tags, and no colour means more than its word.
Plan card
A subscription plan with its name, its price per interval, its features and a link to start, with a featured plan, and a row of plans with a monthly or yearly price.
Collection circles
A row of story-ring links to a store's collections, a cover in a ring and the name under it, that scrolls sideways. A server composition; it ships no JavaScript.