Skip to content

Ship a search page with build-pandoc, using Pagefind's Component UI #1

Description

@t4k

Scope: the Pandoc builder only. Zensical and Sphinx ship their own search. This is about build-pandoc, and about the hand-carried search.md that Pandoc-based sites using this repository each copy.

index-site builds a Pagefind index, but every site still carries its own search.md. Those pages are copies of each other, which turns a one-time improvement into a repeated chore.

They are already copies

Sampling repositories using PagefindUI:

Repository
CL-web-components, CL-Pandoc-filters, etd-workflow byte-identical bodies
opensearch-machine stripped down — just element: "#search"
feeds.library.caltech.edu genuinely customized (showSubResults, highlightParam, own query handling)

The accessibility part

Pagefind 1.5.0 introduced a Component UI built on web components, with "greatly improved accessibility over the Default UI", keyboard navigation, and better RTL and locale handling.

This is not a version upgrade. index-site already pins 1.5.2, the current release, and the 1.5.0 notes are explicit:

The existing Default UI and Modular UI remain available and supported for now, so you can upgrade your sites to Pagefind v1.5.0 without migrating to the Component UI.

So nothing is broken and nothing is urgent. The improvement is opting into <pagefind-*> elements on the search page — the file every site copies.

Open questions, now answered

Does the Component UI support deep-linking? Yes. triggerSearch exists on the instance and is reachable without npm:

window.PagefindComponents.getInstanceManager()
  .getInstance('default').triggerSearch(q);

Is the npm package needed? No. Pagefind emits pagefind-component-ui.css and pagefind-component-ui.js into /pagefind/; both are already in our built output. @pagefind/component-ui is only for bundler imports. index-site stays dependency-free.

Verified: the shared page can compute its own paths

A shared page cannot hardcode a deployment path, so it has to derive one. Both base-url and bundle-path must be set — setting only base-url fails outright with Could not load search bundle. Bundle path: /pagefind/, because auto-detection falls back to the site root rather than the prefix.

<link href="./pagefind/pagefind-component-ui.css" rel="stylesheet">

<pagefind-config id="pf-config"></pagefind-config>
<script>
  // Classic inline script: runs during parsing. Module scripts are deferred,
  // so this always executes before the custom elements are defined. Attributes
  // are read once at connect, so setting them here is reliable, not a race.
  var base = location.pathname.replace(/[^/]*$/, '');
  var cfg = document.getElementById('pf-config');
  cfg.setAttribute('base-url', base);
  cfg.setAttribute('bundle-path', base + 'pagefind/');
</script>

<pagefind-input autofocus></pagefind-input>
<pagefind-results></pagefind-results>

<script src="./pagefind/pagefind-component-ui.js" type="module"></script>
<script type="module">
  const q = new URLSearchParams(location.search).get('q');
  if (q) {
    window.PagefindComponents.getInstanceManager()
      .getInstance('default').triggerSearch(q);
  }
</script>

Tested against a real CL-web-components build served under a /CL-web-components/ prefix: results rendered with sub-results, ?q= populated the input, and links resolved to /CL-web-components/table-sortable.html — correct prefix, anchors included.

Where it goes

In build-pandoc, not index-site.

  • index-site must stay engine-agnostic — it indexes built HTML for any generator, and emitting a page would make it opinionated about theme and markup.
  • The search page has to pass through the Pandoc template so it matches the rest of the site. build-pandoc rendering pandoc/search.md gets that for free, with the project's own template override if it has one. A pre-baked search.html could not.

Proposed: build-pandoc gains a search-page input (default off) that renders the shared pandoc/search.md alongside the project's docs. Used with index-site, a site gets working accessible search with nothing in its own repository.

Option mapping, for anyone migrating a search page

Default UI Component UI
baseUrl <pagefind-config base-url>
highlightParam <pagefind-config highlight-param>
showSubResults: true now the default; opt out with hide-sub-results, tune max-sub-results
showImages: true show-images on <pagefind-results>
pageSize max-resultsnot equivalent, results lazy-load on scroll instead of paginating
processResult <script type="text/pagefind-template"> inside <pagefind-results>
resetStyles: false no equivalent; styling is CSS variables and parts
triggerSearch(q) instance.triggerSearch(q)

The Default UI also requires bolting on an aria-label for the search input by hand, since it does not provide one. The Component UI does, which is a concrete instance of the accessibility difference.

Suggested order

  1. CL-web-components — near-zero customization, proves the pattern, and it is the copy three other repos share
  2. Move the resulting page into this repository behind build-pandoc's search-page input
  3. Adopt per site from there; sites with their own search page keep it

Context

Raised while restoring search in caltechlibrary/CL-web-components#46, where search.md and the committed index had been deleted but the generated search.html remained — so the live site served a search page whose assets 404'd.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions