docs / sula / sula-nav
SulaNav
a row of views that behaves like one body of liquid: one view opens into its bar of section tabs, the rest sit collapsed as icon pills, and the shapes merge and split as you move between them.
intensity · assertsrsc · client
provenance
authored in usvalayer
sulaintensity
asserts · one per region. it is the focal pointcomposition
one per page, fixed in a header that centers itsearch and theme controls ride along as satellites, one material with the barnever a second sula in the nav's region; satellites exist so nothing sits beside itnot an in-page tab strip. SulaSegmented switches content, this switches routesa11y
a labellednav landmark · the active tab carries aria-current="page" · melted sides are inert · the canvas is aria-hiddenjest-axedependencies
motion · ogl · sula-core and sula-motion from the same packagelive demo · try it out
customize
fluidfalse mounts no canvas, plain css pills
sidesOpenmelt brand and pills back into the active bar
satellitesa search and a theme control, one material with the bar
shine0 matte glass, 1 full neon rim
mergeRadiushow eagerly the parts merge, in px
revealDelayms after landing before the sides emerge
usage
import { SulaNav } from "@usva-ui/react/sula/sula-nav";
import Link from "next/link";
<header className="fixed inset-x-0 top-0 z-50 flex justify-center p-4">
<SulaNav
linkComponent={Link}
brand={<span>acme</span>}
brandLabel="acme home"
activeView={view}
onViewChange={setView}
activeItem={section}
onNavigate={setSection}
views={views}
/>
</header>props
| prop | type | default | notes |
|---|---|---|---|
| views | SulaNavView[] | — | each entry is { href, label, icon, items? }. one expands to its bar of section tabs; the rest collapse to icon pills. |
| activeView | string | first view | controlled: the href of the expanded view. derive it from your router. |
| onViewChange | (href: string) => void | — | fires when a collapsed view pill is clicked; the old bar melts down while the clicked one swells up. |
| activeItem | string | — | controlled: the active section tab inside the expanded view. drive it from a scroll-spy. |
| onNavigate | (href: string) => void | — | fires on a section-tab click, alongside the link's own navigation. |
| linkComponent | React.ElementType | "a" | pass next/link or a NavLink. |
| brand / brandHref / brandLabel | ReactNode · string · string | brandHref: "/" | the leftmost pill: a wordmark linking to brandHref. needs brandLabel to be named. |
| satellites | SulaNavSatellite[] | — | fields that split off the body and settle in a corner: search, theme. handing them here keeps them one material with the bar. |
| labelsFrom | "sm" | "md" | "lg" | "xl" | "sm" | below this width the item labels fold away and the tabs are icons. |
| collapseBelow | "sm" | "md" | "lg" | — | below this width the routes and satellites fold into a single menu droplet and the body swells open into a panel. nothing is hidden. |
| menuLabel | string | "Menu" | accessible name of the menu droplet. |
| offset | number | 0 | vertical nudge in px from the nav's anchor: positive is down. |
| ariaLabel | string | "Primary" | names the nav landmark. |
| fluid | boolean | true | false renders plain CSS pills and mounts no canvas. reduced motion and missing WebGL2 take the same path. |
| backdrop / tint / accentColor | string | bg · surface · accent tokens | what the glass tints against, the glass itself, and the rim light. re-read on theme change. |
| shine | number | theme | 0 is flat matte glass, 1 is the full neon rim. dark themes glow, pale ones stay subtle. |
| mergeRadius | number | 14 | how eagerly the parts merge, in pixels. |
| revealDelay | number | 120 | milliseconds after the bar lands before the sides emerge. |
| sidesOpen | boolean | true | whether the brand, pills and satellites are out. toggle from scroll to melt everything but the active bar back in; a hidden part is not tabbable. |
get it
bun add @usva-ui/reactusage
import { SulaNav } from "@usva-ui/react/sula/sula-nav";
import Link from "next/link";
<header className="fixed inset-x-0 top-0 z-50 flex justify-center p-4">
<SulaNav
linkComponent={Link}
brand={<span>acme</span>}
brandLabel="acme home"
activeView={view}
onViewChange={setView}
activeItem={section}
onNavigate={setSection}
views={[
{
href: "/",
label: "Site",
icon: <HomeIcon />,
items: [
{ href: "#home", label: "Home" },
{ href: "#work", label: "Work" },
],
},
{ href: "/play", label: "Playground", icon: <SparkIcon /> },
]}
/>
</header>