Skip to content

Conversation

@mmcky
Copy link
Contributor

@mmcky mmcky commented Dec 11, 2025

Summary

This PR implements a sticky right-hand side table of contents (TOC) that remains fixed while scrolling and highlights the currently active section.

Fixes

Implementation

New Features

  • Sticky TOC: When enabled, the TOC remains fixed at the top of the viewport while scrolling
  • Scroll Spy: Automatically highlights the current section in the TOC as you scroll through the page
  • Configurable: Feature is disabled by default and can be enabled via sticky_contents: true in _config.yml

Technical Approach

  • Uses position: fixed instead of position: sticky for reliable behavior regardless of parent container positioning
  • Pure vanilla JavaScript with no jQuery dependency
  • Uses requestAnimationFrame for throttled scroll handling (smooth performance)
  • Modular design with new scrollspy.js module

Files Changed

  • src/quantecon_book_theme/assets/scripts/scrollspy.js (NEW) - Scroll spy functionality
  • src/quantecon_book_theme/assets/scripts/index.js - Import and initialize scrollspy
  • src/quantecon_book_theme/assets/styles/_page.scss - Sticky positioning and active highlight styles
  • src/quantecon_book_theme/theme/quantecon_book_theme/layout.html - Conditional sticky class
  • src/quantecon_book_theme/theme/quantecon_book_theme/theme.conf - New sticky_contents option

Usage

Add to your Jupyter Book _config.yml:

sphinx:
  config:
    html_theme_options:
      sticky_contents: true

Testing

  • All 25 unit tests passing
  • Pre-commit checks passing
  • CI workflow configured to test with sticky_contents: true in lecture-python-programming.myst

- Add sticky_contents config option (defaults to False)
- Use position: fixed for reliable sticky behavior
- Add scrollspy.js module for tracking scroll position
- Highlight active section in TOC as user scrolls
- Support scrollable TOC for long content lists

Fixes #133

Usage:
  html_theme_options:
    sticky_contents: true
@codecov
Copy link

codecov bot commented Dec 11, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@0358a7c). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #350   +/-   ##
=======================================
  Coverage        ?   45.21%           
=======================================
  Files           ?        2           
  Lines           ?      387           
  Branches        ?        0           
=======================================
  Hits            ?      175           
  Misses          ?      212           
  Partials        ?        0           
Flag Coverage Δ
pytests 45.21% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link

github-actions bot commented Dec 11, 2025

🎭 Visual Regression Test Results

passed  37 passed
skipped  1 skipped

Details

stats  38 tests across 1 suite
duration  48.6 seconds
commit  1a4182b

Skipped tests

mobile-chrome › theme.spec.ts › Theme Features › f-string interpolation styling

@github-actions
Copy link

github-actions bot commented Dec 11, 2025

@github-actions github-actions bot temporarily deployed to pull request December 11, 2025 00:29 Inactive
- Shows floating button at bottom right when page is scrolled down
- Only appears when sticky_contents is enabled
- Uses modern Sass color.adjust() function
- Animated appearance with fade and slide transition
- Button hidden until user scrolls past 300px threshold
@github-actions github-actions bot temporarily deployed to pull request December 11, 2025 00:42 Inactive
@github-actions github-actions bot temporarily deployed to pull request December 11, 2025 01:20 Inactive
- Subsections are hidden by default in sticky TOC mode
- When a section becomes active, its parent sections expand
- When scrolling away, subsections collapse back
- Smooth animated transitions for expand/collapse
- Only affects sticky mode - non-sticky TOC unchanged
@github-actions github-actions bot temporarily deployed to pull request December 11, 2025 01:32 Inactive
- Subsections now stay expanded while scrolling through content
- Parent section stays expanded when any child is active
- Also expands parent's children when parent heading is active
- Only collapses when scrolling to a different top-level section
@github-actions github-actions bot temporarily deployed to pull request December 11, 2025 01:46 Inactive
- Track which top-level TOC section each item belongs to
- When active item changes, keep its entire top-level section expanded
- Expand ALL nested items within the active top-level section
- Subsections only collapse when scrolling to a different top-level section
- Uses :has() selector for finding items with children
@github-actions github-actions bot temporarily deployed to pull request December 11, 2025 04:32 Inactive
- Fix parent traversal to correctly find top-level TOC item
- Replace :has() selector with :scope > ul for broader browser support
- Ensure all nested items with children get expanded class
@github-actions github-actions bot temporarily deployed to pull request December 11, 2025 04:57 Inactive
- New theme option: contents_autoexpand (default: false)
- When false: TOC shows all sections normally (no collapse/expand)
- When true: Subsections auto-collapse/expand based on scroll position
- Allows sticky TOC to ship while auto-expand is refined
- Uses data-autoexpand attribute to pass config to JavaScript

Usage in _config.yml:
  html_theme_options:
    sticky_contents: true
    contents_autoexpand: true  # optional, defaults to false
- Expand all ancestors of the active item (so it's visible in collapsed tree)
- Expand the active item itself if it has children (show its subsections)
- Removed complex top-level tracking - simpler ancestor traversal
- When on 4.2, shows 4.2.1, 4.2.2 etc
- When on 4.2.1, keeps 4.2 expanded so 4.2.1 is visible
@github-actions github-actions bot temporarily deployed to pull request December 11, 2025 05:24 Inactive
- Start from parent ul of active item, not the item itself
- Walk up ul->li->ul->li chain correctly
- Ensures parent sections get expanded class when in subsection
@github-actions github-actions bot temporarily deployed to pull request December 11, 2025 05:37 Inactive
@github-actions github-actions bot temporarily deployed to pull request December 11, 2025 05:49 Inactive
- Theme config values come as strings ('True'/'False') not booleans
- Updated conditionals to check for both boolean and string values
- Fixes autoexpand not being enabled despite default True in theme.conf
@github-actions github-actions bot temporarily deployed to pull request December 11, 2025 06:04 Inactive
- Remove debug console.log statements from scrollspy.js
- Add CSS for contents_autoexpand=false mode (hides subsections)
- Add documentation for sticky_contents and contents_autoexpand options
- Add test_sticky_toc unit test for HTML output verification
- Re-enable visual regression tests in CI workflow
@mmcky
Copy link
Contributor Author

mmcky commented Dec 11, 2025

  • check playright diffs and update fixtures
  • for long CONTENTS due to subsection expansion, can content just expand and collapse dropping content off the bottom if needed to remove the need for scroll bar.
Screenshot 2025-12-11 at 5 19 51 pm
  • test both options for autoexpand: True/False
  • get feedback from the team on the proposed new Top floating button

@github-actions github-actions bot temporarily deployed to pull request December 11, 2025 06:26 Inactive
- Change overflow-y from 'auto' to 'hidden' for cleaner appearance
- Content that exceeds viewport height will be clipped at bottom
- Also adds Core Rules section to copilot-instructions.md
@github-actions github-actions bot temporarily deployed to pull request December 12, 2025 01:24 Inactive
@mmcky
Copy link
Contributor Author

mmcky commented Dec 12, 2025

@jstac @DrDrij I have implemented the sticky contents feature we discussed in #133.
(@DrDrij I took what you started working on in 2021 and built on that).

This implementation is enabled through an option for the theme.

Here is a video of the new implementation which keeps the RHS contents visible while you scroll the page, and continuously provides context as you scroll through sections and subsections in the document. It also has a new Top floating button at the bottom right of the page which is in theme blue.

demo-stick-contents-autoexpand.mov

Interested in thoughts / comments / suggestions.

@mmcky
Copy link
Contributor Author

mmcky commented Dec 12, 2025

Here is demo of contents_autoexpand = False showing just top level sections, not sub-sections.

demo-autoexapand-false.mov

- When contents_autoexpand is false, highlight the top-level parent section
  instead of the hidden subsection as user scrolls through content
- Move back-to-top button outside .inner container to prevent clipping
  from overflow-y: hidden
@DrDrij
Copy link
Member

DrDrij commented Dec 16, 2025

demo-stick-contents-autoexpand.mov

Interested in thoughts / comments / suggestions.

Functionality look perfect @mmcky. Nothing to add.

Just a thought, since you're introducing anchor points, why not make these accessible for users to share links to specific context..

image

@mmcky
Copy link
Contributor Author

mmcky commented Dec 17, 2025

Thanks @DrDrij.

Nice idea, would you add that to Contents or next to the titles themselves (when reading the doc) do you think?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Adjustments for RHS TOC

3 participants