Build / Organisms
SettingsNav
Switching between the sections of a settings page.
Two controls for one job, chosen by width. From md up it is a list, which
shows every section at once and keeps the current one visible while you read
the panel beside it. Below that it is a <select> — eight destinations
stacked above the content would push the content itself off the screen, and
a native select opens as the platform's own picker, which is the one control
on a phone nobody has to learn.
The swap is CSS, so there is no viewport JS to disagree with the server, and
whichever control is hidden is display: none and therefore out of the
accessibility tree rather than merely invisible.
Groups survive the swap. The headings become <optgroup>s, because the
split between what is yours and what is your company's is the same
information at either width, and dropping it on the smaller screen is the
kind of quiet loss that makes a settings page hard to navigate.
import { SettingsNav } from '@meetcard/deck'Example
<SettingsNav currentId="/settings/profile" groups={[{ label: 'User', items: [...] }]} onSelect={go} />Stories
Desktop. The list shows every section at once, so the current one stays visible while you read the panel beside it.
Source
const Default = () => <Harness
groups={[
{
label: 'User',
items: [
{ id: '/settings/profile', href: '/settings/profile', label: 'Profile', icon: <Icon d="M12 13a4 4 0 100-8 4 4 0 000 8zM5 21a7 7 0 0114 0" /> },
{ id: '/settings/account', href: '/settings/account', label: 'Account', icon: <Icon d="M3 6h18v12H3zM3 10h18M7 14h4" /> },
{ id: '/settings/notifications', href: '/settings/notifications', label: 'Notifications', icon: <Icon d="M18 8a6 6 0 10-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9M10.3 21a2 2 0 003.4 0" /> },
],
},
{
label: 'Admin',
items: [
{ id: '/settings/billing', href: '/settings/billing', label: 'Billing', icon: <Icon d="M2 7h20v11H2zM2 11h20" /> },
{ id: '/settings/company', href: '/settings/company', label: 'Company', icon: <Icon d="M4 21V5a1 1 0 011-1h9a1 1 0 011 1v16M15 9h4a1 1 0 011 1v11M8 8h3M8 12h3M8 16h3" /> },
{ id: '/settings/customization', href: '/settings/customization', label: 'Customization', icon: <Icon d="M12 3a9 9 0 100 18h1.5a2 2 0 001.6-3.2 2 2 0 011.6-3.2H19a2 2 0 002-2A9 9 0 0012 3zM7.5 10.5v.01M12 7.5v.01M16.5 10.5v.01" /> },
{ id: '/settings/integrations', href: '/settings/integrations', label: 'Integrations', icon: <Icon d="M9 3v6M15 3v6M6 9h12v4a6 6 0 01-12 0zM12 19v2" /> },
{ id: '/settings/team', href: '/settings/team', label: 'Team', 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" /> },
],
},
]}
currentId="/settings/profile" />;Choosing a section moves the fill and the announcement together.
Source
const Switching = () => <Harness
groups={[
{
label: 'User',
items: [
{ id: '/settings/profile', href: '/settings/profile', label: 'Profile', icon: <Icon d="M12 13a4 4 0 100-8 4 4 0 000 8zM5 21a7 7 0 0114 0" /> },
{ id: '/settings/account', href: '/settings/account', label: 'Account', icon: <Icon d="M3 6h18v12H3zM3 10h18M7 14h4" /> },
{ id: '/settings/notifications', href: '/settings/notifications', label: 'Notifications', icon: <Icon d="M18 8a6 6 0 10-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9M10.3 21a2 2 0 003.4 0" /> },
],
},
{
label: 'Admin',
items: [
{ id: '/settings/billing', href: '/settings/billing', label: 'Billing', icon: <Icon d="M2 7h20v11H2zM2 11h20" /> },
{ id: '/settings/company', href: '/settings/company', label: 'Company', icon: <Icon d="M4 21V5a1 1 0 011-1h9a1 1 0 011 1v16M15 9h4a1 1 0 011 1v11M8 8h3M8 12h3M8 16h3" /> },
{ id: '/settings/customization', href: '/settings/customization', label: 'Customization', icon: <Icon d="M12 3a9 9 0 100 18h1.5a2 2 0 001.6-3.2 2 2 0 011.6-3.2H19a2 2 0 002-2A9 9 0 0012 3zM7.5 10.5v.01M12 7.5v.01M16.5 10.5v.01" /> },
{ id: '/settings/integrations', href: '/settings/integrations', label: 'Integrations', icon: <Icon d="M9 3v6M15 3v6M6 9h12v4a6 6 0 01-12 0zM12 19v2" /> },
{ id: '/settings/team', href: '/settings/team', label: 'Team', 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" /> },
],
},
]}
currentId="/settings/profile" />;Phone. Eight destinations stacked above the content would push the content
off the screen, so the list becomes the platform's own picker — with the
groups intact as <optgroup>s.
Source
const Mobile = () => <Harness
groups={[
{
label: 'User',
items: [
{ id: '/settings/profile', href: '/settings/profile', label: 'Profile', icon: <Icon d="M12 13a4 4 0 100-8 4 4 0 000 8zM5 21a7 7 0 0114 0" /> },
{ id: '/settings/account', href: '/settings/account', label: 'Account', icon: <Icon d="M3 6h18v12H3zM3 10h18M7 14h4" /> },
{ id: '/settings/notifications', href: '/settings/notifications', label: 'Notifications', icon: <Icon d="M18 8a6 6 0 10-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9M10.3 21a2 2 0 003.4 0" /> },
],
},
{
label: 'Admin',
items: [
{ id: '/settings/billing', href: '/settings/billing', label: 'Billing', icon: <Icon d="M2 7h20v11H2zM2 11h20" /> },
{ id: '/settings/company', href: '/settings/company', label: 'Company', icon: <Icon d="M4 21V5a1 1 0 011-1h9a1 1 0 011 1v16M15 9h4a1 1 0 011 1v11M8 8h3M8 12h3M8 16h3" /> },
{ id: '/settings/customization', href: '/settings/customization', label: 'Customization', icon: <Icon d="M12 3a9 9 0 100 18h1.5a2 2 0 001.6-3.2 2 2 0 011.6-3.2H19a2 2 0 002-2A9 9 0 0012 3zM7.5 10.5v.01M12 7.5v.01M16.5 10.5v.01" /> },
{ id: '/settings/integrations', href: '/settings/integrations', label: 'Integrations', icon: <Icon d="M9 3v6M15 3v6M6 9h12v4a6 6 0 01-12 0zM12 19v2" /> },
{ id: '/settings/team', href: '/settings/team', label: 'Team', 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" /> },
],
},
]}
currentId="/settings/profile" />;The breakpoint's own value — 768px is the first width that gets the list.
Source
const Tablet = () => <Harness
groups={[
{
label: 'User',
items: [
{ id: '/settings/profile', href: '/settings/profile', label: 'Profile', icon: <Icon d="M12 13a4 4 0 100-8 4 4 0 000 8zM5 21a7 7 0 0114 0" /> },
{ id: '/settings/account', href: '/settings/account', label: 'Account', icon: <Icon d="M3 6h18v12H3zM3 10h18M7 14h4" /> },
{ id: '/settings/notifications', href: '/settings/notifications', label: 'Notifications', icon: <Icon d="M18 8a6 6 0 10-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9M10.3 21a2 2 0 003.4 0" /> },
],
},
{
label: 'Admin',
items: [
{ id: '/settings/billing', href: '/settings/billing', label: 'Billing', icon: <Icon d="M2 7h20v11H2zM2 11h20" /> },
{ id: '/settings/company', href: '/settings/company', label: 'Company', icon: <Icon d="M4 21V5a1 1 0 011-1h9a1 1 0 011 1v16M15 9h4a1 1 0 011 1v11M8 8h3M8 12h3M8 16h3" /> },
{ id: '/settings/customization', href: '/settings/customization', label: 'Customization', icon: <Icon d="M12 3a9 9 0 100 18h1.5a2 2 0 001.6-3.2 2 2 0 011.6-3.2H19a2 2 0 002-2A9 9 0 0012 3zM7.5 10.5v.01M12 7.5v.01M16.5 10.5v.01" /> },
{ id: '/settings/integrations', href: '/settings/integrations', label: 'Integrations', icon: <Icon d="M9 3v6M15 3v6M6 9h12v4a6 6 0 01-12 0zM12 19v2" /> },
{ id: '/settings/team', href: '/settings/team', label: 'Team', 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" /> },
],
},
]}
currentId="/settings/profile" />;Props
| Prop | Type | Default | Description |
|---|---|---|---|
groupsrequired | SettingsNavGroup[] | — | — |
currentId | string | — | id of the section being shown. |
label | string | 'Settings' | Accessible name of the landmark. |
onSelect | (id: string) => void | — | — |
selectLabel | string | 'Select settings option' | Accessible name of the narrow-screen control. |
Also accepts everything from Omit, so id, data-* and event handlers pass straight through.