# Dedicated Docusaurus Homepage + Native Local Search Use this pattern when a docs-only Docusaurus site needs a product/showcase homepage and search that runs entirely from static build artifacts. ## Route separation A docs plugin with `routeBasePath: '/'` can coexist with `src/pages/index.tsx`, but no doc may also claim `/`. ```md --- id: intro title: Documentation overview slug: /overview --- ``` After moving the intro, re-check Markdown links. A link such as `./methodology` from `/overview/` resolves under `/overview/`; use `../methodology`, a Docusaurus doc link, or an explicit base-path URL. Keep footer/navbar links explicit: ```ts {label: 'Home', to: '/'}, {label: 'Overview', to: '/overview'}, ``` ## Local search without hosted services Install at the root of a root-only pnpm workspace: ```bash pnpm add -w @easyops-cn/docusaurus-search-local ``` Configure it as a **theme**, not a plugin: ```ts themes: [ [ require.resolve('@easyops-cn/docusaurus-search-local'), { hashed: true, indexDocs: true, indexBlog: false, indexPages: true, docsRouteBasePath: ['/', 'clients'], language: ['en'], highlightSearchTermsOnTargetPage: true, explicitSearchResultPath: true, searchResultLimits: 12, searchResultContextMaxLength: 120, }, ], ], ``` `docsRouteBasePath` must cover every docs plugin whose content should be searchable. Do not add Algolia credentials, crawler configuration, Ask AI, or another network service when the requirement is browser-local search. ### One SearchBar only The theme supplies the navbar `@theme/SearchBar`. Avoid importing and rendering another `SearchBar` on the homepage: duplicate instances can contend for the singleton autocomplete/index lifecycle and leave the UI with input text but no fetched index or result overlay. Use a landing-page trigger instead: ```tsx const openSearch = () => { const input = document.querySelector('.navbar__search-input'); if (input) { input.focus(); return; } document.dispatchEvent( new KeyboardEvent('keydown', {key: 'k', ctrlKey: true, bubbles: true}), ); }; ``` Keep the navbar field itself as the sole search implementation. ## Generated homepage metrics Do not hard-code changing registry statistics into JSX. Extend the canonical content generator to emit an ignored TypeScript module: ```python summary = ROOT / "src" / "generated" / "registrySummary.ts" summary.parent.mkdir(parents=True, exist_ok=True) summary.write_text( "export const registrySummary = " + json.dumps({"total": total, "researched": researched}, separators=(",", ":")) + " as const;\n", encoding="utf-8", ) ``` Add `/src/generated` to `.gitignore`, run generation before both `tsc` and `docusaurus build`, and import from `@site/src/generated/registrySummary`. ## Government-portal visual adaptation When adapting an official public-sector site, copy the visual grammar rather than cloning its page: - derive a small token set from the real site (navy, action blue, pale neutral background, white surfaces, restrained borders); - use a locally installed font package to avoid runtime font dependencies; - reuse only appropriate official marks with clear provenance; - reproduce hierarchy: narrow institutional strip, white utility navigation, large editorial hero, evidence/status blocks, and dark institutional footer; - preserve the user's established geometry preference (for Lego, globally force `border-radius: 0` even if the source site uses pills); - provide dark-mode equivalents without replacing the light-first official character. For the VSSA pattern, the useful observed tokens were approximately navy `#091a5a`, action blue `#0b66e4`, pale background `#f3f6f9`, and Public Sans. The official coat-of-arms SVG was suitable as a local navbar asset. ## Public page unchanged: distinguish an untriggered workflow from a failed workflow When a user cannot see locally completed changes, do not begin with runner debugging. Establish the publication chain in order: 1. `git status --short --branch` — are the changes still uncommitted? 2. `git rev-list --left-right --count HEAD...origin/main` — were they pushed? 3. Read the newest Actions run and compare its `head_sha` to the intended commit. 4. If the newest run still points to the previous SHA, Actions did not fail; the new workflow was never triggered. Commit and push. 5. Wait for the run attached to the intended SHA to reach `completed success`. 6. Fetch the public page with `Cache-Control: no-cache` and a query marker such as `?verify=`; assert a distinctive new heading or asset, not only HTTP 200. 7. Verify the public `search-index.json` contains representative searchable terms, then run a real browser query and open a result. This sequence prevents two common false diagnoses: treating a successful local build as a deployment, and treating an old successful Actions run as evidence that a new change was published. ## Verification gate Run all of these before declaring completion: ```bash pnpm validate pnpm build test -s build/search-index.json ``` Then programmatically inspect the index for representative terms from: 1. the main docs plugin; 2. every secondary docs plugin; 3. the custom homepage when `indexPages: true`. Finally, serve the production build and verify in a browser: - desktop and mobile homepage layout; - navbar search and keyboard shortcut; - a query produces visible results; - opening a result reaches the expected dossier route; - square geometry and responsive navigation remain intact. Publication is complete only after commit/push, a successful Gitea Actions run, and HTTP/content verification of the public homepage, overview, representative result route, and search index.