Skip to content

Add Pagefind search to the site - #3989

Merged
hsbt merged 11 commits into
masterfrom
claude/pagefind-inline-search-i18n-49fa5f
Jul 28, 2026
Merged

Add Pagefind search to the site#3989
hsbt merged 11 commits into
masterfrom
claude/pagefind-inline-search-i18n-49fa5f

Conversation

@hsbt

@hsbt hsbt commented Jul 28, 2026

Copy link
Copy Markdown
Member

The site has no search, so finding a particular release note or guide means guessing which page holds it or leaving for a search engine.

This adds Pagefind, which indexes the built HTML rather than the Markdown behind it, so search runs in the browser and the site stays a set of static files. A Jekyll post_write hook runs it, so rake build, rake serve and the deploy workflow each end with an index matching the site they just wrote. Without Node the hook says it skipped and lets the build through.

Pagefind splits the index by the lang attribute on <html> and loads the one matching the page, so a translation finds only its own pages and reads its own interface labels. Bulgarian is the exception, since Pagefind has neither stemming nor translations for it. Its pages stay out of the index and its search runs against English. _config.yml records that, and another language needs nothing more than a line here.

search:
  fallback: en
  unsupported:
    - bg

Leaving that to Pagefind was not an option. It resolves a language with no index of its own to whichever index holds the most pages, and Ukrainian is one page behind English today.

One change beyond search. rake serve called Serve.process without the Build.process that jekyll serve pairs it with, so it only handed out whatever _site already held and never picked up an edit. It now runs both.

hsbt and others added 5 commits July 27, 2026 21:26
Pagefind indexes the built HTML as a post-processing step, so the site keeps
working as a set of static files with no search backend.

It builds one index per language and the browser picks one by the page's `lang`
attribute. A language with no index of its own falls back to whichever index
holds the most pages, which is not a choice we control: Ukrainian currently has
more pages than English. `SearchIndex` rewrites the bundle entry so those
languages point at a fixed index instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pagefind indexes a whole page unless it is told otherwise, which would put the
navigation, the table of contents and the footer into every excerpt. Marking the
article element limits it to the content, and the recent-news and pagination
blocks inside that element are marked as ignored for the same reason.

Bulgarian is left out: Pagefind has neither stemming nor interface translations
for it, so its pages get no `data-pagefind-body` and search there runs against
English. Which languages those are now lives in _config.yml, so the layouts and
`SearchIndex` cannot disagree about it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The button sits next to the language selector and opens a modal; Cmd+K or
Ctrl+K opens it from anywhere. Below 640px the top bar has no room for it
alongside the longer language names, so it moves into the mobile menu and the
two buttons share one modal.

Pagefind reads `<html lang>` for its own interface as well as for the index, so
every translation gets its labels and result counts in its own language with no
strings to add here. Bulgarian is pointed at English on both counts.

Pagefind's dark palette hangs off `data-pf-theme`, which the theme toggle now
sets from the same decision that sets `.dark`, and the brand sits on top of it
through the `--pf-*` variables.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Covers the one thing that surprises people locally: the index is a
post-processing step over `_site`, so `rake serve` has no search and there is a
separate task for previewing it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Indexing hung off `rake build`, so `rake serve` served a site with no search at
all and needed a second task to preview one. Moving it to a `post_write` hook
means any path that writes the site ends with a matching index: `rake build`,
`rake serve` and its regenerations, and the bare `jekyll build` the deploy
workflow runs.

`rake serve` only ever handed out whatever `_site` already held, since it called
`Serve.process` without the `Build.process` the `jekyll serve` command pairs it
with. It now runs both, the way the README has always described it.

Pagefind indexes silently because this fires on every regeneration, and the hook
reports one line instead. Without Node it says so and lets the build through, so
editing content still only needs Ruby.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@hsbt
hsbt requested a review from a team as a code owner July 28, 2026 00:48
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying www-ruby-lang-org with  Cloudflare Pages  Cloudflare Pages

Latest commit: cecd3cd
Status:🚫  Build failed.

View logs

hsbt and others added 6 commits July 28, 2026 11:12
The include still told readers to run a `rake search-index` that no longer
exists and that `rake serve` has no index, which is now the opposite of what
happens. README implied a bare `rake build` survives a missing Node, when it
stops at Tailwind well before the index step. The `rake test` description had
listed a subset of its suites since before this branch and had fallen a further
one behind.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_config.yml and the site directories spell locales `zh_cn`, Pagefind keys its
indexes `zh-cn`. The fallback was written under the config spelling, so
`unsupported: [zh_cn]` would have left the browser looking up a key nobody
wrote and falling back to the largest index, which is the failure this exists to
prevent. Only `bg` and `en` are configured today, so nothing was broken yet,
but README tells maintainers that adding a line here is all it takes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The admin page carries `layout: page` like any other, so search gave it a
public entry point it never had before. A `searchable: false` in its front
matter covers this case and any later page whose contents are not worth
answering a search with.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Search results link with `?highlight=`, so a build whose index was skipped threw
an unhandled rejection on each page a reader arrived at. Losing the highlight is
the whole cost of that build already.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Only the skip path was tested, so nothing held `build` to returning the entry on
success or raising when Pagefind exits non-zero. The `pagefind:` keyword was
added for exactly this and a shell stub is enough to drive both.

The file also reaches its subject through the load path now, the way the other
tests for `lib` do.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A Bulgarian visitor typing Cyrillic got nothing back and no reason for it, which
reads as a broken search rather than a language Pagefind cannot index. The modal
now carries a line explaining that the search covers the English pages and to
type in English.

Only the languages under `search.unsupported` get the modal's parts written out;
the rest keep the contents Pagefind builds for itself. The notice lives in
_data/locales and falls back to English, so a second language needs only its own
string.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@hsbt
hsbt merged commit b5cff3d into master Jul 28, 2026
2 checks passed
@hsbt
hsbt deleted the claude/pagefind-inline-search-i18n-49fa5f branch July 28, 2026 02:59
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