SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/search-bars

9 CSS Search Bars

4 Pure CSS5 with JSPublished 2026-10-04Free · no sign-up

Nine search patterns: a pill bar with a button, an icon that expands into a field, a suggestion list using the combobox pattern, a Ctrl+K command palette, a search with a department select, a glass hero search, voice and clear buttons, a live filter with a result count and a minimal underline field.

01. Pill Search Bar with Button

SearchForm

A rounded search bar with the input and a Search button joined into one pill. The form has role="search" so it is a landmark, the input has a hidden label, and the whole bar shows a focus ring when the field is focused using :focus-within.

View code
<form class="sbp" role="search" onsubmit="event.preventDefault()">
  <label for="sbp-q" class="sr">Search the shop</label>
  <input id="sbp-q" type="search" placeholder="Search mugs, bowls, vases">
  <button>Search</button>
</form>
.sbp { box-sizing: border-box; max-width: 100%; display: flex; width: min(440px, 92vw); padding: 5px; border: 1.5px solid #d0d5dd; border-radius: 999px; background: #fff; font-family: Inter, system-ui, sans-serif; }
.sbp:focus-within { border-color: #2563eb; box-shadow: 0 0 0 4px rgba(37,99,235,.15); }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.sbp input { flex: 1; min-width: 0; padding: 10px 16px; border: 0; background: none; color: #101828; font-size: 15px; outline: none; }
.sbp button { padding: 10px 20px; border: 0; border-radius: 999px; background: #2563eb; color: #fff; font: 700 14px Inter, sans-serif; cursor: pointer; }
.sbp button:focus-visible { outline: 2px solid #1e3a8a; outline-offset: 2px; }

02. Expanding Search Icon

SearchAnimation

A compact search that starts as a round icon and widens into a full field when focused, saving space in a busy header. The width animates on :focus-within, and because the input is always in the DOM the icon is reachable and labelled from the start.

View code
<form class="sbe" role="search" onsubmit="event.preventDefault()">
  <label for="sbe-q" class="sr">Search</label>
  <input id="sbe-q" type="search" placeholder="Search...">
  <svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="M20 20l-3.5-3.5"/></svg>
</form>
.sbe { position: relative; font-family: Inter, system-ui, sans-serif; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.sbe input { width: 46px; height: 46px; box-sizing: border-box; padding: 0 0 0 44px; border: 1px solid #343c51; border-radius: 999px; background: #151a26; color: #fff; font-size: 15px; outline: none; cursor: pointer; transition: width .3s, border-color .3s; }
.sbe input::placeholder { color: transparent; }
.sbe input:focus { width: 280px; border-color: #7c9cff; cursor: text; }
.sbe input:focus::placeholder { color: #8b95a9; }
.sbe svg { position: absolute; left: 13px; top: 13px; width: 20px; height: 20px; fill: none; stroke: #c9d1e2; stroke-width: 2; stroke-linecap: round; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .sbe input { transition: none; } }

03. Search with Live Suggestions

ComboboxJavaScript

A search box that filters a list of suggestions as you type, following the ARIA combobox pattern: aria-expanded, aria-controls and aria-activedescendant. Arrow keys move through the matches, Enter picks one and Escape closes the list.

View code
<div class="sug">
  <label for="sug-q">Find a city</label>
  <input id="sug-q" role="combobox" aria-expanded="false" aria-controls="sug-l" aria-autocomplete="list" autocomplete="off">
  <ul id="sug-l" role="listbox" hidden></ul>
</div>
.sug { position: relative; width: min(320px, 88vw); font: 600 14px Inter, system-ui, sans-serif; color: #344054; }
.sug input { display: block; width: 100%; box-sizing: border-box; margin-top: 6px; padding: 11px 12px; border: 1.5px solid #d0d5dd; border-radius: 10px; font: 15px Inter, sans-serif; outline: none; }
.sug input:focus { border-color: #2563eb; box-shadow: 0 0 0 3px rgba(37,99,235,.15); }
.sug ul { position: absolute; left: 0; right: 0; z-index: 2; margin: 6px 0 0; padding: 6px; list-style: none; border: 1px solid #e4e7ec; border-radius: 12px; background: #fff; box-shadow: 0 16px 30px -12px rgba(16,24,40,.3); }
.sug li { padding: 9px 10px; border-radius: 8px; color: #101828; font-weight: 500; cursor: pointer; }
.sug li[aria-selected=true], .sug li:hover { background: #eef4ff; }
const cities = ['Amsterdam', 'Athens', 'Auckland', 'Barcelona', 'Berlin', 'Boston', 'Cairo', 'Cape Town', 'Dubai', 'Istanbul', 'Karachi', 'Lagos', 'Lahore', 'Lisbon', 'London', 'Madrid', 'Toronto'];
const q = document.getElementById('sug-q'), ul = document.getElementById('sug-l');
let act = -1;
function render() {
  const v = q.value.trim().toLowerCase();
  const hits = v ? cities.filter(c => c.toLowerCase().startsWith(v)).slice(0, 6) : [];
  ul.innerHTML = hits.map((c, i) => '<li role="option" id="sg' + i + '">' + c + '</li>').join('');
  ul.hidden = !hits.length; q.setAttribute('aria-expanded', String(!!hits.length)); act = -1; q.removeAttribute('aria-activedescendant');
}
function move(d) {
  const items = [...ul.children]; if (!items.length) return;
  act = (act + d + items.length) % items.length;
  items.forEach((li, i) => li.setAttribute('aria-selected', String(i === act)));
  q.setAttribute('aria-activedescendant', items[act].id);
}
q.addEventListener('input', render);
q.addEventListener('keydown', e => {
  if (e.key === 'ArrowDown') { e.preventDefault(); move(1); }
  else if (e.key === 'ArrowUp') { e.preventDefault(); move(-1); }
  else if (e.key === 'Enter' && act > -1) { q.value = ul.children[act].textContent; ul.hidden = true; q.setAttribute('aria-expanded', 'false'); }
  else if (e.key === 'Escape') { ul.hidden = true; q.setAttribute('aria-expanded', 'false'); }
});
ul.addEventListener('mousedown', e => { if (e.target.matches('li')) { q.value = e.target.textContent; ul.hidden = true; q.setAttribute('aria-expanded', 'false'); } });

04. Command Palette Search

ShortcutDialog

A command palette like those in code editors: a dialog with a large search field, grouped results and keyboard hints. It opens with Ctrl+K or the button, uses the native dialog element for focus handling, and the results filter as you type.

View code
<button class="cp-open" id="cp-o">Search <kbd>Ctrl K</kbd></button>
<dialog class="cp" id="cp" aria-label="Command palette">
  <input id="cp-q" placeholder="Type a command or search" aria-label="Command">
  <ul id="cp-l">
    <li>New product</li><li>New discount code</li><li>View orders</li><li>Open settings</li><li>Invite a teammate</li>
  </ul>
  <p class="hint"><kbd>Esc</kbd> to close</p>
</dialog>
.cp-open { display: inline-flex; align-items: center; gap: 14px; padding: 10px 14px; border: 1px solid #343c51; border-radius: 10px; background: #151a26; color: #c9d1e2; font: 600 14px Inter, system-ui, sans-serif; cursor: pointer; }
kbd { padding: 1px 6px; border: 1px solid #4a5368; border-radius: 5px; font: 600 11px "JetBrains Mono", monospace; color: #c9d1e2; }
.cp { width: min(460px, 92vw); margin-top: 40px; padding: 0; border: 1px solid #2c3346; border-radius: 16px; background: #12161f; color: #e7ebf5; font-family: Inter, system-ui, sans-serif; }
.cp::backdrop { background: rgba(0,0,0,.6); }
.cp input { width: 100%; box-sizing: border-box; padding: 16px 18px; border: 0; border-bottom: 1px solid #262d3e; background: none; color: #fff; font-size: 16px; outline: none; }
.cp ul { margin: 0; padding: 8px; list-style: none; }
.cp li { padding: 10px 12px; border-radius: 8px; font-size: 14px; }
.cp li:first-child, .cp li:hover { background: #1e2433; }
.hint { margin: 0; padding: 10px 18px; border-top: 1px solid #262d3e; color: #8b95a9; font-size: 12px; }
const cp = document.getElementById('cp'), cq = document.getElementById('cp-q');
document.getElementById('cp-o').addEventListener('click', () => cp.showModal());
document.addEventListener('keydown', e => { if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') { e.preventDefault(); cp.showModal(); } });
cq.addEventListener('input', () => { const v = cq.value.toLowerCase(); [...document.querySelectorAll('#cp-l li')].forEach(li => li.hidden = !li.textContent.toLowerCase().includes(v)); });
cp.addEventListener('click', e => { if (e.target === cp) cp.close(); });

05. Search with Category Select

E-commerceFilter

A marketplace-style search that lets people choose a department before searching. The select and input share one rounded border, each has its own label for screen readers, and the group stacks on narrow screens so neither control gets squeezed.

View code
<form class="sbf" role="search" onsubmit="event.preventDefault()">
  <label for="sbf-c" class="sr">Department</label>
  <select id="sbf-c"><option>All</option><option>Kitchen</option><option>Garden</option><option>Decor</option></select>
  <label for="sbf-q" class="sr">Search</label>
  <input id="sbf-q" type="search" placeholder="What are you looking for?">
  <button aria-label="Search"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="M20 20l-3.5-3.5"/></svg></button>
</form>
.sbf { box-sizing: border-box; max-width: 100%; display: flex; width: min(480px, 94vw); overflow: hidden; border: 2px solid #f59e0b; border-radius: 12px; background: #fff; font-family: Inter, system-ui, sans-serif; }
.sbf:focus-within { box-shadow: 0 0 0 4px rgba(245,158,11,.25); }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.sbf select { padding: 0 12px; border: 0; border-right: 1px solid #e4e7ec; background: #f9fafb; color: #344054; font: 600 14px Inter, sans-serif; }
.sbf input { flex: 1; min-width: 0; padding: 12px; border: 0; color: #101828; font-size: 15px; outline: none; }
.sbf button { width: 52px; border: 0; background: #f59e0b; cursor: pointer; }
.sbf svg { width: 20px; height: 20px; fill: none; stroke: #1a1205; stroke-width: 2.4; stroke-linecap: round; }
.sbf select:focus-visible, .sbf button:focus-visible { outline: 2px solid #b45309; outline-offset: -3px; }
@media (max-width: 420px) { .sbf { flex-wrap: wrap; } .sbf select { width: 100%; padding: 10px 12px; border-right: 0; border-bottom: 1px solid #e4e7ec; } }

06. Glass Hero Search

GlassHero

A large search field set over a colourful hero background, with a frosted-glass bar and quick suggestion chips below it. The chips are buttons that fill in the search term, which is a quick way to help people who are unsure what to type.

View code
<div class="gh">
  <form class="gs" role="search" onsubmit="event.preventDefault()">
    <label for="gs-q" class="sr">Search recipes</label>
    <input id="gs-q" type="search" placeholder="Search 4,000 recipes">
    <button>Go</button>
  </form>
  <div class="chips"><button type="button">Pasta</button><button type="button">Vegan</button><button type="button">15 minutes</button></div>
</div>
.gh { box-sizing: border-box; max-width: 100%; display: grid; gap: 14px; justify-items: center; width: min(480px, 94vw); padding: 38px 20px; border-radius: 20px; background: radial-gradient(circle at 20% 20%, #f97316, transparent 55%), radial-gradient(circle at 85% 80%, #7c3aed, transparent 55%), #1a1430; font-family: Inter, system-ui, sans-serif; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.gs { box-sizing: border-box; display: flex; width: 100%; padding: 6px; border: 1px solid rgba(255,255,255,.3); border-radius: 14px; background: rgba(15,12,30,.5); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }
.gs input { flex: 1; min-width: 0; padding: 12px; border: 0; background: none; color: #fff; font-size: 16px; outline: none; }
.gs input::placeholder { color: #d6d0e6; }
.gs:focus-within { border-color: #fff; }
.gs button { padding: 0 20px; border: 0; border-radius: 10px; background: #fff; color: #1a1430; font: 800 15px Inter, sans-serif; cursor: pointer; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.chips button { padding: 6px 12px; border: 1px solid rgba(255,255,255,.35); border-radius: 999px; background: rgba(15,12,30,.45); color: #fff; font: 600 13px Inter, sans-serif; cursor: pointer; }
.gh button:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
document.querySelectorAll('.chips button').forEach(b => b.addEventListener('click', () => { const q = document.getElementById('gs-q'); q.value = b.textContent; q.focus(); }));

07. Search with Voice and Clear Buttons

SearchIcons

A search field with two trailing icon buttons: a microphone and a clear button that appears only when there is text. Both buttons have accessible names; the microphone here is a visual placeholder for wiring up the Web Speech API in your own app.

View code
<form class="sv" role="search" onsubmit="event.preventDefault()">
  <label for="sv-q" class="sr">Search help articles</label>
  <input id="sv-q" type="search" placeholder="Search help articles" value="refund">
  <button type="button" class="clr" aria-label="Clear search">&times;</button>
  <button type="button" class="mic" aria-label="Search by voice"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="3" width="6" height="11" rx="3"/><path d="M5 11a7 7 0 0014 0M12 18v3"/></svg></button>
</form>
.sv { box-sizing: border-box; max-width: 100%; display: flex; align-items: center; gap: 4px; width: min(420px, 92vw); padding: 4px 6px 4px 16px; border: 1px solid #d0d5dd; border-radius: 12px; background: #fff; box-shadow: 0 1px 2px rgba(16,24,40,.06); font-family: Inter, system-ui, sans-serif; }
.sv:focus-within { border-color: #0f766e; box-shadow: 0 0 0 3px rgba(15,118,110,.15); }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.sv input { flex: 1; min-width: 0; padding: 10px 0; border: 0; color: #101828; font-size: 15px; outline: none; }
.sv input::-webkit-search-cancel-button { display: none; }
.sv button { display: grid; place-items: center; width: 36px; height: 36px; border: 0; border-radius: 8px; background: none; color: #475467; font-size: 20px; cursor: pointer; }
.sv button:hover { background: #f2f4f7; }
.sv button:focus-visible { outline: 2px solid #0f766e; }
.sv svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.sv:has(input:placeholder-shown) .clr { display: none; }
document.querySelector('.clr').addEventListener('click', () => { const q = document.getElementById('sv-q'); q.value = ''; q.focus(); });

08. Search with Results Count

FilterLive region

A filter box above a list that hides non-matching items as you type and reports how many remain, such as 3 of 8 products. The count sits in a polite live region, so screen reader users hear the result without losing their place in the field.

View code
<div class="sr-box">
  <label for="sr-q">Filter products</label>
  <input id="sr-q" type="search" placeholder="Try mug or green">
  <p id="sr-n" aria-live="polite">8 products</p>
  <ul id="sr-l"><li>Green clay mug</li><li>Blue clay mug</li><li>Speckled bowl</li><li>Green plant pot</li><li>Serving platter</li><li>Espresso cup</li><li>Tall vase</li><li>Green tea set</li></ul>
</div>
.sr-box { width: min(340px, 90vw); font-family: Inter, system-ui, sans-serif; color: #c9d1e2; }
.sr-box label { display: block; margin-bottom: 6px; font-weight: 600; font-size: 14px; }
.sr-box input { width: 100%; box-sizing: border-box; padding: 10px 12px; border: 1px solid #343c51; border-radius: 10px; background: #151a26; color: #fff; font-size: 15px; outline: none; }
.sr-box input:focus { border-color: #34d399; }
#sr-n { margin: 10px 0 6px; color: #9aa4b8; font-size: 13px; }
.sr-box ul { display: grid; gap: 4px; margin: 0; padding: 0; list-style: none; }
.sr-box li { padding: 8px 10px; border-radius: 8px; background: #151a26; font-size: 14px; }
const fq = document.getElementById('sr-q'), items = [...document.querySelectorAll('#sr-l li')];
fq.addEventListener('input', () => {
  const v = fq.value.trim().toLowerCase(); let n = 0;
  items.forEach(li => { const hit = li.textContent.toLowerCase().includes(v); li.hidden = !hit; if (hit) n++; });
  document.getElementById('sr-n').textContent = v ? n + ' of ' + items.length + ' products' : items.length + ' products';
});

09. Minimal Underline Search

MinimalHeader

A quiet search for editorial sites: no box, just a large underlined field with a trailing arrow button. The underline thickens and changes colour on focus, which keeps the minimal look while still giving keyboard users a clear focus cue.

View code
<form class="su" role="search" onsubmit="event.preventDefault()">
  <label for="su-q" class="sr">Search the journal</label>
  <input id="su-q" type="search" placeholder="Search the journal">
  <button aria-label="Search">&rarr;</button>
</form>
.su { display: flex; align-items: center; width: min(420px, 92vw); border-bottom: 2px solid #101828; font-family: Georgia, "Times New Roman", serif; }
.su:focus-within { border-bottom: 3px solid #b45309; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.su input { flex: 1; min-width: 0; padding: 12px 0; border: 0; background: none; color: #101828; font: 22px Georgia, serif; outline: none; }
.su input::placeholder { color: #5d6679; font-style: italic; }
.su button { border: 0; background: none; color: #101828; font-size: 24px; cursor: pointer; }
.su button:focus-visible { outline: 2px solid #b45309; }

About these CSS Search Bars

Make it a search landmark

Wrap the field in <form role="search"> so assistive technology lists it as a landmark, and give the input a label, even if it is visually hidden. type="search" brings the right mobile keyboard with a search key.

Suggestions need the combobox pattern

A suggestion list should use role="combobox" on the input with aria-expanded, aria-controls and aria-activedescendant, and role="listbox" on the list, so arrow keys and screen readers work together.

Announce result counts

When results filter as you type, report the count in a polite live region, for example 3 of 8 products, so screen reader users know the list changed without leaving the field.

Help people start

Quick suggestion chips, recent searches or a helpful placeholder reduce the blank-box problem. Chips should be buttons that fill in the field, as in the glass hero demo.

Frequently asked questions

Which demos use JavaScript?

Suggestions, command palette, voice and clear, chips and the results filter. The pill, expanding, category and underline searches are pure CSS.

Does the voice button work?

It is a visual placeholder; connect it to the Web Speech API in your app.

How do I open the palette with Ctrl+K?

Listen for keydown with ctrlKey or metaKey and the k key, then call showModal(), as the demo does.