A .bottom-app-bar is an 80dp row of actions pinned to
the bottom of the screen: the commands that belong to the screen the
reader is already on, plus, optionally, that screen's
FAB.
Anatomy is the HTML. Direct <a> or
<button> children holding a single icon are the
actions; a .button.extra.circle child is the FAB and is
pushed to the end. Every action is icon-only, so every one of them
carries an aria-label — that is the only name it has.
<div class="bottom-app-bar">
<button type="button" aria-label="Check">
<span class="material-symbols" aria-hidden="true">check_box</span>
</button>
<button type="button" aria-label="Edit">
<span class="material-symbols" aria-hidden="true">edit</span>
</button>
<button type="button" aria-label="Share">
<span class="material-symbols" aria-hidden="true">share</span>
</button>
<button type="button" aria-label="More options">
<span class="material-symbols" aria-hidden="true">more_vert</span>
</button>
</div>
With a FAB
Add the screen's FAB as the last child and it goes to the end of the
bar on its own — there is no spacer element to write. Inside the bar
it is flat and secondary-container rather than lifted and
primary-container: the bar already carries the elevation,
and a second shadow inside it reads as a sticker.
<div class="bottom-app-bar">
<button type="button" aria-label="Check">
<span class="material-symbols" aria-hidden="true">check_box</span>
</button>
<button type="button" aria-label="More options">
<span class="material-symbols" aria-hidden="true">more_vert</span>
</button>
<button type="button" class="button extra circle" aria-label="New message">
<span class="material-symbols" aria-hidden="true">add</span>
</button>
</div>
The bar is one height whether or not a FAB is in it. Material's
72dp with-fab height is deprecated: 80dp, content
vertically centred, both ways.
Pinning it
The bar is an ordinary block by default, so it sits wherever you put
it — which is what the demos above are doing. Add
.fixed to pin it to the bottom edge of the viewport,
clear of the device's own safe area.
<div class="bottom-app-bar fixed">…</div>
A fixed bar covers the last 80dp of the page, so leave that much padding at the bottom of the content behind it.
Or a docked toolbar
M3 Expressive's answer to the same problem is the docked toolbar — a shorter 64dp bar holding the same kind of commands, with the emphasized action inside it rather than a FAB beside them. Both ship. This component is the app-bar-shaped version, and it stays because Material still publishes its tokens.
<div class="toolbar docked">…</div>
Tokens
Set these on the bar itself, or in a rule of your own. They are declared on the component, so a value set on an ancestor element is shadowed by the bar's own default rather than inherited.
| Token | Default |
|---|---|
--md-comp-bottom-app-bar-container-color | var(--md-sys-color-surface-container) |
--md-comp-bottom-app-bar-container-height | 80px |
--md-comp-bottom-app-bar-container-shape | 0 |
--md-comp-bottom-app-bar-icon-color | var(--md-sys-color-on-surface-variant) |
--md-comp-bottom-app-bar-icon-size | 24px |
--md-comp-bottom-app-bar-action-size | 48px |
--md-comp-bottom-app-bar-leading-space | 4px |
--md-comp-bottom-app-bar-trailing-space | 4px |
--md-comp-bottom-app-bar-fab-container-color | var(--md-sys-color-secondary-container) |
--md-comp-bottom-app-bar-fab-trailing-space | 16px |
--md-comp-bottom-app-bar-z | 997 |
The leading and trailing space is 4dp, not 16: an action is a 48dp target around a 24dp glyph, so it brings 12dp of its own and the glyph lands 16dp from the container edge.
The first six names are Material's own
(md.comp.bottom-app-bar.* — the container is the whole
family it publishes). action-size,
fab-trailing-space and -z are this
framework's: the actions are icon buttons and the FAB is a FAB, so
Material states their values under those components instead.