Build a top app bar from plain elements, a <header> whose child is a
<nav>. The heading is the headline.
Icon-only links and buttons are the leading and trailing actions.
A <menu> (or <ul>) holds text
destinations. There is no navbar, nav-wrapper,
or brand-logo class. To switch between app views at
the bottom of the screen, use a
navigation bar.
Tokens follow the
M3 app bar spec.
The container is surface at rest. The headline is
on-surface at title-large. Icons are
24dp in a 48dp target, inset 4dp, and inherit the header color,
so a fill and on-* pair stays readable.
The small bar is 64dp tall. Pair a fill utility with its
on-* text class if you want a colored bar.
Top app bars stay pinned to the top by default, without a helper class.
The layout is CSS. AppBar collapses
header.medium and header.large on scroll.
It also opens the related search view when the user selects a search
field in the bar. Navigation rails and tabs are separate components. Do not
nest either one in the header.
Small
The small bar is the default. It has a leading icon, a headline, and trailing actions. DOM order sets the layout. The headline grows, so anything after it sits on the end.
<header>
<nav aria-label="Main">
<button type="button" aria-label="Menu">
<span class="material-symbols" aria-hidden="true">menu</span>
</button>
<h2>Title</h2>
<button type="button" aria-label="Search"><span class="material-symbols" aria-hidden="true">search</span></button>
<button type="button" aria-label="More"><span class="material-symbols" aria-hidden="true">more_vert</span></button>
</nav>
</header>
Destinations
Put text links in a <menu>. Place the menu after the
heading to align it on the end, or first to align it on the start.
When the bar has to collapse, hide the menu below the Expanded
breakpoint and use a full-screen navigation dialog or modal expanded
rail.
<header>
<nav aria-label="Main">
<h2>Title</h2>
<menu>
<li><a href="/sass">Sass</a></li>
<li><a href="/components">Components</a></li>
<li><a class="active" href="/javascript">JavaScript</a></li>
</menu>
</nav>
</header>
Put active on the link, not the list item. The state
layer mixes currentColor at 8% hover and 10% active, so
it follows the theme and any fill you put on the header.
Center-aligned
Add center to the header. This takes the headline out of
flow, so the leading and trailing actions can sit on the edges without
shifting it. Keep the title short enough that it does not run under
the icons.
<header class="center">
<nav aria-label="Main">
<button type="button" aria-label="Back">
<span class="material-symbols" aria-hidden="true">arrow_back</span>
</button>
<h2>Title</h2>
<button type="button" aria-label="More"><span class="material-symbols" aria-hidden="true">more_vert</span></button>
</nav>
</header>
Medium and large flexible
These use the same markup as the small bar. Add medium
for the Expressive medium-flexible bar, 112dp with a
headline-medium title on the second row. Add
large for large-flexible, 120dp with
display-small. The deprecated medium and large bars used
headline-small at 152dp. Do not recreate those.
The title wraps to two lines. The order property moves
it onto the bottom row, so the first row can hold the leading icon on
the start and the trailing icons on the end. Add center
as well to center that second-row title. On scroll,
AppBar adds collapsed and the bar becomes
the small 64dp row.
<header class="medium">
<nav aria-label="Main">
<button type="button" aria-label="Back">
<span class="material-symbols" aria-hidden="true">arrow_back</span>
</button>
<h2>Medium title</h2>
<button type="button" aria-label="More"><span class="material-symbols" aria-hidden="true">more_vert</span></button>
</nav>
</header>
<header class="large">…</header>
Subtitle
For a subtitle, wrap the heading and a <p> in an
<hgroup>. That is the HTML for a heading plus a
subheading. There is no subtitle class. Small uses
label-medium. Medium-flexible grows to 136dp and uses
label-large. Large-flexible grows to 152dp and uses
title-medium.
<header class="medium">
<nav aria-label="Main">
<button type="button" aria-label="Back">
<span class="material-symbols" aria-hidden="true">arrow_back</span>
</button>
<hgroup>
<h2>Medium title</h2>
<p>Subtitle</p>
</hgroup>
<button type="button" aria-label="More"><span class="material-symbols" aria-hidden="true">more_vert</span></button>
</nav>
</header>
Fixed
Every top app bar stays pinned with position: sticky
and top: 0. You need no helper class or wrapper.
At rest the bar is surface,
the same as the page. Once content scrolls under it, supporting
browsers fill it with surface-container through
animation-timeline: scroll(), so it stands apart from the
body. M3 Expressive uses this fill instead of a shadow. In browsers
without that API, the bar keeps its resting color.
The documentation header on this site is a fixed small bar. A second fixed bar on this page would sit on top of it, so the live example is the site header itself.
<header>
<nav aria-label="Main">
<h2>Title</h2>
<button type="button" aria-label="Search"><span class="material-symbols" aria-hidden="true">search</span></button>
</nav>
</header>
Color
Put primary on-primary-text, or any fill and
on-* pair, on the header. The default fill is
surface, and color utilities win over it because they
live in the utilities layer. Icons inherit the header color. Set
--md-comp-top-app-bar-trailing-icon-color to
var(--md-sys-color-on-surface-variant) if you want the
spec's muted trailing icons.
<header class="primary on-primary-text">
<nav aria-label="Main">
<h2>Primary</h2>
</nav>
</header>
Component tokens you can set on the header (or on :root):
| Token | Default |
|---|---|
--md-comp-top-app-bar-container-color | --md-sys-color-surface |
--md-comp-top-app-bar-scrolled-container-color | --md-sys-color-surface-container |
--md-comp-top-app-bar-headline-color | --md-sys-color-on-surface |
--md-comp-top-app-bar-leading-icon-color | inherit (spec: on-surface) |
--md-comp-top-app-bar-trailing-icon-color | inherit (spec: on-surface-variant) |
--md-comp-top-app-bar-container-height | 64px |
--md-comp-top-app-bar-leading-icon-size | 24px |
Search
For a search app bar, put a <search class="search-bar">
in the nav in place of the headline. Icons beside it sit outside
the search, and icons inside it stay on the search bar. The input is
centered. Focusing or clicking the field opens a docked
.search-view.
A full-screen <dialog> view takes over the page,
so it opens on a click, Enter or the Down arrow instead of on focus.
Tabbing through the bar does not open it, and focus coming back after
it closes does not reopen it. Query text and application-managed
focus are preserved.
<header>
<nav aria-label="Main">
<button type="button" aria-label="Menu">
<span class="material-symbols" aria-hidden="true">menu</span>
</button>
<search class="search-bar" aria-label="Search">
<span class="material-symbols" aria-hidden="true">search</span>
<input type="search" placeholder="Search" aria-label="Search" aria-controls="search-results">
</search>
</nav>
</header>
<dialog id="search-results" class="search-view full-screen" aria-label="Search">
<form method="dialog">
<input type="search" aria-label="Search query">
<button type="submit">Close search</button>
</form>
</dialog>
A <form> in the nav still fills the space between
the leading action and anything after it. The input sits unstyled
in the bar and needs no extra field class. Its text uses the complete
body-large type role, including letter-spacing, and follows overrides
of that role's type tokens.