Skip to content
TopClass UI

Navbar

A sticky header whose links are server HTML. One small client part publishes its height as --tc-sticky-top.

split - size not measured yet

Navbar

split - size not measured yet

A sticky header whose links are server HTML. One small client part publishes its height as --tc-sticky-top.

Install

The code

navbar.tsx
import { Button } from 'topclassui/button'
import { NavbarActions, NavbarBrand, NavbarLink, NavbarLinks, NavbarRoot } from 'topclassui/navbar'

<NavbarRoot>
  <NavbarBrand href="/">TopClass UI</NavbarBrand>
  <NavbarLinks>
    <NavbarLink href="/docs">Docs</NavbarLink>
    <NavbarLink href="/components">Components</NavbarLink>
    <NavbarLink href="/icons">Icons</NavbarLink>
  </NavbarLinks>
  <NavbarActions>
    <Button render={<a href="/login" />}>Sign in</Button>
  </NavbarActions>
</NavbarRoot>

The bar writes its own height to --tc-sticky-top, so an #anchor target stops below it, not under it.

Import from the package

topclassui/navbar
import { NavbarRoot, NavbarBrand, NavbarLinks, NavbarLink, NavbarActions } from 'topclassui/navbar'

Install once:npm i topclassui@beta zod

Examples

  • Five flat parts and no bare Navbar: NavbarRoot (a header), NavbarBrand, NavbarLinks (a nav), NavbarLink and NavbarActions.
  • In Next.js, pass the framework link as render={<Link href="/docs" />} on NavbarBrand or NavbarLink. An element only, never a function.
  • sticky={false} lets the bar scroll away with the page, and then it sends no JavaScript to the browser.
  • Without script the variable stays 0px. Set :root { --tc-sticky-top: 3.75rem } in your CSS for one row; the bar replaces it with the measured value.
  • One sticky bar a page: two would write the same variable, so give the second one sticky={false}.
  • A page that scrolls inside a box of your own needs scroll-padding-top: var(--tc-sticky-top) on that box.
  • On a narrow screen the links scroll sideways inside the bar and the actions drop to a second row. The page never scrolls sideways.
basic
with-search
many-links
not-sticky
tall-page
In Arabic, right to left

Props

NavbarRoot

The props of NavbarRoot
PropTypeDefaultLimitsDescription
childrenReactNode--The parts, in order: NavbarBrand, NavbarLinks and NavbarActions. Each one is optional.
stickybooleantrue-NavbarRoot: the bar stays at the top while the page scrolls, and publishes its height as --tc-sticky-top.

NavbarBrand

The props of NavbarBrand
PropTypeDefaultLimitsDescription
childrenReactNode--The name of the site, with a logo before it if there is one.
renderReactElement--The element the brand is drawn as, such as render={<Link href="/" />}. A plain anchor without it.

NavbarLinks

The props of NavbarLinks
PropTypeDefaultLimitsDescription
childrenReactNode--The links: one NavbarLink each.
labelsLabels--The built-in strings; the nav is named by labels.navbar.label. Defaults to English.
localestring--Accepted so {...getLabels(locale)} spreads cleanly. Nothing here formats a number, so it never reaches the element.

NavbarLink

The props of NavbarLink
PropTypeDefaultLimitsDescription
childrenReactNode--The text of the link.
currentbooleanfalse-NavbarLink: the link of the page being read. It sets aria-current="page" and draws the link heavier.
renderReactElement--The element the link is drawn as, such as render={<Link href="/docs" />}. A plain anchor without it.

NavbarActions

The props of NavbarActions
PropTypeDefaultLimitsDescription
childrenReactNode--What sits at the end of the bar: a Button, a search Input, an Avatar.

Keyboard and accessibility

The keyboard of Navbar
KeysWhat it does
TabMoves to the brand, each link, then each action, in reading order.
ShiftTabMoves back.
EnterFollows the focused link.
  • current on one NavbarLink sets aria-current="page". The link is heavier as well as darker, and underlined under forced colours.
  • NavbarLinks is a nav landmark named "Main" from labels.navbar.label. With two on a page, give one an aria-label: yours wins.
  • The link that has the keyboard focus is brought whole into view inside the scrolling list.

Right to left

Under dir="rtl" the brand sits at the right and the actions at the left. Pass labels={ar} to NavbarLinks for the Arabic name.

Server and client

Renders on the server; the interactive part is a client component that ships only when it is used.

Files: index.tsx, client.tsx.

On the chip: split - size not measured yet.