Discounts page
A discount index inside the app shell, with a notice, key figures, status links and a card per discount showing its status, dates and usage on a Meter. It ships no JavaScript.
From Figma 08 Overview Dashboard, Pattern 5 · Discounts (7275:16204). Uses
PageHeader, Button,
Alert, KpiTile,
TabNav, Card,
Badge, Meter and
Text.
The example is the page's content only: in the app it goes inside
AppShell, which owns the sidebar, the top bars and <main>.
// app/(store)/layout.tsx: the shell wraps every store page; the page renders its content
import { AppShell } from '@nizivo/react/app-shell';
<AppShell sidebar={sidebar} topBar={topBar} mobileTopBar={mobileTopBar} tabBar={tabBar}>
<DiscountsPageExample />
</AppShell>;Example
Discounts
Figma 08's Pattern 5, Discounts: the content of the app shell's <main>. The page header, a notice, three key figures and the status links, then the discounts as a list of cards: each code with its offer, its status in words, its dates and a Meter of how many of its uses are taken. A server composition: it ships no JavaScript.
Discounts
AUTUMN20
20% off all mugs and bowls
ActiveUsesFREESHIP
Free shipping over $50
ActiveUsesWELCOME10
10% off a first order
ActiveUsesHOLIDAY25
25% off gift sets
ScheduledUsesNEWYEAR
$10 off orders over $60
ScheduledUsesSUMMER15
15% off tableware
ExpiredUses
import type { JSX } from 'react';
import { Plus } from '@nizivo/icons';
import { Alert } from '@nizivo/react/alert';
import { Badge, type BadgeTone } from '@nizivo/react/badge';
import { Button } from '@nizivo/react/button';
import { Card } from '@nizivo/react/card';
import { KpiTile } from '@nizivo/react/kpi-tile';
import { Meter, type MeterTone } from '@nizivo/react/meter';
import { PageHeader } from '@nizivo/react/page-header';
import { TabNav } from '@nizivo/react/tab-nav';
import { Text } from '@nizivo/react/text';
type Status = 'active' | 'scheduled' | 'expired';
interface Discount {
code: string;
offer: string;
status: Status;
/** When it starts or ends, as the store's own words; the `<time>` keeps the date. */
when: [label: string, date: string];
used: number;
limit: number;
}
const DISCOUNTS: Discount[] = [
{
code: 'AUTUMN20',
offer: '20% off all mugs and bowls',
status: 'active',
when: ['Ends Nov 30', '2026-11-30'],
used: 312,
limit: 500,
},
{
code: 'FREESHIP',
offer: 'Free shipping over $50',
status: 'active',
when: ['Ends Dec 31', '2026-12-31'],
used: 846,
limit: 1000,
},
{
code: 'WELCOME10',
offer: '10% off a first order',
status: 'active',
when: ['Ends Jan 31', '2027-01-31'],
used: 128,
limit: 1000,
},
{
code: 'HOLIDAY25',
offer: '25% off gift sets',
status: 'scheduled',
when: ['Starts Dec 1', '2026-12-01'],
used: 0,
limit: 300,
},
{
code: 'NEWYEAR',
offer: '$10 off orders over $60',
status: 'scheduled',
when: ['Starts Jan 1', '2027-01-01'],
used: 0,
limit: 200,
},
{
code: 'SUMMER15',
offer: '15% off tableware',
status: 'expired',
when: ['Ended Aug 31', '2026-08-31'],
used: 400,
limit: 400,
},
];
const STATUS: Record<Status, [label: string, badge: BadgeTone, meter: MeterTone]> = {
active: ['Active', 'success', 'brand'],
scheduled: ['Scheduled', 'info', 'brand'],
expired: ['Expired', 'neutral', 'neutral'],
};
const TABS = [
['all', 'All'],
['active', 'Active'],
['scheduled', 'Scheduled'],
['expired', 'Expired'],
] as const;
export function DiscountsPageExample(): JSX.Element {
return (
<div className="nzp-discounts-page">
<PageHeader
level={2}
title="Discounts"
actions={
<Button variant="primary" leadingIcon={<Plus />}>
Create discount
</Button>
}
/>
<Alert title="Discounts now stack with free shipping">
A code and free shipping can apply to the same order. Edit a discount to turn it off.
</Alert>
<section aria-label="Key figures" className="nzp-discounts-page-kpis">
<KpiTile label="Active discounts" value={3} delta={0.5} comparison="vs Sep" />
<KpiTile label="Uses · 30 days" value={1284} delta={0.152} comparison="vs Sep" />
<KpiTile
label="Discounted sales"
value={8920}
format={{ style: 'currency', currency: 'USD', maximumFractionDigits: 0 }}
delta={0.087}
comparison="vs Sep"
/>
</section>
<TabNav.Root aria-label="Discount status">
{TABS.map(([value, label]) => (
<TabNav.Link key={value} href={`?status=${value}`} current={value === 'all'}>
{label}
</TabNav.Link>
))}
</TabNav.Root>
<ul className="nzp-discounts-page-list" aria-label="Discounts">
{DISCOUNTS.map((discount) => {
const [label, badge, tone] = STATUS[discount.status];
return (
<Card.Root key={discount.code} render={<li />} className="nzp-discounts-page-card">
<Card.Header>
<Card.Title>
<Text render={<span />} variant="mono-m-strong">
{discount.code}
</Text>
</Card.Title>
<Card.Description>{discount.offer}</Card.Description>
<Card.Action>
<Badge tone={badge}>{label}</Badge>
</Card.Action>
</Card.Header>
<Meter
label="Uses"
value={discount.used}
max={discount.limit}
tone={tone}
valueText={`${String(discount.used)} of ${String(discount.limit)}`}
/>
<Card.Footer className="nzp-discounts-page-footer">
<Text render={<span />} tone="secondary">
<time dateTime={discount.when[1]}>{discount.when[0]}</time>
</Text>
<Button size="sm" aria-label={`Edit ${discount.code}`}>
Edit
</Button>
</Card.Footer>
</Card.Root>
);
})}
</ul>
</div>
);
}
Stylesheet
@layer nz.tokens, nz.themes, nz.reset, nz.base, nz.components, nz.patterns, nz.utilities;
/* Figma 08 Pattern 5 (7275:16204): the header, the notice, the KPI row (16 apart), the status links,
then the discount cards three to a row, 24 apart (Figma's Discount Card is a set with no code
component, so each is a Card). The grids follow the page's own width. */
@layer nz.patterns {
.nzp-discounts-page {
display: flex;
flex-direction: column;
gap: var(--nz-space-6);
min-inline-size: 0;
container-type: inline-size;
}
.nzp-discounts-page-kpis {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: var(--nz-space-4);
}
.nzp-discounts-page-list {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: var(--nz-space-6);
margin: 0;
padding: 0;
list-style: none;
}
@container (width >= 36rem) {
.nzp-discounts-page-kpis {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.nzp-discounts-page-list {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@container (width >= 56rem) {
.nzp-discounts-page-list {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
.nzp-discounts-page-footer {
justify-content: space-between;
margin-block-start: auto;
}
}
Notes
- Heading levels. This site's page already has its
<h1>, so the example passeslevel={2}to PageHeader. In your app, droplevel(the title becomes the page's<h1>). - A list. The discounts are a
<ul>whose items are Cards (render={<li />}), so a screen reader announces how many there are. Each status is a Badge's words, never its colour alone. - Usage is a Meter, not a ProgressBar: it measures how much of a fixed limit is used, and
valueTextreads it as "312 of 500". - Not built. Figma's Discount Card has no code component; each is a Card. The notice is not
dismissible here: add
dismiss={<AlertDismiss … />}to let a reader close it.
Customers page
A customer index inside the app shell, with key figures, segment links and a customers table beside the top cities and a win-back card. It ships no JavaScript.
Analytics page
A report inside the app shell, with a range control, key figures with sparklines, a sales trend, goals, sales by channel, orders per day and the top products.