Storefront bar
The top of a store, with its Avatar, its name and Instagram link, its followers in compact notation, a Follow Button and the bag with its count.
From Figma 10 Instagram Storefront, Storefront Bar (7275:19889, with the orphan master 772:4457
the screens use) and the profile header of the mobile storefront (7275:20706). Uses
Avatar, Badge,
Button, IconButton,
NumberValue, Text, the ShoppingBag
glyph of @nizivo/icons and the Instagram brand glyph of
@nizivo/icons/brands/instagram.
Examples
Store header
Figma 10's Storefront Bar (7275:19889): the top of a store, with its Avatar, its name beside the Instagram glyph (a link to the store's Instagram profile, the glyph's only allowed use), the follower count in compact notation, a Follow Button and the bag. The bag is an IconButton whose name carries the count ("Bag, 2 items"); the count badge on it is decoration. The IconButton carries its Tooltip, a client component. When the bar is too narrow for one row, the actions wrap under the store's name; a long name is cut with an ellipsis.
import type { ComponentPropsWithRef, JSX } from 'react';
import { Avatar } from '@nizivo/react/avatar';
import { Badge } from '@nizivo/react/badge';
import { Button } from '@nizivo/react/button';
import { IconButton } from '@nizivo/react/icon-button';
import { NumberValue } from '@nizivo/react/number-value';
import { Text } from '@nizivo/react/text';
import { ShoppingBag } from '@nizivo/icons';
import { Instagram } from '@nizivo/icons/brands/instagram';
/** 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 Store {
name: string;
/** The store's Instagram profile; the glyph only ever links there. */
instagramUrl: string;
followers: number;
/** Items in the shopper's bag. */
bagCount: number;
}
const STORE: Store = {
name: 'Moss & Kiln',
instagramUrl: 'https://www.instagram.com/',
followers: 12_480,
bagCount: 2,
};
/** "Bag, 2 items": the bag's name says the count, which the badge only shows. */
function bagLabel(count: number): string {
if (count === 0) return 'Bag, empty';
return `Bag, ${count} ${count === 1 ? 'item' : 'items'}`;
}
export function StorefrontBarExample(): JSX.Element {
return (
<header className="nzp-storefront-bar">
<div className="nzp-storefront-bar-identity">
<Avatar name={STORE.name} decorative />
<div className="nzp-storefront-bar-text">
<span className="nzp-storefront-bar-title">
<Text render={<span />} variant="label-l" className="nzp-storefront-bar-name">
{STORE.name}
</Text>
<a
className="nzp-storefront-bar-instagram"
href={STORE.instagramUrl}
aria-label={`${STORE.name} on Instagram`}
>
<Instagram size={16} />
</a>
</span>
<Text render={<span />} tone="secondary" className="nzp-storefront-bar-followers">
<NumberValue
value={STORE.followers}
format={{ notation: 'compact', maximumFractionDigits: 1 }}
/>{' '}
followers
</Text>
</div>
</div>
<div className="nzp-storefront-bar-actions">
<Button variant="primary">Follow</Button>
<span className="nzp-storefront-bar-bag">
<IconButton
label={bagLabel(STORE.bagCount)}
icon={<ShoppingBag />}
render={<Link href="/bag" />}
/>
{STORE.bagCount > 0 && (
<Badge className="nzp-storefront-bar-count" tone="brand" dot={false} aria-hidden="true">
{STORE.bagCount > 99 ? '99+' : STORE.bagCount}
</Badge>
)}
</span>
</div>
</header>
);
}
Stylesheet
@layer nz.tokens, nz.themes, nz.reset, nz.base, nz.components, nz.patterns, nz.utilities;
@layer nz.patterns {
/* One row; when the identity would have less than 11rem, the actions wrap under it. */
.nzp-storefront-bar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--nz-space-3);
padding-block: var(--nz-space-3);
padding-inline: var(--nz-space-4);
border-block-end: var(--nz-border-width-default) solid var(--nz-border-subtle);
background: var(--nz-bg-canvas);
}
.nzp-storefront-bar-identity {
display: flex;
flex: 1 1 11rem;
align-items: center;
gap: var(--nz-space-2);
min-inline-size: 0;
}
.nzp-storefront-bar-text {
display: flex;
flex-direction: column;
min-inline-size: 0;
}
.nzp-storefront-bar-title {
display: flex;
align-items: center;
gap: var(--nz-space-1);
min-inline-size: 0;
}
.nzp-storefront-bar-name,
.nzp-storefront-bar-followers {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 24 px around the 16 px glyph, and 44 px on a coarse pointer. */
.nzp-storefront-bar-instagram {
position: relative;
display: inline-grid;
flex: none;
place-items: center;
inline-size: 1.5rem;
block-size: 1.5rem;
border-radius: var(--nz-radius-full);
color: var(--nz-icon-primary);
&:focus-visible {
outline: var(--nz-border-width-focus) solid var(--nz-border-focus);
outline-offset: 2px;
}
@media (pointer: coarse) {
&::after {
content: '';
position: absolute;
inset: calc((1.5rem - var(--nz-size-touch-min)) / 2);
}
}
}
.nzp-storefront-bar-actions {
display: flex;
flex: none;
margin-inline-start: auto;
align-items: center;
gap: var(--nz-space-2);
}
.nzp-storefront-bar-bag {
position: relative;
display: inline-flex;
}
/* The count sits on the bag's top end corner; the bag's name says it, so it takes no press. */
.nzp-storefront-bar-count {
position: absolute;
inset-block-start: calc(-1 * var(--nz-space-1));
inset-inline-end: calc(-1 * var(--nz-space-1));
min-inline-size: 1.25rem;
justify-content: center;
padding-inline: var(--nz-space-1);
font: var(--nz-font-label-m-strong);
pointer-events: none;
}
}
Notes
- The bag says its count. The IconButton is named "Bag, 2 items" and links to the bag; the count badge on it is hidden from screen readers and takes no press. Past 99 it shows "99+", and with an empty bag it shows nothing. The button is 40 px and its hit area grows to 44 px on a coarse pointer (Figma's bag is 40 px, §3.5).
- The Instagram glyph. Its usage rule allows it only on links that go to Instagram, so here it is a link to the store's profile, named "Moss & Kiln on Instagram", with a 24 px target (44 px on a coarse pointer). Do not use it as decoration.
- Followers.
NumberValuewithnotation: 'compact'prints 12,480 as "12.5K" in English and as the locale writes it elsewhere. - Where it differs from Figma. No search: put a SearchField
between the identity and the actions when the store has one (Figma's mobile search is a bare
20 px icon with no button, §3.5). The Follow Button and the follower count come from the
storefront's profile header (§4), where Figma draws them as custom frames. There is no
Platformvariant: when the bar is too narrow for one row, the actions wrap under the store's name, and a long name is cut with an ellipsis.