Orders (12 mo)
27
Full-page account shell composing TopNav, an account-section Tabs bar, a main content slot, and the Footer organism — one organism, the whole logged-in account page.
components/organisms/AccountShell/AccountShell.tsxFull-page account shell composing TopNav, an account-section Tabs bar, a main content slot, and the Footer organism — one organism, the whole logged-in account page.
Organism — Layout shell family
The outermost layout for logged-in account surfaces: TopNav (brand, links, search trigger, cart badge, user), an account-section Tabs bar driven by tabs + activeTab + onTabChange, a children main slot (<main id="main-content">, max-w-4xl), and the Footer organism. The top-nav slots and the whole footer are branded through props, with ePassLive defaults.
One shell, the whole account page: TopNav (user + cart), the account-section tabs bar, the main slot, and the Footer organism — switch sections below. Tab into the demo to see the skip link surface.
Page content lands in the max-w-4xl main slot — the route body, not another frame. The tabs above are controlled: activeTab + onTabChange keep section switching in parent state.
Orders (12 mo)
27
Upcoming tickets
4
Pending transfers
1
This component supports light and dark mode.
Keep one shell per account route family and drive section switching through the controlled tabs trio — tabs, activeTab, onTabChange. Brand the top nav through its slots (user, logo, navLinks, onSearchOpen, cartCount) and the footer through its data props; omitted props fall back to the ePassLive defaults (or empty rows).
import { AccountShell } from '@epasslive/design-system'
<AccountShell
tabs={tabs}
activeTab="orders"
onTabChange={(id) => router.push(`/account/${id}`)}
user={user}
onSearchOpen={() => setSearchOpen(true)}
cartCount={2}
footerColumns={footerColumns}
>
<h1>{t('ordersTitle')}</h1>
{/* order list */}
</AccountShell>The tabs bar is the account nav: a nav[aria-label="Account sections"] wrapping the Tabs molecule in variant="href" — one controlled state per page, no duplicated markup per route. Children land in <main role="main"> ( max-w-4xl); className appends to that main region only. Don't hand-roll a second top nav or footer inside children — the shell renders both.
A skip link is the first focusable element, targeting #main-content (WCAG 2.4.1 bypass blocks). The account sections bar is a labelled nav landmark whose tabs are real anchors when they carry an href and real buttons otherwise (keyboard reachable either way, WCAG 2.1.1). Main content is a <main role="main" tabIndex={-1}> landmark — a programmatic focus target with the stable id main-content (WCAG 1.3.1 / 2.4.1). Social icons are named through their ariaLabel (WCAG 4.1.2). Colors come from tokens, which meet AA contrast in both themes.
tabs*AccountTab[]Account section navigation — { id, label, href? } entries rendered by the Tabs molecule.
activeTab*stringId of the active tab (controlled).
onTabChange*(tabId: string) => voidCalled when the user picks another account section.
user{ name: string; initials: string; href: string } | nullSigned-in user shown by TopNav — null renders the signed-out state.
logoReact.ReactNodeBrand node for the top nav — defaults to a bold "ePassLive" wordmark.
navLinks{ label: string; href: string; active?: boolean }[][]Global links in the top nav (Browse events, …).
onSearchOpen() => voidOpens the search overlay from the top-nav trigger (defaults to a no-op).
cartCountnumber0Cart badge count in the top nav.
footerBrand{ name: string; tagline: string }Footer brand block — defaults to { name: "ePassLive", tagline: "Your gateway to live events" }.
footerColumns{ title: string; links: { label: string; href: string }[] }[][]Footer link columns (Discover, Support, …).
footerSocialFooterSocial[][]Social link icons — platform is one of twitter, instagram, facebook, tiktok, youtube; ariaLabel names each platform.
footerLegalLinksFooterLink[][]Footer legal row (Terms, Privacy, …).
footerCopyrightstringFooter copyright line — defaults to the current year and brand.
children*React.ReactNodeMain content — rendered inside <main id="main-content" role="main">.
classNamestringExtra classes appended to the main content region.
cartAriaLabelstringAccessible name for the cart link, used verbatim (overrides the default "{cartLabel}, {N} items" composition — pass a locale-aware string for FR consumers).
accountLabelstringAccessible name for the user chip, used verbatim (default: "Account: {user.name}").
siteHeaderLabelstringAccessible name for the header landmark.
homeLabelstringAccessible name for the logo home link.
Each entry of the tabs array is an AccountTab. Mind the href trap: entries with an href render as anchors — clicking them navigates natively and onTabChange is not called, so route changes must drive activeTab. Entries without href render as buttons that fire onTabChange (the controlled flow the demo above uses).
id*stringSection identifier — matches activeTab and reaches onTabChange.
label*stringVisible tab label (i18n on the consumer side).
hrefstringOptional href — the tab renders as an anchor instead of a button. Trap: with href set, clicking navigates natively and onTabChange is NOT called; use it only when one tab = one URL.
tabsAccountTab[]—Account section navigation — { id, label, href? } entries rendered by the Tabs molecule in the bar below the top nav.
activeTabstring—Id of the active tab (controlled).
onTabChange(tabId: string) => void—Called when the user picks another account section.
childrenReact.ReactNode—Main content — rendered inside <main id="main-content" role="main">.
user{ name: string; initials: string; href: string } | null—Signed-in user shown by TopNav (avatar initials + name linking to href). null renders the signed-out state.
logoReact.ReactNode—Brand node for the top nav — defaults to a bold "ePassLive" wordmark in the heading style.
navLinks{ label: string; href: string; active?: boolean }[][]Global links in the top nav (Browse events, …).
onSearchOpen() => void—Opens the search overlay from the top-nav trigger (defaults to a no-op).
cartCountnumber0Cart badge count in the top nav.
footerBrand{ name: string; tagline: string }—Footer brand block — defaults to the ePassLive brand name and the "Your gateway to live events" tagline.
footerColumns{ title: string; links: { label: string; href: string }[] }[][]Footer link columns (Discover, Support, …).
footerSocialFooterSocial[][]Social link icons — platform is one of twitter, instagram, facebook, tiktok, youtube; ariaLabel names each platform for screen readers.
footerLegalLinksFooterLink[][]Footer legal row (Terms, Privacy, …).
footerCopyrightstring—Footer copyright line — defaults to the current year and brand.
classNamestring—Extra classes appended to the main content region.
cartAriaLabelstring—Accessible name for the cart link, used verbatim (overrides the default "{cartLabel}, {N} items" composition — pass a locale-aware string for FR consumers).
accountLabelstring—Accessible name for the user chip, used verbatim (default: "Account: {user.name}").
siteHeaderLabelstring—Accessible name for the header landmark.
homeLabelstring—Accessible name for the logo home link.
<AccountShell />import { AccountShell } from "@epasslive/design-system"<AccountShell tabs={tabs} activeTab="orders" onTabChange={(id) => router.push(`/account/${id}`)} user={user} footerColumns={footerColumns}><h1>Orders</h1></AccountShell>