Skip to content
EspañolEnglish
All components (204)

Side navigation

Navigation · SideNav · .amset-nav

What it is and when to use it

The side menu of an application, with the AMSET menu design: each entry is an icon in its tile with the name in capitals and a line saying what is there; the current page lights up with the accent (gradient, edge, glowing bar on the border and the tile lit up); groups unfold under their header, and when collapsed what remains is the column of tiles with whatever is pending in a bubble.

When to use it

  • As the main menu of a management panel, on the left and always in view.
  • description on a link or a group for the line below the name ("To prepare and shipped"): it orients newcomers without having to go in.
  • A group ({ key, label, icon, links: [...] } instead of href) to bring sibling screens together under a header that unfolds, like "Marketing" with its campaigns and its coupons. If the current page is inside, it comes out open.
  • counter on a link for what is waiting for someone (orders to prepare, overdue invoices).
  • collapsed when the screen needs the width: the tiles remain, each with its name and its tooltip, and the counter moves up to a bubble with the number. With onCollapsedChange the menu carries its own collapse and expand button in the header; the state is still held by whoever mounts it.
  • header and footer as a function (({ collapsed }) => …) to draw the full brand when open and only its seal in tiles. collapsed also arrives as true on a narrow screen, where the menu stays in tiles on its own: that way the name is not cut off.
  • onNavigate with routers like Next or React Router: it receives the key and the href of the pressed link and is called instead of following the link (the browser click is cancelled). With Ctrl, Cmd, Shift, Alt or the middle button the link opens in another tab or window, like any other, and onNavigate is not called. If the application already puts the library's RouterProvider at the root, the links navigate with it and onNavigate is unnecessary.

When not to

  • For the main links of a public website, on top and horizontal: TopBar.
  • For the path to the current screen: Breadcrumbs.
  • For the index of a long page: TableOfContents.

Keyboard: Tab goes through the links and the group headers in order. Enter follows a link (with onNavigate, Enter also hands it to the router); Enter or Space open and close a group, and the next Tab enters its links. A link with disabled does not take focus and is announced as unavailable. When collapsed, the tooltip with the name also appears when arriving by keyboard.

Accessibility: it is a nav with a required name (label), with real lists and an h2 per section title. The current page carries aria-current="page" in addition to the background and the bar; the header of the group that contains it is marked, but without aria-current, which belongs only to the link. The header of a group is a <button> with aria-expanded and aria-controls. The description is announced as a description (aria-describedby), not inside the name. The name of each link also carries its counter, with the number written in the language and in singular or plural ("Orders 4 pending", "Notices 1 pending"), open and collapsed: when collapsed, the label is hidden from view but is still the name (without aria-label, which would cover the counter), and the section titles are still there, hidden. The icons are decorative; without an icon, the collapsed state shows the initial.

Fine-tuning: CSS variables --amset-nav-*: -accent (the color of the whole menu, by default the theme accent), -background (and -fade-color, the same flat color, which covers the glow of "there is more menu below" on reaching the end), -width, -padding, -gap, -radius, -icon-box and -icon-size (the tile and its icon), -font and -description-font, and the lettering of the name: -label-transform (uppercase; none to remove the capitals), -label-spacing and -label-weight. The size (size) changes the width (13, 16 or 19 rem), the tile, the padding, the gap and the font size. Below a screen width of 40rem the menu stays in tiles even without collapsed.

What it looks like

const [activo, setActivo] = useState('orders')
const [plegado, setPlegado] = useState(false)

<SideNav
  label="Main menu"
  active={activo}
  onNavigate={(clave) => setActivo(clave)}
  collapsed={plegado}
  onCollapsedChange={setPlegado}
  header={({ collapsed }) => (collapsed ? <Sello /> : <Marca />)}
  footer={({ collapsed }) => (collapsed ? <Cara /> : <Usuaria />)}
  sections={[
    { key: 'store', title: 'Store', links: [
      { key: 'inicio', label: 'Home', description: 'Daily summary', href: '/', icon: <LayoutDashboardIcon /> },
      { key: 'orders', label: 'Orders', description: 'To prepare and shipped', href: '/pedidos', icon: <CartIcon />, counter: 4 },
      { key: 'marketing', label: 'Marketing', description: 'Campaigns and coupons', icon: <MegaphoneIcon />, links: [
        { key: 'campanas', label: 'Campaigns', href: '/campanas', icon: <MailIcon /> },
        { key: 'cupones', label: 'Coupons', href: '/cupones', icon: <TagIcon /> },
      ] },
    ] },
  ]}
/>

Props

Import it with import { SideNav } from '@amset/ui'. Required props are marked.

PropTypeWhat it does
labelrequiredstringWhich navigation this is. Required: a page can have several.
sectionsrequiredreadonly NavSection[]—
activestringKey of the link of the current page.
classNamestringMerged with the library classes; it does not replace them.
collapsedbooleanIcons only. The label moves to the tooltip and the accessible name.
footerNavSlotAt the very bottom: the user, sign out, the version. As a function, like `header`.
headerNavSlotAbove the links: the logo, a company selector. As a function it receives `{ collapsed }` (also `true` on a narrow screen, where the menu shrinks to tiles on its own) to paint only the emblem instead of cutting off the name.
onCollapsedChange(collapsed: boolean) => voidIf passed, the menu carries its collapse and expand button in the header, and what is asked for arrives here. The state is still kept by whoever mounts the menu, with `collapsed`.
onNavigate(key: string, href: string) => voidSo the project's router (Next, React Router) navigates and not the browser. If passed, a normal click and Enter call here **instead of** following the link: the browser click is cancelled. With Ctrl, Cmd, Shift or Alt, or with the middle button, the link opens in another tab or window like any other and this is not called. If the application already puts the library's `RouterProvider` at the root, links navigate with it without this.
size"sm" | "md" | "lg"The width and the font size: 13, 16 or 19 rem. Below 40 rem of screen the menu stays as icons no matter what, which is all that fits next to the content.

It also accepts the attributes of the element it renders: className, style, data-*, aria-* and its events.

CSS variables

These CSS variables are how you adapt it. Declare them on the component's selector, not on :root: the stylesheet redeclares them in the rule itself, and a declaration on the same element always wins.

VariableDefault
--amset-nav-width16rem
--amset-nav-paddingvar(--amset-space-2)
--amset-nav-gapvar(--amset-space-3)
--amset-nav-fontvar(--amset-text-xs)
--amset-nav-description-fontvar(--amset-text-sm)
--amset-nav-icon-box2.375rem
--amset-nav-icon-size1.125rem
--amset-nav-label-transformuppercase
--amset-nav-label-spacing0.08em
--amset-nav-label-weightvar(--amset-weight-bold)
--amset-nav-accentvar(--amset-accent)
--amset-nav-backgroundvar(--amset-surface)
--amset-nav-fade-colorvar(--amset-surface)
--amset-nav-radiusvar(--amset-radius-lg)

How to change it

.amset-nav {
  --amset-nav-width: ...;
  --amset-nav-padding: ...;
}

In the test bench

Every state and variant, to try one at a time, on its page at ui.amset.tech: