Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Navbar

Live Demo

Open the demo in a separate tab.

A Navbar is a horizontal layout primitive for organizing content across the top or edge of an interface.

It provides three named sections:

  • left
  • center
  • right

The center section is always centered within the Navbar. The left and right sections work inward from their respective edges.

The Navbar can also contain arbitrary children(), making it useful for simple layouts as well as more structured application navigation.

The Navbar provides the layout. The application provides the navigation.

Basic Usage

Create a Navbar with navbar() and compose content into its sections:

#![allow(unused)]
fn main() {
navbar()
    .left([
        text("My App"),
    ])
    .center([
        text("Dashboard"),
    ])
    .right([
        text("Account"),
    ])
}

The three sections have predictable spatial behavior:

┌──────────────────────────────────────────────────────────┐
│  LEFT                  CENTER                  RIGHT      │
│  ←───────              ───────              ───────→    │
└──────────────────────────────────────────────────────────┘

The center section remains centered in the Navbar rather than simply appearing after the left section.

This makes the layout predictable even when the left and right sections contain different amounts of content.

Named Sections

The three named sections are semantic conveniences for the most common Navbar layout.

Left

left() places content against the left side of the Navbar.

#![allow(unused)]
fn main() {
navbar()
    .left([
        button().text(localize("navigation.menu")),

        text("My App"),
    ])
}

The left section grows inward from the edge toward the center.

Center

center() places content in the center of the Navbar.

#![allow(unused)]
fn main() {
navbar()
    .center([
        text("Dashboard"),
    ])
}

The center section is centered relative to the Navbar itself.

It does not simply follow the left section.

This distinction is important when building predictable application layouts.

right() places content against the right side of the Navbar.

#![allow(unused)]
fn main() {
navbar()
    .right([
        button().text(localize("user.profile")),
    ])
}

The right section grows inward from the right edge toward the center.

Using All Three

A common application Navbar might look like this:

#![allow(unused)]
fn main() {
navbar()
    .left([
        button().text(localize("navigation.menu")),

        text("Beverly"),
    ])
    .center([
        text("Dashboard"),
    ])
    .right([
        button().text(localize("user.profile")),
    ])
}

The resulting structure is conceptually:

LEFT CONTENT        CENTER CONTENT        RIGHT CONTENT
     →                    ●                    ←

The center remains centered even as the left and right content changes.

Children

children() remains the general composition primitive.

#![allow(unused)]
fn main() {
navbar()
    .children([
        text("Beverly"),
        text("Dashboard"),
    ])
}

This is useful when the application does not need the three-section layout.

Named sections are conveniences, not a replacement for children().

A Navbar can therefore be used as either a simple container:

#![allow(unused)]
fn main() {
navbar()
    .children([
        text("My Application"),
    ])
}

or as a structured three-section layout:

#![allow(unused)]
fn main() {
navbar()
    .left(text("Beverly"))
    .center(text("Dashboard"))
    .right(text("Account"))
}

A section can accept one child or many children.

#![allow(unused)]
fn main() {
navbar()
    .left(text("Beverly"))
}
#![allow(unused)]
fn main() {
navbar()
    .left([
        icon("menu"),
        text("Beverly"),
    ])
}

children() is the general primitive. Named sections add useful semantics.

Fixed and Non-Fixed Navbars

A Navbar can either participate in normal layout flow or remain fixed relative to its containing interface.

For a normal Navbar:

#![allow(unused)]
fn main() {
navbar()
    .left(text("Beverly"))
    .center(text("Dashboard"))
    .right(text("Account"))
}

For a fixed Navbar:

#![allow(unused)]
fn main() {
navbar()
    .fixed(true)
    .left(text("Beverly"))
    .center(text("Dashboard"))
    .right(text("Account"))
}

A fixed Navbar remains positioned according to the application’s layout rules rather than moving with ordinary content flow.

The Navbar itself does not decide whether an application should use fixed positioning. It simply provides the layout option.

Styling

Navbar uses the same fluent styling model as other Beverly components.

#![allow(unused)]
fn main() {
navbar()
    .padding(16)
    .height(64)
    .radius(12)
    .left(text("Beverly"))
    .center(text("Dashboard"))
    .right(text("Account"))
}

Layout and presentation remain separate from application behavior.

The Navbar determines where content goes. Its children determine what that content is.

Despite its name, the Navbar does not own application navigation.

Navigation is application behavior.

A navigation control can emit a UI Event or connect directly to an application Command:

#![allow(unused)]
fn main() {
navbar()
    .left([
        button().text(localize("navigation.home"))
            .on("click", Command::Navigation::Home),

        button().text(localize("navigation.settings"))
            .on("click", Command::Navigation::Settings),
    ])
}

The Navbar does not need to know what Command::Navigation::Home does.

It only provides the surface in which the controls are arranged.

The Navbar provides the layout. Buttons provide the interaction. Commands provide the application behavior.

Responsive Content

Because the Navbar is composed from ordinary components, its contents can be data-driven.

#![allow(unused)]
fn main() {
navbar()
    .left(navigation_menu())
    .center(text(page.title()))
    .right(user_actions())
}

Higher-level pieces are ordinary Rust functions:

#![allow(unused)]
fn main() {
fn user_actions() -> impl Component {
    row()
        .children([
            button().text(localize("user.notifications")),

            avatar()
                .src(user.avatar_url())
                .aria(localize("user.avatar.description")),
        ])
}
}

There is no separate Navbar component language.

Higher-level components are ordinary Rust functions that return lower-level components.

Accessibility

The Navbar is a layout primitive. Its children remain responsible for their own interaction semantics.

Interactive elements inside the Navbar should have their own accessible labels and descriptions:

#![allow(unused)]
fn main() {
navbar()
    .left([
        button().text(localize("navigation.menu")),
    ])
    .center([
        text("Dashboard"),
    ])
}

The Navbar itself can also expose accessible semantics when the application needs the navigation surface to be identified:

#![allow(unused)]
fn main() {
navbar()
    .label(localize("navigation.primary.label"))
    .aria(localize("navigation.primary.description"))
    .left([
        text("Beverly"),
    ])
    .center([
        text("Dashboard"),
    ])
}

The exact semantic role belongs to the Navbar’s implementation and context; applications should not need to manually reproduce the underlying layout or accessibility mechanics.

Accessible by construction, not accessible by cleanup.

What Navbar Does Not Do

Navbar does not:

  • own navigation state
  • decide which page is active
  • manage routing
  • own application data
  • define authentication
  • define user permissions
  • replace Buttons or Links
  • require a separate navigation state system

Those concerns belong to the application.

The Navbar is responsible for organizing interface content.

Design Principle

The Navbar deliberately provides only the structure that is difficult to express consistently by hand:

  • content from the left edge
  • content from the center
  • content from the right edge
  • predictable centered positioning
  • optional fixed positioning
  • general children() composition

Everything else remains ordinary Beverly composition and ordinary Rust.

Three sections when you need them. children() when you don’t.

The center stays centered. The edges work inward. The application owns the meaning.