/Documentation

Top app bar

PublishedUpdated

A top app bar shows the current screen's title, a navigation button and the screen's most important actions. M3 has four types: small and center-aligned, one 64dp row, and medium and large, with the headline on a second row, which compress to the small bar as the page scrolls. See the M3 top app bar guidelines.

Usage

The leading button is the navigation icon; the actions go at the trailing end. Icon buttons need ariaLabel to name them.

Vanilla
import { createTopAppBar, createIconButton } from 'mtrl';

const topBar = createTopAppBar({ title: 'Inbox' });
topBar.addLeadingElement(createIconButton({ icon: backIcon, ariaLabel: 'Back' }).element);
topBar.addTrailingElement(createIconButton({ icon: searchIcon, ariaLabel: 'Search' }).element);
topBar.addTrailingElement(createIconButton({ icon: moreIcon, ariaLabel: 'More options' }).element);
document.body.append(topBar.element);
Web Components
<m-top-app-bar headline="Inbox">
  <m-icon-button slot="leading" aria-label="Back"></m-icon-button>
  <m-icon-button slot="trailing" aria-label="Search"></m-icon-button>
  <m-icon-button slot="trailing" aria-label="More options"></m-icon-button>
</m-top-app-bar>

<script type="module">
  const topAppBar = document.querySelector('m-top-app-bar');
  topAppBar.querySelector('m-icon-button[slot="leading"][aria-label="Back"]').setAttribute('icon', backIcon);
  topAppBar.querySelector('m-icon-button[slot="trailing"][aria-label="Search"]').setAttribute('icon', searchIcon);
  topAppBar.querySelector('m-icon-button[slot="trailing"][aria-label="More options"]').setAttribute('icon', moreIcon);
</script>
React
import { TopAppBar, IconButton } from 'mtrl/react';
import { backIcon, searchIcon, moreIcon } from './app';

export function Example() {
  return (
    <TopAppBar headline="Inbox">
      <IconButton slot="leading" icon={backIcon} ariaLabel="Back" />
      <IconButton slot="trailing" icon={searchIcon} ariaLabel="Search" />
      <IconButton slot="trailing" icon={moreIcon} ariaLabel="More options" />
    </TopAppBar>
  );
}
Vue
<script setup lang="ts">
import { MTopAppBar, MIconButton } from 'mtrl/vue';
import { backIcon, searchIcon, moreIcon } from './app';
</script>

<template>
  <MTopAppBar headline="Inbox">
    <template #leading>
      <MIconButton :icon="backIcon" aria-label="Back" />
    </template>
    <template #trailing>
      <MIconButton :icon="searchIcon" aria-label="Search" />
      <MIconButton :icon="moreIcon" aria-label="More options" />
    </template>
  </MTopAppBar>
</template>
Svelte
<script lang="ts">
  import { TopAppBar, IconButton } from 'mtrl/svelte';
  import { backIcon, searchIcon, moreIcon } from './app';
</script>

<TopAppBar headline="Inbox">
  {#snippet leading()}
    <IconButton icon={backIcon} ariaLabel="Back" />
  {/snippet}
  {#snippet trailing()}
    <IconButton icon={searchIcon} ariaLabel="Search" />
    <IconButton icon={moreIcon} ariaLabel="More options" />
  {/snippet}
</TopAppBar>
SolidJS
import { TopAppBar, IconButton } from 'mtrl/solid';
import { backIcon, searchIcon, moreIcon } from './app';

export function Example() {
  return (
    <TopAppBar headline="Inbox">
      <IconButton slot="leading" icon={backIcon} ariaLabel="Back" />
      <IconButton slot="trailing" icon={searchIcon} ariaLabel="Search" />
      <IconButton slot="trailing" icon={moreIcon} ariaLabel="More options" />
    </TopAppBar>
  );
}

Examples

A large bar that compresses

Past scrollThreshold pixels of window scroll, the bar takes its scrolled state: surface-container and one level of elevation. A medium or large bar also compresses to 64dp, its headline moving into the top row as Title Large; compressible: false keeps its height.

Vanilla
import { createTopAppBar } from 'mtrl';

const topBar = createTopAppBar({ type: 'large', title: 'Photos', scrollThreshold: 8 });
document.body.append(topBar.element);
Web Components
<m-top-app-bar type="large" headline="Photos" scroll-threshold="8"></m-top-app-bar>
React
import { TopAppBar } from 'mtrl/react';

export function Example() {
  return (
    <TopAppBar type="large" headline="Photos" scrollThreshold={8} />
  );
}
Vue
<script setup lang="ts">
import { MTopAppBar } from 'mtrl/vue';
</script>

<template>
  <MTopAppBar type="large" headline="Photos" :scroll-threshold="8" />
</template>
Svelte
<script lang="ts">
  import { TopAppBar } from 'mtrl/svelte';
</script>

<TopAppBar type="large" headline="Photos" scrollThreshold={8} />
SolidJS
import { TopAppBar } from 'mtrl/solid';

export function Example() {
  return (
    <TopAppBar type="large" headline="Photos" scrollThreshold={8} />
  );
}

Following another scroller

scrollable: false stops the bar following the window. A bar over its own scrolling container is driven with setScrollState(scrolled); the web component follows the element whose id scroll-target names.

Vanilla
import { createTopAppBar } from 'mtrl';

const topBar = createTopAppBar({ type: 'medium', title: 'Messages', scrollable: false });
document.body.append(topBar.element);
Web Components
<m-top-app-bar type="medium" headline="Messages" no-scroll></m-top-app-bar>
React
import { TopAppBar } from 'mtrl/react';

export function Example() {
  return (
    <TopAppBar type="medium" headline="Messages" noScroll />
  );
}
Vue
<script setup lang="ts">
import { MTopAppBar } from 'mtrl/vue';
</script>

<template>
  <MTopAppBar type="medium" headline="Messages" no-scroll />
</template>
Svelte
<script lang="ts">
  import { TopAppBar } from 'mtrl/svelte';
</script>

<TopAppBar type="medium" headline="Messages" noScroll />
SolidJS
import { TopAppBar } from 'mtrl/solid';

export function Example() {
  return (
    <TopAppBar type="medium" headline="Messages" noScroll />
  );
}

The bar is position: absolute at the top of its container, so that container needs position: relative. setType() switches the type and keeps what is in the leading and trailing containers.

API

Options

Option Type Default Description
type 'small' | 'center' | 'medium' | 'large' 'small' Height and headline placement
title string — The headline
scrollable boolean true Follow window scrolling and toggle the scrolled state
compressible boolean true Let a medium or large bar compress to small once scrolled
scrollThreshold number 4 Pixels of scroll before the scrolled state turns on
onScroll (scrolled: boolean) => void — Called each time the scrolled state flips
tag string 'header' The element to build the bar from
class string — Extra classes on the element
prefix string 'mtrl' Class-name prefix
componentName string 'top-app-bar' Name used in class generation

Methods

Method Returns Description
setTitle(title) / getTitle() TopAppBar / string The headline
addLeadingElement(element) TopAppBar Appends to the leading container
addTrailingElement(element) TopAppBar Appends to the trailing container
setType(type) TopAppBar Switches type, keeping the containers and their contents
setScrollState(scrolled) TopAppBar Turns the scrolled state on or off
getHeadlineElement() HTMLElement The <h1> holding the headline
getLeadingContainer() / getTrailingContainer() HTMLElement The containers
destroy() void Removes the bar and its window scroll listener

Events

The bar emits no events: onScroll reports the scrolled state, once per change.

Accessibility

  • The bar is a <header role="banner"> named "Top app bar"; the web component's aria-label names it instead.
  • The headline is an <h1>: it is the screen's top-level heading, so the screen should not have another. A long headline is cut with an ellipsis and stays whole for assistive tech.
  • The bar has no keyboard behaviour of its own: its buttons are in the tab order, and each icon button needs its ariaLabel.

Styling

The small type has no modifier class. The colors are the theme's: surface behind on-surface, and surface-container once scrolled.

.mtrl-top-app-bar { }
.mtrl-top-app-bar--center, .mtrl-top-app-bar--medium, .mtrl-top-app-bar--large { }
.mtrl-top-app-bar--compressible, .mtrl-top-app-bar--scrolled { }
.mtrl-top-app-bar__leading, .mtrl-top-app-bar__headline, .mtrl-top-app-bar__trailing { }
.mtrl-top-app-bar__row { }   /* medium and large only */

Measurements

Attribute Value
Height 64dp small and center; 112dp medium; 152dp large; 64dp compressed
Headline Title Large (small, center, compressed); Headline Small (medium); Headline Medium (large)
Horizontal padding 16dp, 12dp below the sm breakpoint
Space after the leading button 24dp
Space between actions 8dp
Scrolled Elevation level 1