Options: Doc Search Ranking
Options for demoting roadmap/ pages in the documentation site’s search results, with the measurements that rule several of them in or out. Read Goal first for why this is wanted.
All measurements below were taken with the versions this repository’s package-lock.json pins — Starlight 0.38.5 and Pagefind 1.5.2 — using the probe harness in scratch/pagefind-probe/.
Where the Lever Is
Section titled “Where the Lever Is”Starlight marks each page’s indexed region by putting data-pagefind-body on the <main> element (node_modules/@astrojs/starlight/components/Page.astro, line 47). Pagefind’s data-pagefind-weight attribute multiplies the ranking contribution of the text inside an element, over the range 0.0–10.0. Combining the two is the only per-page ranking control available, because Starlight’s pagefind configuration option is site-global: it exposes pageLength, termFrequency, termSaturation, and termSimilarity, plus indexWeight and mergeFilter which apply only to merged multisite indexes.
Starlight also honors pagefind: false in a page’s frontmatter, which drops the page from the index entirely.
Mechanism Check
Section titled “Mechanism Check”Pagefind’s documentation does not state whether data-pagefind-weight cascades to nested elements, nor whether it applies on the data-pagefind-body element itself. Both were measured on five synthetic fixtures carrying identical text, varying only where the attribute sits, searched for "widget calibration".
| Fixture | Markup | Score | Relative |
|---|---|---|---|
| a | No weight (baseline) | 1.522 | 1.00 |
| d | h1 outside a weighted <div weight="0.2"> | 1.493 | 0.98 |
| b | weight="0.2" on the data-pagefind-body element | 1.107 | 0.73 |
| c | weight="0.2" on an inner div wrapping h1 and body | 1.107 | 0.73 |
| e | weight="0.05" on the data-pagefind-body element | 0.949 | 0.62 |
Two mechanism conclusions follow, and both hold regardless of corpus:
- The attribute works on the
data-pagefind-bodyelement itself and cascades into nested elements, including headings. Fixtures b and c score identically, so attributing the body element is equivalent to wrapping the content. - The page’s
h1must be inside the weighted region. Fixture d moved the score by 2%. This rules out the otherwise-attractive route of wrapping content in the existingMarkdownContent.astrooverride, because Starlight rendersPageTitleas a sibling ofMarkdownContent, not a parent. The attribute has to land on<main>.
A weight sweep on the same fixtures (0, 0.05, 0.2, 0.5, 1, 2, 5, 10) is monotonic but compressed — even weight="0" scores 0.57 of baseline, and weight="10" only reaches 1.05. These fixtures are near-pure heading matches, which is the worst case for the attribute; the real corpus behaves very differently, which is why the numbers below are the ones that matter.
Real-Corpus Measurement
Section titled “Real-Corpus Measurement”The production build’s HTML (1169 pages) was copied twice. In the treatment copy, data-pagefind-weight="0.2" was injected into <main data-pagefind-body> on all 689 roadmap/ pages and nothing else. Both copies were indexed with Pagefind 1.5.2 and queried identically.
| Query | Total hits | Roadmap in top 10 | First durable result |
|---|---|---|---|
purchase order | 108 | 4 → 4 | 1 → 1 |
universe pattern | 89 | 0 → 0 | 1 → 1 |
bitemporal | 146 | 4 → 0 | 1 → 1 |
edit lifecycle | 170 | 6 → 2 | 1 → 1 |
authentication | 218 | 0 → 0 | 1 → 1 |
item image upload | 164 | 10 → 10 | none → none |
changelog | 217 | 10 → 8 | none → 1 |
settings | 619 | 6 → 5 | 3 → 1 |
email | 233 | 9 → 1 | 5 → 2 |
scan | 136 | 5 → 4 | 1 → 1 |
Roadmap pages in the top 10 fall from 54 to 34 out of 100 result slots. For purchase order, ranks 2 through 5 change from three roadmap pages and one product page to four durable pages (use cases, information model, current-system functional, decision record) while the roadmap pages slide to 6–9. For email, the first durable result moves from rank 5 to rank 2.
Two results deserve attention:
item image uploadstays 10 out of 10 roadmap. The only content on that topic is roadmap content. Demotion correctly leaves it findable rather than manufacturing a durable result that does not exist.changelogandsettingsimprove only marginally. Both are terms where roadmap pages genuinely carry the densest coverage. A stronger weight would push them further; whether that is desirable is open question 2.
Options
Section titled “Options”A — Post-build HTML rewrite
Section titled “A — Post-build HTML rewrite”A small Astro integration listed before starlight() in the integrations array, hooking astro:build:done to add data-pagefind-weight to <main data-pagefind-body> in dist/roadmap/** before Pagefind indexes the output. Starlight runs Pagefind from its own astro:build:done hook (node_modules/@astrojs/starlight/index.ts, line 187) and Astro fires integration hooks in array order, so an earlier entry gets to mutate the built HTML first.
This is the option measured above, and it produced the result table.
- For: No coupling to Starlight internals. The path rule sits in
astro.config.mjsnext to the existingstarlight-llms-txtexclude: ['roadmap/**'], so both surfaces state their policy in one place. The weight is tunable without touching content. - Against: Depends on integration hook ordering — documented Astro behavior, but silent if it ever regresses, so it needs a test that asserts the attribute is present in the built output.
B — Override Page.astro
Section titled “B — Override Page.astro”Vendor a copy of Starlight’s Page.astro and add the weight attribute conditionally.
- For: Applies at render time, no build-order dependency.
- Against:
Pageis an internal component Starlight discourages overriding; the copy must be re-reconciled on every Starlight upgrade. The repository already overridesMarkdownContentandFooter, both supported override points — this would be the first unsupported one.
C — Exclude roadmap from search entirely
Section titled “C — Exclude roadmap from search entirely”Set pagefind: false on roadmap pages, or inject data-pagefind-ignore through the same rewrite as option A.
- For: Exactly mirrors the policy already applied to the agent-facing surfaces. The only option offering a hard guarantee rather than a relevance multiplier.
- Against: The
item image uploadrow above is the argument against — that content becomes unreachable through search, and it is the only content on its topic. Setting frontmatter by hand would touch 689 files; path-based injection avoids that but is then no simpler than option A.
D — Section filter facet
Section titled “D — Section filter facet”Inject data-pagefind-filter="section:roadmap" at the same point as option A. Pagefind’s default UI renders a filter panel automatically when the index carries filters, so this needs no UI override — verified against the built corpus in Front-Page Dual Search, where a search of email produced a facet listing every section with its hit count.
- For: Gives readers an explicit, visible control. Composes with A rather than competing with it.
- Against: Adds a facet panel to the search modal for all sections, a visible UI change beyond the stated scope.
Ruled Out
Section titled “Ruled Out”Client-side reordering. Pagefind’s default UI exposes sort, which overrides relevance ranking entirely rather than adjusting it, and processResult, which can modify a result but cannot reorder the result set. Passing either would also require overriding Starlight’s Search component, because Starlight validates the pagefind option against a Zod schema that strips unrecognized keys.
Multisite indexWeight. Starlight surfaces Pagefind’s indexWeight, but it weights a whole merged index against another site’s index. With a single index it has no per-section meaning.
Open Questions
Section titled “Open Questions”- Demote or exclude — should roadmap content still be findable through search while a project is active? The
item image uploadresult argues for demotion. - What weight?
0.2produces the table above. Stronger values were not measured against the real corpus; the harness makes that a single re-run. - Should the same rule cover other low-durability areas, or is
roadmap/the only one? - Is the filter facet (option D) wanted, given it changes the search modal’s appearance?
- Should demotion vary by project
status— acompletedproject’s pages demoted harder than anin-progressone’s?
Reproducing
Section titled “Reproducing”The harness lives in scratch/pagefind-probe/ (untracked). It indexes a directory of HTML with the repository’s own Pagefind and drives the browser-only search API through Playwright.
# Mechanism fixturesnode build.mjs ./site && node probe.mjs "widget calibration" ./site
# Real corpus: baseline vs treatment, after `make build`rsync -a --include='*/' --include='*.html' --exclude='*' ../../dist/ base/rsync -a base/ treat/find treat/roadmap -name '*.html' -print0 | xargs -0 \ perl -pi -e 's/<main data-pagefind-body/<main data-pagefind-body data-pagefind-weight="0.2"/'node build.mjs ./base && node build.mjs ./treat && node compare.mjs ./base ./treat 10Copyright: © Arda Systems 2025-2026, All rights reserved