App bar

An app bar is a <header> with a row inside it. The pieces are separate so you can arrange them: appBarNav() for links, appBarSpacer() to push what follows to the far end, and appBarActions() for the buttons at the end.

Basic app bar

sitelo
appBar({ brand: 'sitelo' },
  appBarSpacer(),
  appBarActions(
    button({ size: 'sm', variant: 'soft' }, 'Sign in'),
  ),
)

With navigation

navLink() is the link style for a bar; current marks the active page with aria-current as well as colour.

sitelo
appBar({ brand: 'sitelo' },
  appBarNav(
    navLink({ href: '#docs', current: true }, 'Docs'),
    navLink({ href: '#ui' }, 'UI'),
    navLink({ href: '#examples' }, 'Examples'),
  ),
  appBarSpacer(),
  appBarActions(
    button({ size: 'sm', variant: 'outline', color: 'neutral' }, 'GitHub'),
    button({ size: 'sm' }, 'Get started'),
  ),
)

A brand with a mark

The brand takes any markup, and links to / unless href says otherwise.

Ssitelo
v2.6.3
appBar({
  href: '#home',
  brand: stack({ direction: 'row', gap: 'sm', inline: true, align: 'center' },
    avatar({ name: 'S', size: 'sm', square: true, color: 'primary' }),
    'sitelo',
  ),
},
  appBarSpacer(),
  appBarActions(chip({ size: 'sm', color: 'neutral' }, 'v2.6.3')),
)

Sticky and blurred

sticky pins the bar to the top of the scroll container; blur makes it translucent so content passes under it. Both are shown here inside a scrolling box rather than on the page itself.

sitelo
sticky

Scroll me — paragraph 1.

Scroll me — paragraph 2.

Scroll me — paragraph 3.

Scroll me — paragraph 4.

Scroll me — paragraph 5.

Scroll me — paragraph 6.

div({ style: 'height: 12rem; overflow: auto; border: 1px solid var(--su-border); border-radius: 0.6rem' },
  appBar({ brand: 'sitelo', sticky: true, blur: true },
    appBarSpacer(),
    appBarActions(chip({ size: 'sm', color: 'primary' }, 'sticky')),
  ),
  container({ size: 'sm', style: 'padding-block: 1rem' },
    stack({ gap: 'md' },
      ...Array.from({ length: 6 }, (unused, index) =>
        text({ variant: 'small', tone: 'muted' }, 'Scroll me — paragraph ' + (index + 1) + '.'),
      ),
    ),
  ),
)

Out of the way while scrolling

sticky: 'auto' keeps the bar pinned, but slides it away while the page scrolls down and brings it back as soon as it scrolls up, or reaches the top. It stays in view while keyboard focus is inside it, or a menu in it is open.

In Chromium this is all CSS, @container scroll-state(scrolled), and nothing is fetched. Other browsers fetch a small module when the page loads, which watches the scroll instead. The bar follows the page; inside a scrolling box of your own, like the one here, give the box container-type: scroll-state as well.

sitelo
auto

Scroll down, then back up — paragraph 1.

Scroll down, then back up — paragraph 2.

Scroll down, then back up — paragraph 3.

Scroll down, then back up — paragraph 4.

Scroll down, then back up — paragraph 5.

Scroll down, then back up — paragraph 6.

Scroll down, then back up — paragraph 7.

Scroll down, then back up — paragraph 8.

Scroll down, then back up — paragraph 9.

Scroll down, then back up — paragraph 10.

Scroll down, then back up — paragraph 11.

Scroll down, then back up — paragraph 12.

div({ style: 'height: 12rem; overflow: auto; container-type: scroll-state; border: 1px solid var(--su-border); border-radius: 0.6rem' },
  appBar({ brand: 'sitelo', sticky: 'auto', blur: true },
    appBarSpacer(),
    appBarActions(chip({ size: 'sm', color: 'primary' }, 'auto')),
  ),
  container({ size: 'sm', style: 'padding-block: 1rem' },
    stack({ gap: 'md' },
      ...Array.from({ length: 12 }, (unused, index) =>
        text({ variant: 'small', tone: 'muted' }, 'Scroll down, then back up — paragraph ' + (index + 1) + '.'),
      ),
    ),
  ),
)

With a drawer on small screens

The usual pattern: links in the bar on desktop, a button that opens a drawer() on a phone. The drawer is a <dialog> the button opens with a command, so it needs no script.

fragment(
  appBar({ brand: 'sitelo' },
    appBarSpacer(),
    appBarActions(
      themeToggle(),
      iconButton({
        label: 'Open navigation',
        variant: 'ghost',
        color: 'neutral',
        commandfor: 'app-bar-drawer', command: 'show-modal',
        icon: icon('menu'),
      }),
    ),
  ),
  drawer({ id: 'app-bar-drawer', title: 'Navigation' },
    navLink({ href: '#docs' }, 'Docs'),
    navLink({ href: '#ui' }, 'UI'),
    navLink({ href: '#examples' }, 'Examples'),
  ),
)

Props

appBar():

PropTypeDefaultDescription
brandChild—Content of the brand link at the start.
hrefstring'/'Where the brand links to.
stickyboolean | 'auto'falsePins the bar to the top on scroll. 'auto' also hides it while scrolling down.
blurbooleanfalseTranslucent background with a backdrop blur.
asstring'header'Element to render.

The pieces:

PiecePropsDefaultDescription
appBarNav—A nav element holding the links.
appBarSpacer—Flexible gap; everything after it goes to the far end.
appBarActions—Trailing cluster of buttons.
navLinkhref, current, color—A link styled for the bar; current marks the active page.