Skip to content

fix: don't highlight the first chapter when another one is the index - #3189

Open
VXNCXNX wants to merge 1 commit into
rust-lang:masterfrom
VXNCXNX:fix/toc-index-chapter-highlight
Open

fix: don't highlight the first chapter when another one is the index#3189
VXNCXNX wants to merge 1 commit into
rust-lang:masterfrom
VXNCXNX:fix/toc-index-chapter-highlight

Conversation

@VXNCXNX

@VXNCXNX VXNCXNX commented Aug 14, 2026

Copy link
Copy Markdown

Fixes #2883.

The problem

The sidebar treats the first chapter as an alias for index.html:

|| i === 0 && path_to_root === '' && current_page.endsWith('/index.html')

That holds when the first chapter is the index page. But a README.md chapter placed later in SUMMARY.md generates index.html itself, so loading the index highlights both it and the first chapter:

SUMMARY.md:  [Prefix 1](prefix-1.md)
             [Prefix 2](prefix-2.md)
             - [Introduction](README.md)

before:  ACTIVE: [ 'Prefix 1', '1. Introduction' ]
after:   ACTIVE: [ '1. Introduction' ]

which matches the screenshot in the issue.

The fix

Skip the alias when some chapter already links to index.html:

const has_index_chapter = links.some(link => link.getAttribute('href') === 'index.html');

In that case the real index chapter matches current_page directly on the line above, so the first-chapter fallback isn't needed and is actively wrong.

getAttribute rather than link.href is deliberate: link.href is reassigned inside the loop (link.href = path_to_root + href), and the browser-resolved absolute form would never equal a bare string on a file:// book. Reading the raw attribute before the loop is the consistent choice.

Why the exact string comparison holds up

This was the part I most expected to be brittle, so I checked rather than assumed. The href comes from Chapter::path verbatim (helpers/toc.rs:119-125), and the index preprocessor rewrites that path with set_file_name("index.md") (builtin_preprocessors/index.rs:35-40), so the filename is always literally index. Across 14 books I built to try to break it — ./README.md in SUMMARY, site-url set, ReadMe.markdown, a README.md#top fragment, a prefix chapter, the index preprocessor disabled — every one produced exactly index.html. site-url only affects the 404 page's base_url; --dest-dir and no-section-label don't touch hrefs.

The subdirectory case is correctly not matched: src/sub/README.md gives href sub/index.html, and the root index.html really is generated from the first chapter there, so the alias should still apply.

I also looked for a false positive — a book with both src/index.md and src/README.md. That configuration is already broken and warned about (it emits duplicate index.html hrefs and highlighted three entries before this change); with the patch it highlights two rather than three. Not made worse.

Tests

Added a case to tests/gui/sidebar-active.goml with a new fixture book whose index chapter is not first.

I ran the GUI tests for real rather than reasoning about them — no browser was present, so I installed puppeteer's chrome:

cargo test --test gui         23 succeeded, 0 failed
cargo test --workspace        all green
npm run lint                  clean

and confirmed the new case is load-bearing by stashing only the toc.js.hbs change:

[ERROR] line 23: expected 1 elements, found 2: assert-count: (".sidebar-scrollbox a.active", 1)
[ERROR] line 24: `Prefix 1` isn't equal to `1. Introduction`
<= doc-ui tests done: 22 succeeded, 1 failed

No CHANGELOG entry, since CONTRIBUTING says those are generated at release time with cargo xtask changelog — let me know if you'd like one anyway.

One pre-existing thing I noticed

A SUMMARY link written as sub/../README.md produces href sub/../index.html, which this comparison misses. That book already has a broken path_to_root and highlights nothing before or after the change, so it looks like a separate pre-existing bug rather than something this should handle.

the sidebar treats the first chapter as an alias for index.html, but a README chapter placed later in SUMMARY.md generates index.html itself, so both entries were marked active. Skip the alias when a chapter already links to index.html. Fixes rust-lang#2883.
@rustbot rustbot added the S-waiting-on-review Status: waiting on a review label Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: waiting on a review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

README that is not the first chapter confuses the current page highlight

2 participants