Build / Organisms
SideNav
The desktop navigation rail.
BottomNav's counterpart above the md breakpoint, and a deliberate
sibling of it rather than a port: the two carry different destinations
because the devices are used differently. The rail leads with managing
your own cards and connections; the bar leads with Exchange, which is a
thing you do standing in front of someone with a phone in your hand.
The root is a <nav>, not the <aside> the visual design implies.
<aside> maps to the complementary landmark — "tangentially related to
the main content" — which the app's own navigation is not. The rail look
comes from CSS; the semantics stay honest.
footerItems renders as a second list inside that same landmark, pinned
down by a flexible margin. Splitting it into its own <nav> would either
add an unnamed landmark or a second one competing for the same name, for
a result CSS already gives us.
A destination with an href renders as a real link; without one it
renders as a button. onSelect fires either way, so a router can
intercept the click while the markup stays navigable and shareable.
The active item carries aria-current="page", so it is announced rather
than only colored.
import { SideNav } from '@meetcard/deck'Example
<SideNav currentId="/connections" items={destinations} footerItems={[settings]} />Stories
Source
const Default = () => <SideNav
items={[
{ id: '/', href: '/', label: 'Dashboard', icon: <Icon d="M4 11l8-7 8 7v8a1 1 0 01-1 1h-5v-6h-4v6H5a1 1 0 01-1-1z" /> },
{ id: '/cards', href: '/cards', label: 'My cards', icon: <Icon d="M3 6h18v12H3zM3 10h18M7 14h4" /> },
{ id: '/connections', href: '/connections', label: 'Connections', icon: <Icon d="M4 19v-1a4 4 0 014-4h2a4 4 0 014 4v1M9 10a3 3 0 100-6 3 3 0 000 6zM17 11a3 3 0 100-6" /> },
{ id: '/events', href: '/events', label: 'Events', icon: <Icon d="M4 6h16v14H4zM4 10h16M8 3v4M16 3v4" /> },
]}
currentId="/" />;A count trails the label, where the rail has room for it.
Source
const WithBadge = () => <SideNav
items={destinations.map((d) =>
d.id === '/connections' ? { ...d, badge: 3 } : d,
)}
currentId="/"
footerItems={[
{ id: '/settings', href: '/settings', label: 'Settings', icon: <Icon d="M4 6h16M4 12h16M4 18h16M9 4v4M15 10v4M9 16v4" /> },
]} />;Props
| Prop | Type | Default | Description |
|---|---|---|---|
itemsrequired | SideNavItemProps[] | — | — |
currentId | string | — | id of the active destination. |
footerItems | SideNavItemProps[] | — | Destinations pinned to the foot of the rail, below a flexible gap —
Settings in MeetCard. They are the same kind of thing as items, only
reached less often, so they render inside the same navigation landmark
as a second list rather than in a landmark of their own. |
label | string | 'Global' | Accessible name of the landmark. Deliberately not "Primary": the shell
renders this and BottomNav into the same document, and two navigation
landmarks may not share a name. |
onSelect | (id: string) => void | — | — |
Also accepts everything from Omit, so id, data-* and event handlers pass straight through.