Top app bar
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.
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);
<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>
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>
);
}
<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>
<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>
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.
import { createTopAppBar } from 'mtrl';
const topBar = createTopAppBar({ type: 'large', title: 'Photos', scrollThreshold: 8 });
document.body.append(topBar.element);
<m-top-app-bar type="large" headline="Photos" scroll-threshold="8"></m-top-app-bar>
import { TopAppBar } from 'mtrl/react';
export function Example() {
return (
<TopAppBar type="large" headline="Photos" scrollThreshold={8} />
);
}
<script setup lang="ts">
import { MTopAppBar } from 'mtrl/vue';
</script>
<template>
<MTopAppBar type="large" headline="Photos" :scroll-threshold="8" />
</template>
<script lang="ts">
import { TopAppBar } from 'mtrl/svelte';
</script>
<TopAppBar type="large" headline="Photos" scrollThreshold={8} />
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.
import { createTopAppBar } from 'mtrl';
const topBar = createTopAppBar({ type: 'medium', title: 'Messages', scrollable: false });
document.body.append(topBar.element);
<m-top-app-bar type="medium" headline="Messages" no-scroll></m-top-app-bar>
import { TopAppBar } from 'mtrl/react';
export function Example() {
return (
<TopAppBar type="medium" headline="Messages" noScroll />
);
}
<script setup lang="ts">
import { MTopAppBar } from 'mtrl/vue';
</script>
<template>
<MTopAppBar type="medium" headline="Messages" no-scroll />
</template>
<script lang="ts">
import { TopAppBar } from 'mtrl/svelte';
</script>
<TopAppBar type="medium" headline="Messages" noScroll />
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'saria-labelnames 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 |