Search — Svelte

Search component — Svelte.

Live example

Search is in the navbar (top of page) — click the search icon or press Ctrl+K / Cmd+K. You can also use the standalone search below.

Standalone search (same as navbar)

Search component

An accessible search overlay with Algolia integration (or client-side fallback). The docs site uses the Astro Search in the layout. In a Svelte app, use the same BEM classes and data attributes; wire open/close state, Cmd+K / Ctrl+K, and your search API or Algolia client.

Live example

Search is in the navbar (top of page) — click the search icon or press Ctrl+K / Cmd+K. You can also use the standalone search above (same as navbar).

Features

  • Full-screen overlay — Centered search panel; overlay covers the viewport when open.
  • Keyboard shortcut — Ctrl+K / Cmd+K to open or close.
  • Escape — Closes search; restore focus to trigger.
  • Close button (X) with screen reader label; optional text label on mobile.
  • Live results as you type; Algolia integration or client-side search.
  • Full keyboard navigation (Arrow keys, Enter, Tab) in results.
  • Mobile responsive; full-width panel on small screens. Mutually exclusive with mobile menu on the docs site.
  • ARIA (dialog, listbox, searchbox), focus trap, and live regions for screen readers.

Key BEM classes and data attributes

Use these so styles and any shared scripts (e.g. Cmd+K) work. Toggle aria-hidden on the overlay and panel from your open state.

  • search — root wrapper; use data-search if you reuse the Astro script pattern.
  • search__trigger-wrapper / search__trigger — button to open search; aria-expanded, aria-controls = panel id.
  • search__overlay — full-screen overlay; data-search-overlay; set aria-hidden from state.
  • search__panel — dialog panel; role="dialog", aria-modal="true", aria-labelledby (title id), tabindex="-1" for focus trap.
  • search__header — contains input row and close button.
  • search__input-wrapper / search__input — search input; aria-controls = results list id; data-search-input for script hooks.
  • search__clear — clear input button; data-search-clear.
  • search__close-btn — close overlay button.
  • search__results — container for results; role="listbox", aria-label="Search results", aria-live="polite".
  • search__empty, search__loading, search__no-results — states; use hidden or conditional render.
  • search__result-item / search__result-title / search__result-category / search__result-content — result row and parts.

Structure example (simplified)

Minimal markup; add IDs, ARIA, and data attributes as needed. Control visibility with aria-hidden and optional classes.

svelte svelte
<div class="search" data-search>
  <div class="search__trigger-wrapper">
    <button
      type="button"
      class="search__trigger"
      aria-label="Open search"
      aria-expanded={open}
      aria-controls="search-results-panel"
      onclick=() => (open = !open)
    >
      <SearchIcon class="search__icon" width={20} height={20} />
      <span class="search__trigger-text">Search</span>
      <kbd class="search__kbd" aria-hidden="true"><span class="search__kbd-modifier"><!-- Cmd icon --></span><kbd>K</kbd></kbd>
    </button>
  </div>
  <div
    class="search__overlay"
    data-search-overlay
    aria-hidden={!open}
    role="dialog"
    aria-modal="true"
  >
    <div
      class="search__panel"
      id="search-results-panel"
      tabindex="-1"
      aria-hidden={!open}
    >
      <div class="search__header">
        <input
          type="search"
          class="search__input"
          placeholder="Search documentation..."
          aria-label="Search"
          data-search-input
          bind:value={query}
        />
        <button type="button" class="search__close-btn" aria-label="Close" onclick=() => (open = false)>
          Close
        </button>
      </div>
      <div class="search__results" role="listbox" aria-label="Search results">
        <!-- search__empty / search__results-list / search__loading / search__no-results -->
      </div>
    </div>
  </div>
</div>

Implementing in Svelte

  • Open state: e.g. let open = $state(false). Set aria-expanded on the trigger and aria-hidden={!open} on overlay and panel.
  • Cmd+K / Ctrl+K: Add a keydown listener on window or document; if key === 'k' and (metaKey or ctrlKey), prevent default and toggle open.
  • Escape: When overlay is open, handle Escape to set open = false and call triggerEl.focus().
  • Focus trap: When open, focus the input; trap Tab inside the panel; on close, restore focus to the trigger.
  • Search: Connect query to Algolia (e.g. searchClient + index) or to client-side filtering; render results with search__result-item and siblings. Use aria-activedescendant for keyboard-highlighted result.

Keyboard shortcuts

  • Ctrl+K / Cmd+K — Open or close search
  • Escape — Close search
  • Arrow Down — Next result
  • Arrow Up — Previous result
  • Enter — Open selected result

Full Astro Search documentation — Algolia setup, props, and the inline script you can port to Svelte.

← Back to Svelte components