Tagged product
The products in a post, each a row with a thumbnail, its name as a link, variant, price and a named add-to-bag IconButton.
From Figma 10 Instagram Storefront, Tagged Product (7275:19862, with the orphan master 772:4414
the screens use), as the "In this post" list of the mobile post view (7275:20903). Uses
IconButton, NumberValue,
Text and the Plus and Check glyphs of
@nizivo/icons.
Examples
In this post
Figma 10's Tagged Product (7275:19862): the products in a post, listed under its photo. Each row has a thumbnail, the name as a link to the product, the variant and the price, and an add-to-bag IconButton named for its product ("Add Moss mug to bag"). It is a toggle: a product already in the bag shows a check, the button is pressed, and the row says "In your bag" in words. The IconButton carries its Tooltip, a client component. The thumbnail stands in for the product's Image.
- Moss mugMoss · 350 ml · In your bag$28.00
- Ash bud vaseAsh · 14 cm$34.00
import type { ComponentPropsWithRef, JSX } from 'react';
import { IconButton } from '@nizivo/react/icon-button';
import { NumberValue } from '@nizivo/react/number-value';
import { Text } from '@nizivo/react/text';
import { Check, Plus } from '@nizivo/icons';
/** Money as the store's currency: Intl formats it, never string concatenation. */
const USD = { style: 'currency', currency: 'USD' } as const;
/** Stands in for the app's router link (Next `Link`, React Router `Link`). */
function Link({ children, ...props }: ComponentPropsWithRef<'a'>): JSX.Element {
return <a {...props}>{children}</a>;
}
interface TaggedProduct {
id: string;
name: string;
variant: string;
price: number;
href: string;
/** Already in the shopper's bag. */
inBag: boolean;
}
const PRODUCTS: TaggedProduct[] = [
{
id: 'mug',
name: 'Moss mug',
variant: 'Moss · 350 ml',
price: 28,
href: '/products/moss-mug',
inBag: true,
},
{
id: 'vase',
name: 'Ash bud vase',
variant: 'Ash · 14 cm',
price: 34,
href: '/products/ash-bud-vase',
inBag: false,
},
];
export function TaggedProductExample(): JSX.Element {
return (
<ul className="nzp-tagged-product" aria-label="In this post">
{PRODUCTS.map((product) => (
<li key={product.id} className="nzp-tagged-product-row">
<span className="nzp-tagged-product-thumb" aria-hidden="true" />
<span className="nzp-tagged-product-info">
<Text
render={<Link href={product.href} />}
className="nzp-tagged-product-name"
variant="label-m-strong"
>
{product.name}
</Text>
<Text render={<span />} tone="secondary">
{product.inBag ? `${product.variant} · In your bag` : product.variant}
</Text>
<Text render={<span />} variant="numeric-m">
<NumberValue value={product.price} format={USD} />
</Text>
</span>
<IconButton
label={`Add ${product.name} to bag`}
icon={product.inBag ? <Check /> : <Plus />}
variant={product.inBag ? 'primary' : 'secondary'}
pressed={product.inBag}
/>
</li>
))}
</ul>
);
}
Stylesheet
@layer nz.tokens, nz.themes, nz.reset, nz.base, nz.components, nz.patterns, nz.utilities;
@layer nz.patterns {
.nzp-tagged-product {
display: flex;
flex-direction: column;
gap: var(--nz-space-2);
max-inline-size: 23.75rem;
margin: 0;
padding: 0;
list-style: none;
}
.nzp-tagged-product-row {
display: flex;
align-items: center;
gap: var(--nz-space-3);
padding: var(--nz-space-3);
border: var(--nz-border-width-default) solid var(--nz-border-subtle);
border-radius: var(--nz-radius-lg);
background: var(--nz-bg-surface);
}
.nzp-tagged-product-thumb {
flex: none;
inline-size: 4rem;
block-size: 4rem;
border-radius: var(--nz-radius-md);
background: var(--nz-bg-muted);
}
.nzp-tagged-product-info {
display: flex;
flex: 1;
flex-direction: column;
gap: var(--nz-space-0-5);
min-inline-size: 0;
}
.nzp-tagged-product-name {
align-self: flex-start;
border-radius: var(--nz-radius-xs);
text-decoration: none;
&:hover {
text-decoration: underline;
}
&:focus-visible {
outline: var(--nz-border-width-focus) solid var(--nz-border-focus);
outline-offset: 2px;
}
}
}
Notes
- Named action, 44 px on touch. The IconButton's name is "Add Moss mug to bag", and its
Tooltip shows the same words. It is 40 px, Figma's size, and its hit area grows to 44 px on a
coarse pointer (Figma's 40 px button is under
size/touch-min, §3.4). - Added, in words too. The button is a toggle:
aria-pressedsays whether the product is in the bag, the check replaces the plus, and the row says "In your bag" next to the variant. Figma shows the added state only by the icon and the fill (§3.4). When the shopper presses it, announce the change ("Added Moss mug to bag") with the app's live region or a Toast. - Where it differs from Figma. The variant line is
text/secondary: Figma'stext/tertiaryis 4.03 to 4.31:1, under AA for 14 px text (§3.4). The product's name is the link to its page; the row is not one big link, so the button is not nested in it.