Passer au contenu principal

TopNav

Implemented

Public site top navigation — sticky 64px, Logo + nav links + search + cart + user. Three variants (solid / transparent / solidOnScroll): solidOnScroll is a static translucent backdrop-blur look; TopNav attaches no scroll listener. Mobile: nav links hidden below md — pair with MobileNav for the mobile menu.

Categoryorganisms
Sourcecomponents/organisms/TopNav/TopNav.tsx
AI HintPublic storefront header; the only DS composition is AccountShell — don't hand-roll a second top nav inside AccountShell children. Links hide below md — pair with MobileNav. solidOnScroll is a static backdrop-blur style; the consumer owns the scroll listener. FR consumers: pass cartAriaLabel/accountLabel/siteHeaderLabel/homeLabel.

Sticky public-site top navigation (64px): logo, desktop link row, search trigger, cart link with badge count, and an optional signed-in user chip.

TopNav

Organism — Navigation family (Family 5)

Sticky site header for public storefront pages. Three background variants (solid / transparent / solidOnScroll — solidOnScroll is a static translucent backdrop-blur look; there is no built-in scroll listener, consumers swap variants on their own scroll state). Below the md breakpoint the nav links are hidden (hidden md:block) — pair with MobileNav for the mobile menu.

This component supports light and dark mode.

Usage

import { TopNav } from '@epasslive/design-system'

<TopNav
  logo={<strong>ePassLive</strong>}
  links={[
    { label: 'Events', href: '/events', active: true },
    { label: 'Venues', href: '/venues' },
  ]}
  onSearchOpen={() => openSearch()}
  cartCount={3}
  user={{ name: 'Alex Tremblay', initials: 'AT', href: '/account' }}
/>

// FR consumers: pass the locale-aware label props verbatim
<TopNav ... cartAriaLabel="Panier, 3 billets" accountLabel="Compte : Alex" />

Responsive behavior

Below the md breakpoint the nav links are hidden (hidden md:block) — there is no built-in hamburger button. Pair TopNav with MobileNav for the mobile menu (storefront Header composes exactly this pair). The search, cart, and user-chip actions remain visible at every width.

Composition patterns

  • AccountShell is the only DS composer — it renders TopNav as the account-page header band and forwards the four i18n label props (cartAriaLabel, accountLabel, siteHeaderLabel, homeLabel). Don't hand-roll a second top nav inside its children.
  • The storefront site header intentionally diverges — SiteHeader is built on the DS Header organism (DS-CORR-09-13), not on TopNav. The two coexist by design.
  • Reference consumer: the storefront /docs header (packages/storefront/src/components/docs/TopNav.tsx) wraps TopNav with locale-aware labels from the topnav.* message namespace — copy it when embedding TopNav in a localized app.
  • FR/EN labels: the English defaults are baked in; pass the label props above to get translated aria strings (cartAriaLabel / accountLabel are used verbatim).

Accessibility

The root is a <header role="banner"> with an accessible name (siteHeaderLabel, default "Site header"). The primary nav landmark is named via ariaLabel (default "Primary"), the active link carries aria-current="page", and every icon-only control (search, cart, user chip, logo) exposes an accessible name through aria-label per WCAG 4.1.2. The cart, account, header, and home labels can each be overridden verbatim for locale-aware consumers. Interactive icons are decorative (aria-hidden) and focus rings / the active-link underline use the ep-text-teal token, which meets WCAG AA non-text contrast (5.45:1) in the light theme.

Props

Prop
Type
Default
Description
logo*
React.ReactNode
—

Brand node rendered inside the home link.

links*
TopNavLink[]
—

Primary nav links; the entry with active: true gets aria-current="page".

onSearchOpen*
() => void
—

Fires when the search button is clicked — open your SearchOverlay here.

cartCount*
number
—

Badge count on the cart link; 0 hides the badge.

user
TopNavUser | null
—

Signed-in account chip; null/undefined renders the signed-out state.

searchLabel
string
'Open search'

Accessible name for the search button.

cartLabel
string
'Cart'

Accessible name prefix for the cart link.

cartAriaLabel
string
—

Full accessible name for the cart link, used verbatim — overrides the default "{cartLabel}, {N} items" composition (locale-aware labels).

accountLabel
string
—

Full accessible name for the user chip, used verbatim (default: "Account: {user.name}").

siteHeaderLabel
string
'Site header'

Accessible name for the header landmark.

homeLabel
string
'Home'

Accessible name for the logo home link.

ariaLabel
string
'Primary'

Accessible name for the primary nav landmark.

variant
'solid' | 'transparent' | 'solidOnScroll'
'solid'

Header background style; solidOnScroll is a static translucent backdrop-blur look.

className
string
—

Additional classes merged onto the header element.

id
string
auto tn-<useId>

Stable id for the header element.

Props

Preview

TopNav

Prop
Type
Required
Default
Description
logo
React.ReactNode
Required
—

Brand node rendered inside the home link.

links
TopNavLink[]
Required
—

Primary nav links; the entry with active: true gets aria-current='page'.

onSearchOpen
() => void
Required
—

Fires when the search button is clicked — open your SearchOverlay here.

cartCount
number
Required
—

Badge count on the cart link; 0 hides the badge.

user
TopNavUser | null
Optional
—

Signed-in account chip; null/undefined renders the signed-out state.

searchLabel
string
Optional
'Open search'

Accessible name for the search button.

cartLabel
string
Optional
'Cart'

Accessible name prefix for the cart link.

cartAriaLabel
string
Optional
—

Full accessible name for the cart link, used verbatim — overrides the default '{cartLabel}, {N} items' composition (locale-aware labels).

accountLabel
string
Optional
—

Full accessible name for the user chip, used verbatim (default: 'Account: {user.name}').

siteHeaderLabel
string
Optional
'Site header'

Accessible name for the header landmark.

homeLabel
string
Optional
'Home'

Accessible name for the logo home link.

homeHref
string
Optional
'/'

Href for the logo home link. Pass a locale-prefixed href (e.g. '/fr') in consumers under a /[locale] segment so the logo click does not drop the locale. Parity with MobileNav.homeHref.

cartHref
string
Optional
'/cart'

Href for the cart link. Pass a locale-prefixed href (e.g. '/fr/cart') when the cart route is localized.

ariaLabel
string
Optional
'Primary'

Accessible name for the primary nav landmark.

variant
"solid" | "transparent" | "solidOnScroll"
Optional
'solid'

Header background style; solidOnScroll is a static translucent backdrop-blur look.

className
string
Optional
—

Additional classes merged onto the header element.

id
string
Optional
auto tn-<useId>

Stable id for the header element.

Quick use:<TopNav />

Accessibility

ARIA rolebanner

Type

1 rule

NASA Rule 5

logo, links, onSearchOpen, cartCount all required

ARIA

1 rule

WCAG 1.3.1, 4.1.2

<header role='banner'>; <nav aria-label='Primary'>; active link aria-current='page'; cart has aria-label='Cart, {N} items' (cartAriaLabel overrides verbatim)

Usage

Import

tsxSnippet
import { TopNav } from "@epasslive/design-system"

Example 1

tsxSnippet
<TopNav logo={<Logo size="sm" />} links={[{ label: "Events", href: "/events", active: true }]} onSearchOpen={openSearch} cartCount={3} />

Example 2

tsxSnippet
<TopNav ... cartAriaLabel="Panier, 3 billets" accountLabel="Compte : Alex" siteHeaderLabel="En-tête du site" homeLabel="Accueil" />

Anti-patterns

— Do not fetch data directly in organisms

— Avoid tight coupling to specific data sources

Design System v4.1.0 | ePassLive