Skip to content

Header search bar with Cmd+K dialog - #671

Merged
Flotapponnier merged 2 commits into
devfrom
feat/header-search-bar
Jun 24, 2026
Merged

Header search bar with Cmd+K dialog#671
Flotapponnier merged 2 commits into
devfrom
feat/header-search-bar

Conversation

@Flotapponnier

Copy link
Copy Markdown
Collaborator

Summary

Adds a sitewide search bar to the header that opens a Cmd+K (or Ctrl+K, or /) dialog and fuzzy-matches across every editorial surface on the site: benchmarks, products, alternatives, answers, chains, compare pairs, and top-level pages.

Stack

  • cmdk for the command menu primitive (keyboard nav, group headings, a11y).
  • fuse.js for client-side fuzzy matching against a pre-built index.
  • Build-time index built server-side in src/lib/search/buildIndex.ts, shipped once as JSON via the root layout. No /api/search route, no backend lookups.

Index

Per-kind counts at build time:

Kind Count Source
Benchmark 31 benchmarks/*.yml (drafts skipped)
Product 195 src/data/provider-registry.ts
Compare 22 src/data/compare-pairs.ts
Alternative 25 alternatives/*.yml
Answer 18 answers/*.yml
Chain 21 src/lib/chains.ts CHAINS
Page 16 hardcoded list of top-level routes
Total 328

JSON payload: ~90 KB raw / ~20 KB gzip (shipped once with the RSC payload of the root layout).

Bundle impact

Dialog + cmdk + Fuse are lazy-loaded via next/dynamic on the first open of the dialog. The header trigger only adds an icon button and the provider plumbing to the shared chunk.

Lazy chunk that holds cmdk + Fuse + the search dialog: 76 KB raw / ~25 KB gzip. Zero impact on first paint for pages that never open the dialog.

UX

  • Cmd+K / Ctrl+K toggles the dialog from anywhere; / opens it (skipped when an input/textarea is already focused, GitHub-style).
  • Header trigger: inline button between Contribute and the | separator on desktop. On mobile, a 44 by 44 icon-only button sits to the left of the hamburger.
  • Result row: kind badge + bold title + one-line truncated description. Grouped by kind in cmdk.
  • Empty state shows a hint with the queried string.
  • No-query state shows 4 hardcoded popular benches (pm-data-freshness, aggregator-head-lag, l1-finality, rpc-capabilities).
  • All colors via existing CSS vars (bg-paper, text-ink, border-rule, etc.). No dark: variants needed.
  • role="dialog", aria-modal="true", aria-label="Search" on the panel. Focus lands on the input. ESC, click outside, and a close button all dismiss.

Files

New:

  • src/lib/search/types.ts
  • src/lib/search/buildIndex.ts
  • src/components/search/search-provider.tsx (client, Cmd+K listener + dynamic dialog)
  • src/components/search/search-dialog.tsx (client, cmdk + Fuse)
  • src/components/search/search-trigger.tsx (desktop/mobile variants)

Modified:

  • src/app/layout.tsx (mounts <SearchProvider items={await buildSearchIndex()}>)
  • src/components/site-header.tsx (drops the trigger in desktop nav + next to the hamburger)
  • package.json / pnpm-lock.yaml (adds cmdk, fuse.js)

v2 ideas (out of scope)

  • Per-chain bench subroutes (/benchmarks/<slug>/<chain>) and category routes
  • Recent searches in localStorage
  • Popular-search tracking (today the popular list is editorial, no analytics)
  • Highlighting matched substrings in the result rows

Validation

  • pnpm typecheck clean
  • pnpm validate 31 specs OK
  • pnpm test src/ 113 passes
  • pnpm lint no new issues
  • pnpm build succeeds; dialog code lands in its own lazy chunk

Test plan

  • Open the staging preview, press Cmd+K, type "finality", hit Enter, land on /benchmarks/l1-finality
  • Press / anywhere except inside a text input and confirm the dialog opens
  • Type "mobula" and see Product + Compare rows
  • Type "ethereum vs solana" and see the compare pair surface as the top result
  • On mobile viewport, confirm the search icon sits to the left of the hamburger and the dialog fills the screen with a tap target large enough to dismiss
  • Toggle light/dark theme and confirm the dialog inherits both via CSS vars

@Flotapponnier
Flotapponnier merged commit 334dd79 into dev Jun 24, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant