feat(search): replace Elasticsearch search with Pagefind#1018
Conversation
The docs UI shipped a custom Elasticsearch-browser search that only activated when ELASTICSEARCH_NODE was set, requiring an external Elasticsearch cluster to be provisioned, indexed, and reachable from the browser (host/index/read-auth exposed as data-attributes). Pagefind indexes the static site at build time and serves search entirely from the generated bundle, so search works with no backend service. - drop the elasticsearch-browser dependency and the bespoke src/js/vendor/elastic.js client - load Pagefind's Default UI (pagefind-ui.js / pagefind-ui.css) from siteRootPath, where docs-server emits the index, and mount it on a plain #search div; the ELASTICSEARCH_NODE env gate is gone, so search is always present - replace the hand-rolled .search .results CSS with Pagefind's --pagefind-ui-* variables mapped onto the ownCloud brand tokens (--oc-navy primary, brand text/border/tag colors, body font); keep the navbar width constraints and the z-index results drawer so the panel floats above page content Not verified with npm run lint / bundle: both fail in this environment on a pre-existing del@8 ESM-vs-gulp require() incompatibility on Node 18, unrelated to this change. Signed-off-by: Thomas Müller <1005065+DeepDiver1975@users.noreply.github.com>
The initial Pagefind integration mounted the Default UI inside the ~150px
navbar search slot, reusing the old Elasticsearch-era results-drawer CSS.
The Default UI is built for a full-width page region, so in the navbar the
results drawer overflowed and overlapped the breadcrumb row and the input
looked cramped. Pagefind recommends the Component UI for new integrations.
Switch to the Component UI modal: a navbar trigger button opens a centered
modal overlay (also via the Ctrl/Cmd+K shortcut), keeping the navbar clean
and giving search results room to render.
- header-content.hbs: replace the `#search` mount with `pagefind-config`
(points at the index under siteRootPath), `pagefind-modal-trigger` and
`pagefind-modal`
- head-styles.hbs / footer-scripts.hbs: load pagefind-component-ui.css and
pagefind-component-ui.js (ES module, self-registers the web components);
drop the pagefind-ui.js include and the `new PagefindUI()` init
- owncloud.css: remove the dead Default-UI / Elasticsearch drawer CSS and
theme the modal via its --pf-* custom properties on :root (the components
apply `all: initial`, so site CSS cannot cascade in)
The pagefind-component-ui.{js,css} assets are already emitted by
`pagefind --site`, so no docs-server / playbook change is needed.
Verified on the full production build (Node 22): npm run bundle passes, and
on the served site the navbar shows the trigger button and Ctrl+K opens the
themed modal overlay.
Signed-off-by: Thomas Müller <1005065+DeepDiver1975@users.noreply.github.com>
|
Updated the search UX based on testing the local build: the Pagefind Default UI rendered poorly inside the navbar (it's designed for a full-width region, so the results drawer overflowed and overlapped the breadcrumb row). Switched to the Pagefind Component UI modal — a navbar trigger button that opens a centered modal overlay (also via Commit
The |
phil-davis
left a comment
There was a problem hiding this comment.
Looks reasonable. The screenshot of the UI verifies that it renders OK.
Code reviewFront-end half of the Elasticsearch → Pagefind migration. Pairs with owncloud/docs#5106 — both must land together (this expects a Pagefind index that only #5106 produces). Strengths
Issues & suggestions🟡 Orphaned dependencies. "handlebars": "^4.7.9",
"jquery": "^4.0.0",They're now dead weight. The repo ships 🟢 🟢 Cross-version duplicate results (tracked separately). Because Pagefind indexes the whole tree, each page is indexed once per published version ( VerdictApprove, with one pre-merge ask: prune the orphaned |
jquery was only used by the Elasticsearch search client (src/js/vendor/elastic.js), which was removed in the switch to Pagefind. Nothing else in src/ or the build pipeline references it (confirmed with depcheck and a full-tree grep), so it is now dead weight. handlebars is intentionally kept: tasks/build-preview.js requires it to compile the .hbs layouts/partials for `npm run preview`. Verified: npm run bundle and gulp build:preview both pass with jquery removed. Signed-off-by: Thomas Müller <1005065+DeepDiver1975@users.noreply.github.com>
|
Addressed the orphaned-dependency finding in
Correcting my earlier review: I'd flagged Scope note: Verified: |
DeepDiver1975
left a comment
There was a problem hiding this comment.
🤖 Automated review by Claude Code review agent.
Overview
This PR swaps the bespoke Elasticsearch-browser search for Pagefind's Component UI. Net effect is a strong simplification: it deletes the 90-line jQuery elastic.js client, drops the jquery and elasticsearch-browser runtime dependencies, removes the ELASTICSEARCH_NODE env gate (search is now always rendered), and replaces ~80 lines of hand-rolled .search .results CSS with --pf-* brand-token mapping. The three <pagefind-config> / <pagefind-modal-trigger> / <pagefind-modal> tags plus the module script and stylesheet are loaded from {siteRootPath}/pagefind/, where docs-server is expected to emit the Pagefind index. The diff is internally consistent and the integration matches Pagefind's documented Component UI API. The build/bundle implications are clean.
Code quality / style
- Dependency removal is clean.
jqueryandelasticsearch-browserwere used only by the deletedsrc/js/vendor/elastic.js. The build globsjs/vendor/*.jsand browserifies each file (tasks/build.js), so removing the single file that didrequire('jquery')/require('elasticsearch-browser')removes the only consumers — no dangling bundle references.depcheckshould stay green. - Pagefind is loaded at runtime, not bundled.
pagefind-component-ui.jsis included via<script type="module" src="{{{siteRootPath}}}/pagefind/...">and the matching CSS via<link>, so it deliberately bypasses the gulp/browserify pipeline. This is the correct approach for Pagefind (the bundle is generated by the indexer over the built site, not shipped in the UI bundle). - Brand tokens all resolve. Every token consumed in the new
:rootblock —--body-font-family,--color-jet-50,--color-smoke-90,--color-smoke-50,--oc-link,--oc-teal— is defined invars.css/owncloud-vars.css, both of which are@import-ed beforeowncloud.cssinsite.css. Ordering is correct. siteRootPathis a legitimate Antora UI-model variable (already used for the logo link in this same partial), so the new references are sound.- Good inline documentation. The CSS and HBS comments explaining the
all: initialstyle isolation and thebundle-pathwiring are accurate and helpful for the next maintainer.
Specific suggestions
- The PR description is stale relative to the code. The body describes the Default UI (
pagefind-ui.js/pagefind-ui.css,--pagefind-ui-*variables, a#searchdiv). The actual diff ships the Component UI (pagefind-component-ui.js/.css,--pf-*variables, the<pagefind-*>web components). The code is the correct/consistent one — please update the PR description so reviewers and the docs-server follow-up aren't misled about which artifact to index/serve. - CSS variable coverage is partial. Only 8 of the many
--pf-*tokens are set. Notably unset:--pf-text-secondary/--pf-text-muted(result excerpts/metadata),--pf-modal-backdrop,--pf-modal-max-width, the focus-ring tokens (--pf-outline-focus), and--pf-dropdown-z-index. The old CSS deliberately usedz-index: 999999to float results above page content; if the Component UI's default modalz-indexis lower than ownCloud's sticky navbar/toolbar, the results overlay could render behind them. Worth a visual check and possibly setting the modal/dropdown z-index explicitly. --oc-navyis referenced in the PR text but not in the diff. The body says the navy token is reused as the primary color, but the new:rootblock maps--pf-border-focusto--oc-linkand--pf-markto--oc-teal— no--oc-navy. Minor, but another sign the description drifted from the implementation.- Loss of the min-length / debounce behavior is now Pagefind's concern. The old client only queried at ≥3 chars. Pagefind has its own debouncing, so this is fine, but worth confirming during the visual check that there's no noticeable request storm.
Potential issues / risks
- End-to-end search is broken until docs-server ships the indexer. This is correctly called out as a follow-up, but it bears emphasizing: merging this alone removes working (Elasticsearch-gated) search and points the UI at
{siteRootPath}/pagefind/, which won't exist until docs-server runspagefindover the built site and stops provisioning Elasticsearch. If the/pagefind/directory is absent, the module script and CSS will 404 and the search trigger will be non-functional. Coordinate the merge of both PRs, or the live docs site loses search in the interim. - CI may behave differently from the author's local run. The PR notes lint/bundle were not run because of a
del@8ESM-vs-gulp incompatibility "on Node 18." However.github/workflows/ci.ymlruns on Node 22, whererequire(esm)is supported — so CI may well passnpm run lint/npm run bundleeven though the local Node 18 build failed. Conversely, if it does fail in CI, the build/release job gates on it. Either way, rely on the CI result rather than the local-Node-18 caveat; please confirm the CI build is green before merge. - No automated coverage for the search UI (none existed before either), so the brand-color + drawer rendering really does need the manual visual check the author flagged.
- Always-on search markup. Removing the
{{#if env.ELASTICSEARCH_NODE}}gate means the<pagefind-*>elements always render. If a downstream consumer builds the UI without running the indexer, they get visible-but-dead search rather than no search. Acceptable trade-off given Pagefind's static model, just noting the behavior change.
Overall this is a clean, well-scoped change that meaningfully reduces complexity and removes an external-service dependency. Main asks before merge: refresh the PR description to match the Component UI implementation, confirm CI is green, do the visual/z-index check on the modal, and coordinate the docs-server indexer rollout.

What
Replaces the docs UI's custom Elasticsearch-browser search with Pagefind.
The old search only activated when
ELASTICSEARCH_NODEwas set and required an external Elasticsearch cluster — provisioned, indexed, and reachable from the browser (host/index/read-auth exposed asdata-*attributes). Pagefind indexes the static site at build time and serves search entirely from the generated bundle, so search works with no backend service.Changes
elasticsearch-browserdependency and the bespokesrc/js/vendor/elastic.jsclientpagefind-ui.js/pagefind-ui.css) fromsiteRootPath(where docs-server emits the index) and mount it on a plain#searchdiv. TheELASTICSEARCH_NODEenv gate is gone, so search is always present.search .resultsCSS with Pagefind's--pagefind-ui-*variables mapped onto the ownCloud brand tokens (--oc-navyprimary, brand text/border/tag colors, body font); keep the navbar width constraints and the high-z-indexresults drawer so the panel floats above page contentBuilds on the corporate-identity theme (#1014), whose
--oc-navytoken the search styling reuses.Follow-up needed on docs-server
This PR wires the UI to consume a Pagefind index at
{siteRootPath}/pagefind/. docs-server must run the Pagefind indexer over the built site (and stop provisioning Elasticsearch) for search to function end to end.Verification
npm run lint/npm run bundlewere not run — both currently fail on a pre-existingdel@8ESM-vs-gulprequire()incompatibility on Node 18, unrelated to this change. Needs a bundle build + visual check of the search drawer (rendering + brand colors) before merge.