A banner carries an important message at the top of a view, in the flow of the page, with one or two actions on it. It stays until the user acts — nothing dismisses it on a timer, and nothing about it is scripted.
A <div class="banner"> is the container. A
<p> is the message. Everything else is optional
and in this order: a leading
<span class="material-symbols">, a
<div class="actions"> of buttons, and a trailing
.icon-button that closes it. Removing the banner from
the page is what "closing" means, and that is the page's job — there
is no plugin to call.
<div class="banner">
<span class="material-symbols" aria-hidden="true">cloud_off</span>
<p>You're offline. Edits are saved locally and will sync later.</p>
<div class="actions">
<button class="button text" type="button">Retry</button>
</div>
<button class="icon-button" type="button" aria-label="Dismiss">
<span class="material-symbols" aria-hidden="true">close</span>
</button>
</div>
Banner, snackbar or dialog
Three components tell the user something, and the difference between them is how much of the user's attention they are entitled to take. Reach for the least of the three that does the job.
| Component | Lifetime | Blocks the page | Use it when |
|---|---|---|---|
| Snackbar | 4–10 seconds, then gone | No — it floats over the page | Confirming something that already happened. Missing it costs nothing: "Photo saved", "Message sent". |
| Banner | Until the user acts on it | No — it sits in the flow and pushes content down | A condition that persists and that the user can keep working around. Offline, a failed sync, an expiring trial, a cookie choice. |
| Dialog | Until the user answers | Yes — nothing else is reachable | The user cannot continue without deciding, or the decision destroys something. |
Two rules of thumb follow from that. If missing the message is harmless, it is a snackbar — a banner that reports a completed action is a banner that never earns its dismissal. And if the user can usefully carry on with the message on screen, it is not a dialog: blocking the page for something the user is allowed to ignore is how a product teaches people to dismiss its dialogs unread.
One banner at a time, at the top of the content it is about. Two stacked banners are two messages competing, and the second one loses.
Basic
The basic banner is a single row 56dp tall: icon, message, actions, close. The message grows the row when it wraps. Anything you leave out simply is not there — a message on its own is a valid banner.
<div class="banner">
<p>Your trial ends in three days.</p>
<div class="actions">
<button class="button text" type="button">See plans</button>
</div>
</div>
Below 600dp the actions take a line of their own under the message and the row grows to 112dp, because a phone-width row cannot hold a message and two buttons side by side without shrinking the message to nothing.
Standard and vibrant
Two colour variants. A standard banner sits on
surface-container and is the default — quiet enough to
live above content without competing with it. Add
vibrant for primary-container, when the
message is the most important thing on the screen. Both keep their
close button in primary.
<div class="banner vibrant">
<span class="material-symbols" aria-hidden="true">system_update</span>
<p>A new version is ready to install.</p>
<div class="actions">
<button class="button text" type="button">Later</button>
<button class="button text" type="button">Restart</button>
</div>
<button class="icon-button" type="button" aria-label="Dismiss update notice">
<span class="material-symbols" aria-hidden="true">close</span>
</button>
</div>
square flattens the 28dp corners to nothing, for a
banner that runs flush under an
app bar or against the edges of
the window. It applies to the basic banner only: ExpressiveCSS gives the
rich layout one shape and no square counterpart, so
class="banner rich square" is a rich banner with rounded
corners.
<div class="banner square">
<span class="material-symbols" aria-hidden="true">wifi_off</span>
<p>Connection lost. Reconnecting…</p>
</div>
Rich
rich gives the banner a heading, room for a longer
message, and either a 24dp icon or an 80dp image beside it. The
actions move to their own row underneath. Use it when the message
needs a sentence rather than a clause — otherwise the basic row says
the same thing in a third of the height.
<div class="banner rich vibrant">
<span class="material-symbols" aria-hidden="true">shield_person</span>
<h3>We updated our privacy policy</h3>
<p>
Analytics cookies are now off by default, and you can change
what we collect at any time from your account settings.
</p>
<div class="actions">
<button class="button text" type="button">Read the policy</button>
<button class="button text" type="button">Got it</button>
</div>
<button class="icon-button" type="button" aria-label="Dismiss privacy notice">
<span class="material-symbols" aria-hidden="true">close</span>
</button>
</div>
An <img> takes the icon's place. It is drawn as an
80dp square with square corners, cropped to fit, and it needs an
alt — descriptive when it says something the heading and
the message do not, empty when it is decoration, as it is here.
<div class="banner rich">
<img src="/photo-book.jpg" alt="">
<h3>Your photo book is ready</h3>
<p>
Twenty-four pages, printed and bound. It ships within two
business days once you approve the proof.
</p>
<div class="actions">
<button class="button text" type="button">View proof</button>
</div>
</div>
The rich layout is a named grid, so the five parts — leading element,
heading, message, actions, close — can be written in whatever order
reads best and each one collapses its track when it is absent. Pick
the heading level that fits the page's outline; the banner styles
whichever of h1–h6 it finds.
Semantics
A banner is not role="banner". That role is the page
header landmark: a page has one of those, and it may have several
banners, so announcing each one as the site header buries the real
header among them. The
semantics suite
blocks both spellings of that confusion —
<header class="banner"> and
role="banner" on the container.
The action row is .actions and never a
<nav>. Retry, restart and dismiss are commands,
not destinations, and a landmark for them would be one more identical
entry in the landmark menu.
Icons are decoration: the message says what the leading icon says,
and the close button carries its own aria-label, so both
are aria-hidden="true". A banner that appears while the
user is on the page — a connection dropping, a sync failing — should
announce itself: put role="status" on it, which is
polite and does not move focus. A banner that is there when the page
loads has already been read in document order and needs nothing.
Tokens
Set these on the banner itself, or in a rule of your own that picks
out the banners you want to change —
.notice-area .banner { … }. They are declared on the
component, so a value set on an ancestor element is shadowed
by the banner's own default rather than inherited. The colours are
shared by both variants; the geometry belongs to one layout or the
other.
| Token | Default |
|---|---|
--md-comp-banners-color | var(--md-sys-color-surface-container) |
--md-comp-banners-body-text-color | var(--md-sys-color-on-surface) |
--md-comp-banners-title-text-color | var(--md-sys-color-on-surface) |
--md-comp-banners-icon-color | var(--md-sys-color-on-surface) |
--md-comp-banners-close-button-color | var(--md-sys-color-primary) |
--md-comp-banners-basic-height | 56px (112px below 600dp) |
--md-comp-banners-basic-shape | 28px (0 when square) |
--md-comp-banners-basic-leading-space | 4px |
--md-comp-banners-basic-trailing-space | 4px |
--md-comp-banners-basic-top-space | 4px |
--md-comp-banners-basic-bottom-space | 4px |
--md-comp-banners-basic-icon-container-size | 48px |
--md-comp-banners-basic-icon-size | 24px |
--md-comp-banners-basic-body-text-top-space | 14px |
--md-comp-banners-basic-body-text-bottom-space | 14px |
--md-comp-banners-basic-no-icon-body-text-leading-space | 16px |
--md-comp-banners-basic-actions-between-space | 8px |
--md-comp-banners-basic-actions-leading-space | 8px |
--md-comp-banners-basic-actions-trailing-space | 8px |
--md-comp-banners-rich-shape | 28px |
--md-comp-banners-rich-leading-space | 12px |
--md-comp-banners-rich-trailing-space | 12px |
--md-comp-banners-rich-top-space | 12px |
--md-comp-banners-rich-bottom-space | 12px |
--md-comp-banners-rich-icon-size | 24px |
--md-comp-banners-rich-icon-text-space | 4px |
--md-comp-banners-rich-image-size | 80px |
--md-comp-banners-rich-image-text-space | 8px |
--md-comp-banners-rich-leading-element-leading-space | 4px |
--md-comp-banners-rich-with-image-leading-space | 20px |
--md-comp-banners-rich-title-text-top-space | 14px |
--md-comp-banners-rich-title-text-bottom-space | 14px |
--md-comp-banners-rich-actions-between-space | 8px |
--md-comp-banners-rich-actions-top-space | 12px |
The message and the rich heading both carry the body-medium type role — the heading at weight 500, which is Material's emphasized body-medium. Action buttons are ordinary text buttons and take their colour from the theme, not from the banner.