A <search> with search-bar on it
is the bar: a leading icon or icon button, an
<input type="search">, and whatever trailing
actions the query needs. The element is the landmark — there is
no role="search" to add.
<search class="search-bar" aria-label="Search">
<span class="material-symbols" aria-hidden="true">search</span>
<input type="search" aria-label="Search recipes" placeholder="Search recipes">
<button type="button" class="icon-button" aria-label="Filters">
<span class="material-symbols" aria-hidden="true">tune</span>
</button>
</search>
The input carries none of the text-field chrome — no underline,
no floating label, no container of its own. The bar is the
container, so the input goes in bare rather than inside a
.field.
Spacing follows what is at each end: a bare glyph sits 16dp from the edge, an icon button 4dp, because the button already insets its own glyph by 12dp. Nothing to set — the bar reads its own children.
.searchbar, the pre-1.0 name, still reaches the
same rules.
A leading action
When the bar opens a drawer or goes back, the leading glyph becomes an icon button. The bar closes up around it on its own.
<search class="search-bar" aria-label="Search">
<button type="button" class="icon-button" aria-label="Open navigation">
<span class="material-symbols" aria-hidden="true">menu</span>
</button>
<input type="search" aria-label="Search mail" placeholder="Search mail">
<button type="button" class="icon-button" aria-label="Account">
<span class="material-symbols" aria-hidden="true">account_circle</span>
</button>
</search>
An <img> in the bar is the account avatar M3
puts in that slot — 30dp and circular. Put it inside the button
when it is tappable, so the 48dp target comes from the button.
Docked view
The docked view is a .search-view inside the bar. It
hangs off the bar, so the bar keeps its
position: relative and the view needs no coordinates
of its own. Show and hide it with the
hidden attribute.
It is a plain element rather than a <dialog> on
purpose: dialog.show() moves focus into the dialog,
which would take the caret out of the input the user is typing in.
The demo below starts open so the surface is on the page; the
sample under it has the hidden a real page would
write. Focus the input to reopen it.
<search class="search-bar" aria-label="Search">
<span class="material-symbols" aria-hidden="true">search</span>
<input type="search" aria-label="Search fruit" placeholder="Search fruit"
onfocus="document.getElementById('results').hidden = false">
<button type="button" class="icon-button" aria-label="Close search"
onclick="document.getElementById('results').hidden = true">
<span class="material-symbols" aria-hidden="true">close</span>
</button>
<div class="search-view" id="results" hidden>
<ul class="list">
<li><a href="/apricot"><span class="material-symbols" aria-hidden="true">history</span><span>Apricot</span></a></li>
</ul>
</div>
</search>
Full-screen view
Full screen, the view is a <dialog> opened with
showModal(). Escape and a tap outside come from the
platform, so there is nothing to wire up beyond opening it. Its
header is another .search-bar — same markup, with the
pill and the shadow taken off.
<button type="button" onclick="document.getElementById('search-full').showModal()">
Search
</button>
<dialog class="search-view full-screen" id="search-full" aria-label="Search">
<search class="search-bar" aria-label="Search">
<form method="dialog">
<button type="submit" class="icon-button" aria-label="Back">
<span class="material-symbols" aria-hidden="true">arrow_back</span>
</button>
</form>
<input type="search" autofocus aria-label="Search fruit" placeholder="Search fruit">
</search>
<hr>
<ul class="list">
<li><a href="/apricot"><span class="material-symbols" aria-hidden="true">history</span><span>Apricot</span></a></li>
</ul>
</dialog>
autofocus on the input matters here.
showModal() focuses the first focusable descendant,
which is the back button — so without it a keyboard user lands on
Back and cannot type.
Full screen at Compact and docked from Medium up is the M3 rule of
thumb, and the app picks: there is no breakpoint
in the sheet that swaps one for the other. The two are not
interchangeable markup — the docked view is a plain element
nested inside the bar, the full-screen one a top-level
<dialog> — so render the one the window calls
for.
if (window.matchMedia('(width < 600px)').matches) {
document.getElementById('search-full').showModal();
} else {
document.getElementById('results').hidden = false;
}
Suggestions are Autocomplete
Search does not ship a suggestion list of its own. Put
autocomplete on the bar's input and you get the
combobox, its listbox, and the arrow-key and
aria-activedescendant handling that
Autocomplete already
implements and is tested for.
<search class="search-bar" aria-label="Search">
<span class="material-symbols" aria-hidden="true">search</span>
<input type="search" class="autocomplete" aria-label="Search fruit" placeholder="Search fruit">
</search>
Expressive.Autocomplete.init(document.querySelector('.search-bar input'), {
minLength: 0,
data: [{ id: 'Apricot' }, { id: 'Blackcurrant' }, { id: 'Clementine' }]
});
That is the whole relationship, and it is deliberate. A
.search-view takes no composite role: its contents
are links and buttons reached with Tab, and a second listbox here
would be the same promise as Autocomplete's with no keyboard model
behind it. Use the view for recent searches, filters, and results;
use Autocomplete for a list the user arrows through.
Tokens
Override these on the bar or on the view.
| Token | Default |
|---|---|
--md-comp-search-bar-container-color |
--md-sys-color-surface-container-high |
--md-comp-search-bar-container-height |
56px |
--md-comp-search-bar-container-shape |
9999px (full) |
--md-comp-search-bar-leading-space |
16px, or 4px beside an icon button |
--md-comp-search-bar-trailing-space |
16px, or 4px beside an icon button |
--md-comp-search-bar-icon-label-space |
16px |
--md-comp-search-bar-icon-size |
24px |
--md-comp-search-bar-avatar-size |
30px |
--md-comp-search-view-container-color |
--md-sys-color-surface-container-high |
--md-comp-search-view-container-shape |
12px, 0 full-screen |
--md-comp-search-view-header-height |
56px, 72px full-screen |
--md-comp-search-view-divider-color |
--md-sys-color-outline |
--md-comp-search-view-bar-results-gap |
2px |
--md-comp-search-view-max-height |
60vh, none full-screen |