Open demo

CSS code

HTML
<header class="hs">
  <a class="hs-logo" href="#">Docs</a>
  <form class="hs-search" role="search" action="#">
    <label class="hs-sr" for="hs-q">Search the docs</label>
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><circle cx="7" cy="7" r="5" stroke="currentColor" stroke-width="2"/><path d="m11 11 3.5 3.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
    <input id="hs-q" type="search" name="q" placeholder="Search components and templates" aria-keyshortcuts="/">
    <kbd class="hs-kbd" aria-hidden="true">/</kbd>
  </form>
  <button class="hs-toggle" type="button" aria-expanded="false" aria-controls="hs-menu" aria-label="Menu" data-menu-button><i></i><i></i><i></i></button>
  <nav class="hs-menu" id="hs-menu" aria-label="Main">
    <ul>
      <li><a href="#" aria-current="page">Guides</a></li>
      <li><a href="#">API</a></li>
      <li><a href="#">Changelog</a></li>
    </ul>
  </nav>
</header>
CSS
/* Search-first header: the search form takes the flexible middle of the bar and the links sit to the right.
   Below 992px the links fold behind a menu button. "/" focuses the field (see the script). */
.hs{position:sticky;top:0;z-index:10;display:flex;flex-wrap:wrap;align-items:center;column-gap:26px;
  min-height:72px;padding:0 30px;background:#fff;border-bottom:1px solid #e8eaf0;color:#1a1d26}
.hs,.hs *{box-sizing:border-box}
.hs a{text-decoration:none}
.hs ul{list-style:none;margin:0;padding:0}
.hs a:focus-visible,.hs button:focus-visible{outline:2px solid #4f46e5;outline-offset:2px}
.hs-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.hs-logo{font-weight:800;font-size:1.05rem;color:#1a1d26;white-space:nowrap}
.hs-search{position:relative;flex:1;max-width:560px;margin:0}
.hs-search svg{position:absolute;left:15px;top:50%;transform:translateY(-50%);color:#6b7385;pointer-events:none}
.hs-search input{width:100%;margin:0;font:inherit;font-size:.92rem;color:#1a1d26;padding:11px 44px 11px 42px;
  -webkit-appearance:none;appearance:none;border:1px solid #858da1;border-radius:11px;background:#f7f8fb;
  transition:border-color .18s,background-color .18s}
.hs-search input::placeholder{color:#6b7385}
.hs-search input:focus{outline:0;border-color:#4f46e5;background:#fff;box-shadow:0 0 0 3px #e0e7ff}
.hs-kbd{position:absolute;right:11px;top:50%;transform:translateY(-50%);padding:3px 7px;font:inherit;font-size:.72rem;
  color:#6b7385;background:#fff;border:1px solid #dfe3ec;border-radius:6px;pointer-events:none}
.hs-menu{margin-left:auto}
.hs-menu ul{display:flex;gap:2px}
.hs-menu a{display:block;padding:9px 13px;font-size:.88rem;font-weight:600;color:#4b5468;border-radius:8px}
.hs-menu a:hover{background:#f2f4f8;color:#1a1d26}
.hs-menu a[aria-current="page"]{background:#eef2ff;color:#4338ca}
.hs-toggle{display:none;flex:none;width:44px;height:44px;flex-direction:column;align-items:center;justify-content:center;
  gap:5px;padding:0;background:#fff;border:1px solid #d9dfe8;border-radius:10px;cursor:pointer}
.hs-toggle i{display:block;width:20px;height:2px;border-radius:2px;background:#1a1d26;transition:transform .2s,opacity .2s}
.hs-toggle[aria-expanded="true"] i:nth-child(1){transform:translateY(7px) rotate(45deg)}
.hs-toggle[aria-expanded="true"] i:nth-child(2){opacity:0}
.hs-toggle[aria-expanded="true"] i:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
@media (max-width:991.98px){
  .hs{column-gap:12px;min-height:0;padding:10px 16px}
  .hs-kbd{display:none}
  .hs-search input{padding-right:14px}
  .hs-toggle{display:flex}
  .hs-menu{display:none;flex-basis:100%;margin:0;padding:6px 0 12px;border-top:1px solid #e8eaf0}
  .hs-menu[data-open]{display:block}
  .hs-menu ul{flex-direction:column}
  .hs-menu a{padding:11px 12px}
}
@media (prefers-reduced-motion:reduce){.hs *{transition:none!important}}
JavaScript
/* Menu button: toggles aria-expanded on itself and data-open on the menu it controls (aria-controls).
   Esc closes an open menu and returns focus to the button; widening past 992px resets it. */
document.querySelectorAll('[data-menu-button]').forEach(btn => {
  const menu = document.getElementById(btn.getAttribute('aria-controls'));
  if (!menu) return;
  const set = open => { btn.setAttribute('aria-expanded', String(open)); menu.toggleAttribute('data-open', open); };
  btn.addEventListener('click', () => set(btn.getAttribute('aria-expanded') !== 'true'));
  document.addEventListener('keydown', e => {
    if (e.key === 'Escape' && btn.getAttribute('aria-expanded') === 'true') { set(false); btn.focus(); }
  });
  matchMedia('(min-width: 992px)').addEventListener('change', e => { if (e.matches) set(false); });
});

/* "/" moves focus to the search field from anywhere on the page, unless the visitor is already typing. */
document.addEventListener('keydown', e => {
  if (e.key !== '/' || e.ctrlKey || e.metaKey || e.altKey) return;
  if (e.target.closest && e.target.closest('input, textarea, select, [contenteditable]')) return;
  const field = document.getElementById('hs-q');
  if (!field) return;
  e.preventDefault();
  field.focus();
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS, Bootstrap 5.3.8 JS bundle

HTML
<header class="navbar navbar-expand-lg hs">
  <a class="navbar-brand hs-logo" href="#">Docs</a>
  <form class="hs-search" role="search" action="#">
    <label class="visually-hidden" for="hs-q">Search the docs</label>
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><circle cx="7" cy="7" r="5" stroke="currentColor" stroke-width="2"/><path d="m11 11 3.5 3.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
    <input class="form-control" id="hs-q" type="search" name="q" placeholder="Search components and templates" aria-keyshortcuts="/">
    <kbd class="hs-kbd d-none d-lg-block" aria-hidden="true">/</kbd>
  </form>
  <button class="navbar-toggler hs-toggle" type="button" data-bs-toggle="collapse" data-bs-target="#hs-menu" aria-controls="hs-menu" aria-expanded="false" aria-label="Menu"><i></i><i></i><i></i></button>
  <nav class="collapse navbar-collapse" id="hs-menu" aria-label="Main">
    <ul class="navbar-nav ms-lg-auto">
      <li class="nav-item"><a class="nav-link active" href="#" aria-current="page">Guides</a></li>
      <li class="nav-item"><a class="nav-link" href="#">API</a></li>
      <li class="nav-item"><a class="nav-link" href="#">Changelog</a></li>
    </ul>
  </nav>
</header>
CSS
/* Bootstrap .navbar-expand-lg on the <header>, recoloured through the --bs-navbar-* and --bs-nav-link-*
   variables. The search is a .form-control; the links fold into .navbar-collapse below 992px. */
.hs{--bs-navbar-padding-x:30px;--bs-navbar-padding-y:0;--bs-navbar-color:#4b5468;--bs-navbar-hover-color:#1a1d26;
  --bs-navbar-active-color:#4338ca;--bs-navbar-brand-color:#1a1d26;--bs-navbar-brand-hover-color:#1a1d26;
  --bs-navbar-brand-padding-y:0;--bs-navbar-brand-margin-end:0;--bs-navbar-brand-font-size:1.05rem;--bs-navbar-nav-link-padding-x:13px;
  position:sticky;top:0;z-index:10;column-gap:26px;min-height:72px;background:#fff;border-bottom:1px solid #e8eaf0}
.hs a{text-decoration:none}
.hs a:focus-visible,.hs button:focus-visible{outline:2px solid #4f46e5;outline-offset:2px;box-shadow:none}
.hs-logo{font-weight:800}
.hs-search{position:relative;flex:1;max-width:560px}
.hs-search svg{position:absolute;left:15px;top:50%;transform:translateY(-50%);color:#6b7385;pointer-events:none}
.hs-search .form-control{padding:11px 44px 11px 42px;font-size:.92rem;line-height:normal;color:#1a1d26;
  border-color:#858da1;border-radius:11px;background-color:#f7f8fb}
.hs-search .form-control::placeholder{color:#6b7385}
.hs-search .form-control:focus{border-color:#4f46e5;background-color:#fff;box-shadow:0 0 0 3px #e0e7ff}
.hs-kbd{position:absolute;right:11px;top:50%;transform:translateY(-50%);padding:3px 7px;font-family:inherit;font-size:.72rem;
  color:#6b7385;background:#fff;border:1px solid #dfe3ec;border-radius:6px;pointer-events:none}
.hs .navbar-nav{--bs-nav-link-padding-y:9px;--bs-nav-link-font-size:.88rem;--bs-nav-link-font-weight:600;gap:2px}
.hs .nav-link{border-radius:8px}
.hs .nav-link:hover{background:#f2f4f8}
.hs .nav-link.active{background:#eef2ff}
.hs .navbar-toggler{width:44px;height:44px;padding:0;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:5px;background:#fff;border:1px solid #d9dfe8;border-radius:10px}
.hs .navbar-toggler:focus{box-shadow:none}
.hs .navbar-toggler i{display:block;width:20px;height:2px;border-radius:2px;background:#1a1d26;transition:transform .2s,opacity .2s}
.hs .navbar-toggler[aria-expanded="true"] i:nth-child(1){transform:translateY(7px) rotate(45deg)}
.hs .navbar-toggler[aria-expanded="true"] i:nth-child(2){opacity:0}
.hs .navbar-toggler[aria-expanded="true"] i:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
@media (min-width:992px){.hs .navbar-toggler{display:none}}
@media (max-width:991.98px){
  .hs{--bs-navbar-padding-x:16px;--bs-navbar-padding-y:10px;column-gap:12px;min-height:0}
  .hs-search .form-control{padding-right:14px}
  .hs .navbar-nav{--bs-nav-link-padding-x:12px;--bs-nav-link-padding-y:11px;padding:6px 0 12px;border-top:1px solid #e8eaf0}
}
@media (prefers-reduced-motion:reduce){.hs *{transition:none!important}}
JavaScript
/* Bootstrap's Collapse has no Esc handling of its own: close an open navbar menu on Esc
   and return focus to its toggler. */
document.addEventListener('keydown', e => {
  if (e.key !== 'Escape') return;
  document.querySelectorAll('.navbar-collapse.show').forEach(menu => {
    bootstrap.Collapse.getOrCreateInstance(menu, { toggle: false }).hide();
    document.querySelector(`[data-bs-target="#${menu.id}"]`)?.focus();
  });
});

/* "/" moves focus to the search field from anywhere on the page, unless the visitor is already typing. */
document.addEventListener('keydown', e => {
  if (e.key !== '/' || e.ctrlKey || e.metaKey || e.altKey) return;
  if (e.target.closest && e.target.closest('input, textarea, select, [contenteditable]')) return;
  const field = document.getElementById('hs-q');
  if (!field) return;
  e.preventDefault();
  field.focus();
});

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<header class="sticky top-0 z-10 flex min-h-[72px] flex-wrap items-center gap-x-[26px] border-b border-[#e8eaf0] bg-white px-[30px] text-[#1a1d26] max-lg:min-h-0 max-lg:gap-x-3 max-lg:px-4 max-lg:py-2.5">
  <a class="text-[1.05rem] font-extrabold whitespace-nowrap text-[#1a1d26] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#4f46e5]" href="#">Docs</a>
  <form class="relative max-w-[560px] flex-1" role="search" action="#">
    <label class="sr-only" for="hs-q">Search the docs</label>
    <svg class="pointer-events-none absolute top-1/2 left-[15px] -translate-y-1/2 text-[#6b7385]" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><circle cx="7" cy="7" r="5" stroke="currentColor" stroke-width="2"/><path d="m11 11 3.5 3.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
    <input class="w-full appearance-none rounded-[11px] border border-[#858da1] bg-[#f7f8fb] py-[11px] pr-11 pl-[42px] text-[.92rem] text-[#1a1d26] outline-none transition-colors duration-[180ms] placeholder:text-[#6b7385] focus:border-[#4f46e5] focus:bg-white focus:shadow-[0_0_0_3px_#e0e7ff] motion-reduce:transition-none max-lg:pr-3.5" id="hs-q" type="search" name="q" placeholder="Search components and templates" aria-keyshortcuts="/">
    <kbd class="pointer-events-none absolute top-1/2 right-[11px] -translate-y-1/2 rounded-md border border-[#dfe3ec] bg-white px-[7px] py-[3px] font-[inherit] text-[.72rem] text-[#6b7385] max-lg:hidden" aria-hidden="true">/</kbd>
  </form>
  <button class="group flex size-11 flex-none cursor-pointer flex-col items-center justify-center gap-[5px] rounded-[10px] border border-[#d9dfe8] bg-white p-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#4f46e5] lg:hidden " type="button" aria-expanded="false" aria-controls="hs-menu" aria-label="Menu" data-menu-button><i class="block h-0.5 w-5 rounded-sm bg-[#1a1d26] transition-transform duration-200 motion-reduce:transition-none group-aria-expanded:translate-y-[7px] group-aria-expanded:rotate-45"></i><i class="block h-0.5 w-5 rounded-sm bg-[#1a1d26] transition-opacity duration-200 motion-reduce:transition-none group-aria-expanded:opacity-0"></i><i class="block h-0.5 w-5 rounded-sm bg-[#1a1d26] transition-transform duration-200 motion-reduce:transition-none group-aria-expanded:-translate-y-[7px] group-aria-expanded:-rotate-45"></i></button>
  <nav class="ml-auto hidden data-open:block lg:block max-lg:m-0 max-lg:basis-full max-lg:border-t max-lg:border-[#e8eaf0] max-lg:pt-1.5 max-lg:pb-3" id="hs-menu" aria-label="Main">
    <ul class="flex gap-0.5 max-lg:flex-col">
      <li><a class="block rounded-lg px-[13px] py-[9px] text-[.88rem] font-semibold text-[#4b5468] hover:bg-[#f2f4f8] hover:text-[#1a1d26] aria-[current=page]:bg-[#eef2ff] aria-[current=page]:text-[#4338ca] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#4f46e5] max-lg:px-3 max-lg:py-[11px]" href="#" aria-current="page">Guides</a></li>
      <li><a class="block rounded-lg px-[13px] py-[9px] text-[.88rem] font-semibold text-[#4b5468] hover:bg-[#f2f4f8] hover:text-[#1a1d26] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#4f46e5] max-lg:px-3 max-lg:py-[11px]" href="#">API</a></li>
      <li><a class="block rounded-lg px-[13px] py-[9px] text-[.88rem] font-semibold text-[#4b5468] hover:bg-[#f2f4f8] hover:text-[#1a1d26] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#4f46e5] max-lg:px-3 max-lg:py-[11px]" href="#">Changelog</a></li>
    </ul>
  </nav>
</header>
CSS
@theme {
  --breakpoint-lg: 62rem; /* 992px, the same collapse point as Bootstrap's navbar-expand-lg */
}
JavaScript
/* Menu button: toggles aria-expanded on itself and data-open on the menu it controls (aria-controls).
   Esc closes an open menu and returns focus to the button; widening past 992px resets it. */
document.querySelectorAll('[data-menu-button]').forEach(btn => {
  const menu = document.getElementById(btn.getAttribute('aria-controls'));
  if (!menu) return;
  const set = open => { btn.setAttribute('aria-expanded', String(open)); menu.toggleAttribute('data-open', open); };
  btn.addEventListener('click', () => set(btn.getAttribute('aria-expanded') !== 'true'));
  document.addEventListener('keydown', e => {
    if (e.key === 'Escape' && btn.getAttribute('aria-expanded') === 'true') { set(false); btn.focus(); }
  });
  matchMedia('(min-width: 992px)').addEventListener('change', e => { if (e.matches) set(false); });
});

/* "/" moves focus to the search field from anywhere on the page, unless the visitor is already typing. */
document.addEventListener('keydown', e => {
  if (e.key !== '/' || e.ctrlKey || e.metaKey || e.altKey) return;
  if (e.target.closest && e.target.closest('input, textarea, select, [contenteditable]')) return;
  const field = document.getElementById('hs-q');
  if (!field) return;
  e.preventDefault();
  field.focus();
});

About this set

A documentation-style header built around a wide search field. The logo sits on the left, the search form takes the flexible middle of the bar up to 560px, and three links (Guides, API, Changelog) sit on the right, with the current page shown by an indigo tint. Use it for a docs site, a template library or a knowledge base, anywhere visitors are more likely to search than to browse. The search is a real form with role=”search”, a visually hidden label and a magnifier drawn as inline SVG. A short script moves focus to the field when someone presses the / key, unless they are already typing in a field, and the input advertises that shortcut with aria-keyshortcuts. The / hint inside the field is hidden from screen readers and from narrow screens. Below 992px the links fold behind a menu button while the search stays in the bar, and the whole header stays sticky at the top of the page.

What’s included

  • Search form with role="search", a hidden label and a / keyboard shortcut
  • Current page styled from aria-current="page"
  • Links fold behind a menu button below 992px while the search stays visible
  • Esc closes the open menu and returns focus to the button
  • Bootstrap version uses .navbar-expand-lg, .form-control and the Collapse plugin

Accessibility

The links sit in a nav labelled Main and the current one has aria-current="page". The search field has a visually hidden label, and aria-keyshortcuts announces the / shortcut. The menu button is a real button with an accessible name that reports aria-expanded; it opens with Enter or Space, and Esc closes the menu and returns focus to the button. Every link, button and field shows a visible focus style, and the transitions switch off under prefers-reduced-motion.

Customise it

In plain CSS change the hex values on .hs, .hs-search and .hs-menu (#4f46e5 for focus, #eef2ff and #4338ca for the current link) and the 72px min-height. In Bootstrap the link colours are the --bs-navbar-* variables on .hs; in Tailwind edit the hex values in the hover: and aria-[current=page]: utilities.