Contact Us

Search

DOC-00016 reference implementor, developer

Search delivers Pagefind-powered static search across all content pages. Three search surfaces are provided: a global search modal, a dedicated search page, and Pagefind integration in the docs index.

Who this is for

  • Implementors configuring search behavior or adjusting which content is indexed
  • Developers extending search surfaces or modifying the Pagefind integration

Architecture

Pagefind runs as a post-build step (astro build && npx pagefind --site dist), indexing all elements marked with data-pagefind-body in PageGridLayout.astro. The JS search runtime (~30KB) is lazy-loaded on first use via search-client.ts.

Key files

FileOwnershipPurpose
src/lib/search-client.tsCOREPagefind singleton init (lazy-load + cache) and typed query wrapper.
src/lib/search-events.tsCORE5 analytics event stubs (trackSearchOpened, trackSearchSubmitted, trackSearchResultsViewed, trackSearchResultClicked, trackSearchNoResults).
src/core/components/search/SearchModal.astroCOREGlobal modal dialog using native <dialog>, focus trap, scroll lock, Cmd/Ctrl+K shortcut.
src/core/components/search/SearchResults.astroCOREShared result list with vanilla JS rendering and variant prop ("card" default, "compact" for modal). Parent scope provides results, selectedIndex, onResultClick.
src/core/components/search/SearchPage.astroCOREDedicated search page logic with URL state (?q= via replaceState).
src/core/components/search/SearchTrigger.astroCOREDual-mode trigger: modal mode dispatches search-open event; page mode links to /search/.
src/pages/search.astroSITEThin route wrapper with PageGridLayout, noindex.

Search Modal

Rendered in PageGridLayout when siteConfig.header.search.enabled && search.behavior === "modal". Communicates with SearchTrigger via a search-open custom DOM event on document.

The modal backdrop uses the shared --st-color-overlay-scrim semantic token at 50% alpha so overlay treatment stays consistent with other full-screen surfaces.

UX states

  1. initial — Modal open, no query entered.
  2. loading — First search in progress (Pagefind loading).
  3. results — Results displayed in a scrollable list.
  4. no-results — Query returned zero results.
  5. error — Pagefind failed to load or query threw.

Keyboard

  • Cmd/Ctrl+K opens the modal (suppressed in inputs/textareas/contenteditable).
  • Tab moves between input, result links, and close button.
  • Escape closes the modal and returns focus to the trigger element.

Search Page

Available at /search/ when search.enabled is true. Supports ?q= URL parameter for direct linking. Same Pagefind integration as the modal but rendered inline.

Docs Index Integration

The docs index (/theme-docs/) uses a slug-match hybrid approach (D-C7-10): Pagefind returns text search results, which are matched by URL to the server-rendered items array. Faceted filters (topic, type, audience) remain as client-side post-filters. Empty queries show all items without invoking Pagefind.

Search Events

Five analytics event stubs are defined in search-events.ts. They emit console.debug in development and are no-ops in production. C11 (Analytics & Consent) will replace the dispatch mechanism without changing call sites.

HelperWhen fired
trackSearchOpened(source)Modal opens
trackSearchSubmitted(queryLength)Pagefind query completes
trackSearchResultsViewed(count)Results rendered
trackSearchResultClicked(index)User clicks a result
trackSearchNoResults(queryLength)Zero results returned

Configuration

Search behavior is controlled via siteConfig.header.search:

search: {
  enabled: true,        // Show search trigger in header
  behavior: "modal",    // "modal" | "page"
  href: "/search/",     // Page mode link target
}

Pagefind Indexing

data-pagefind-body is placed unconditionally on the main-header div and #main-content in PageGridLayout.astro. This indexes page titles, subheadings, and body content while excluding nav, footer, and chrome.

Collection filters

Content is scoped to collections via data-pagefind-filter="collection:{value}" on <main>. PageGridLayout exposes a pagefindFilter prop that renders this attribute declaratively. Routes set pagefindFilter="page", "article", or "doc" to place content in a collection. Pages without a pagefindFilter value are indexed by Pagefind but excluded from filtered queries (e.g., the search page itself, 404).

Multi-value filter workaround

Pagefind treats multi-value filter arrays as AND (all values must match). search-client.ts works around this by running separate queries per filter value and merging results, deduplicating by result ID. This enables queries like { collection: ["page", "article"] } to return results from either collection.

Manual accessibility test entries verified during the accessibility audit. Covers keyboard operability, screen-reader announcements, and ARIA semantics.

SearchModal

Interaction Expected Behavior WCAG Criterion Test Method
Activate the search trigger to open the SearchModalModal opens, focus moves to the search input, focus is trapped inside the modal2.1.1 Keyboard Keyboard
Type a search query and review resultsResults update; Arrow Down moves focus into results if keyboard-navigable2.1.1 Keyboard Keyboard
Press Escape while the SearchModal is openModal closes, focus returns to the search trigger2.1.1 Keyboard Keyboard
Inspect the modal container in DevToolsModal has role="dialog" or is a dialog element with aria-modal="true" and an accessible name; search input has an accessible label4.1.2 Name, Role, Value Visual Inspection
Open and close the modal with prefers-reduced-motion: reduce enabledOpen/close transitions are suppressed or instantaneous2.3.3 Animation from Interactions Visual Inspection
Type a query with NVDA runningNVDA announces the modal as a dialog and announces result count via live region4.1.3 Status Messages Screen Reader

SearchTrigger

Interaction Expected Behavior WCAG Criterion Test Method
Tab to the SearchTrigger buttonButton is focusable with visible focus ring2.4.7 Focus Visible Keyboard
Press Enter on the SearchTriggerSearch modal opens, focus moves to the modal search input2.1.1 Keyboard Keyboard
Inspect the SearchTrigger button in DevToolsButton has an accessible name; aria-haspopup="dialog" is present; icon-only buttons have aria-label4.1.2 Name, Role, Value Visual Inspection
Focus the SearchTrigger with NVDA runningNVDA announces the button with a descriptive label (e.g., "Search" or "Open search")4.1.2 Name, Role, Value Screen Reader

SearchPage

Interaction Expected Behavior WCAG Criterion Test Method
Tab to the search input, type a queryInput is focusable with visible focus ring; results display after typing2.1.1 Keyboard Keyboard
Tab through the search resultsEach result link is focusable2.1.1 Keyboard Keyboard
Inspect the search input and results in DevToolsSearch input has an accessible label; results are in a semantic list structure; a live region exists for dynamic result count updates1.3.1 Info and Relationships Visual Inspection
Tab through results with NVDA runningNVDA announces each result's title and the input label4.1.2 Name, Role, Value Screen Reader
REQ-00018 implemented Search, filtering, sorting, and pagination behaviors shall be supported.
REQ-00199 implemented The search entry point (REQ-00163) shall be upgraded to a functional search modal dialog that allows users to search site content without navigating away from the current page.
REQ-00205 implemented The theme shall provide a sitewide search system that indexes published content and returns relevant results to power the search modal experience (REQ-00199).
REQ-00231 implemented The search modal shall be activatable via Cmd/Ctrl+K keyboard shortcut, with the shortcut suppressed when focus is in form inputs, textareas, or contenteditable elements.
REQ-00232 implemented The Pagefind search library shall be lazy-loaded on first search interaction (modal open), not at page load, to avoid impacting initial page weight and loading performance.
REQ-00233 implemented A standalone search page (/search/) shall be provided as a full-page search interface with URL state via query parameter (?q=), scoped to pages and articles collections, and marked noindex to prevent search engine indexing of search result pages.
REQ-00234 implemented Search interactions shall emit a defined event taxonomy (search_opened, search_submitted, search_results_viewed, search_result_clicked, search_no_results) routed through the analytics trackEvent system, with privacy-safe payloads (no raw query text; use queryLength, resultCount, resultIndex).

Search

Search across pages and articles. Use arrow keys to navigate results.

Search across pages and articles.

Loading search...

Search is unavailable. Please try again later.

    No results for ""

    Try different keywords or fewer words.