Skip to content

fix(sidebar): highlight nearest ancestor item - #2120

Merged
markdumay merged 4 commits into
mainfrom
fix/sidebar-ancestor-highlight
Aug 5, 2026
Merged

fix(sidebar): highlight nearest ancestor item#2120
markdumay merged 4 commits into
mainfrom
fix/sidebar-ancestor-highlight

Conversation

@markdumay

Copy link
Copy Markdown
Collaborator

Problem

Navigating to a page that has no sidebar entry of its own leaves the sidebar completely inert — no active item, no aria-current, and every group collapsed. Nothing is highlighted at all.

Sidebar markup is rendered once per (section, version, language, variant) and cached, so the active item is resolved client side by assets/js/critical/sidebar-active.js. That matcher only treated a link as an ancestor candidate if it carried data-sidebar-match="prefix". Only the L1-only mode (level-min <= 1, level-max == 1) emits that attribute; the default full-tree mode never does. So in the full-tree sidebar there were no ancestor candidates at all, and a page missing from the menu matched nothing.

Pages built with build.list = never, and pages generated by a content adapter, are excluded from the page collections assets/live-pages.html builds the menu from. They can never have an entry of their own, so they always land on this path.

Reproduced on a docs site whose CLI reference is generated by a content adapter: on /docs/reference/command-line/version/ the built page had 0 active links, 0 aria-current, 0 expanded groups, and 0 prefix-capable links across both nav instances — while the /docs/reference/command-line/ link was present in the markup the whole time.

Fix

1. fix(sidebar): highlight nearest ancestor item

Exact match still wins outright. Only when no link matches exactly does the longest link that is a path ancestor of the current page take over, flagged aria-current="true" rather than "page". Ancestor matching compares against path + "/", so /docs/guides cannot claim /docs/guides-advanced/.

The brand logo is excluded from the candidates. It sits inside the <nav> and points at the site home, so under ancestor matching it would match every page below it. Verified necessary: without the guard, a page with no ancestor in the menu lights up the logo, and the home page marks it active.

2. refactor(sidebar): drop dead prefix-match markup

With ancestor matching derived from the href, nothing reads data-sidebar-match="prefix" any more — not the script, not the stylesheets, not the docs. Removes the attribute and the prefixMatch argument threading it through the inline item partial. Rendered markup loses one attribute and is otherwise unchanged.

Verification

Check Result
Reported page, before 0 active links, 0 aria-current, 0 expanded groups
Reported page, after correct item active, aria-current="true", parent group expanded, link visible — both navs
exampleSite, all 62 sidebar pages x 2 navs 124/124 identical before vs. after — no regressions
exampleSite, before vs. after the dead-code strip identical result digest across all 62 pages
data-sidebar-match in built HTML after strip 0 occurrences
pnpm test (eslint, stylelint, markdownlint, template build) passes

Note

There is no standing automated coverage for this: the repo has no JS unit harness (pnpm test is lint plus a Hugo template build), and tests/visual/ has Playwright installed but no specs. Verification above was a one-off browser run over both builds. Happy to wire up a tests/visual/ spec in a follow-up if that is wanted.

🤖 Generated with Claude Code

markdumay and others added 2 commits August 5, 2026 07:04
The sidebar markup is rendered once per section and cached, so the
active item is resolved client side. Matching only considered links
flagged `data-sidebar-match="prefix"`, which the L1-only mode emits and
the full-tree mode never does. A page absent from the menu therefore
matched nothing at all: no active item, every group collapsed.

Pages built with `build.list = never`, or generated by a content
adapter, are excluded from the page collections the menu is built from,
so they never have an entry of their own and always hit this path.

Fall back to the longest link that is a path ancestor of the current
page whenever no link matches exactly. Ancestor matching compares
against `path + "/"`, so `/docs/guides` cannot claim
`/docs/guides-advanced/`. The brand logo is excluded from the
candidates: it sits inside the nav and points at the site home, so it
would otherwise match every page below it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`data-sidebar-match="prefix"` marked the links that were allowed to
match the current page as an ancestor. Ancestor matching is now derived
from the href itself and applies to every link, so nothing reads the
attribute: not the highlighting script, not the stylesheets, not the
docs.

Remove the attribute and the `prefixMatch` argument that threaded it
through the inline item partial. Rendered markup loses one attribute
and is otherwise unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@netlify

netlify Bot commented Aug 5, 2026

Copy link
Copy Markdown

Deploy Preview for gethinode-demo ready!

Name Link
🔨 Latest commit bc79b6e
🔍 Latest deploy log https://app.netlify.com/projects/gethinode-demo/deploys/6a72c76eee75210008a53a10
😎 Deploy Preview https://deploy-preview-2120--gethinode-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

markdumay and others added 2 commits August 5, 2026 07:10
A link to the site root is an ancestor of every page, so ancestor
matching would let a "Home" or "Overview" entry claim any page that is
not in the menu. Checked against a consuming app: the sidebar wrongly
highlighted Overview on /contact/, /cookies/ and /privacy/, which sit
outside the menu entirely and previously highlighted nothing.

Skip root links in the ancestor pass. They still win when they match
the current path exactly, so the home page itself is unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@markdumay
markdumay merged commit f9e6fc3 into main Aug 5, 2026
17 checks passed
@markdumay
markdumay deleted the fix/sidebar-ancestor-highlight branch August 5, 2026 05:23
@markdumay

Copy link
Copy Markdown
Collaborator Author

🎉 This PR is included in version 3.19.2 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant