Enhanced TableOfContents Component with Improved Scroll Behavior and Progress Tracking

Summary

Enhanced the TableOfContents component with improved scroll behavior, progress bar functionality, and better user experience through native scrolling and proper scroll margin handling.

Why Care

These improvements provide a more responsive and accurate table of contents experience, with real-time progress tracking and smoother navigation. The changes ensure proper scroll positioning that accounts for fixed headers and provides better visual feedback to users about their reading progress.

Implementation

Changes Made

  • File Modified: site/src/components/markdown/TableOfContents.astro
  • Scroll Behavior: Replaced custom scroll calculation with native scrollIntoView() method
  • Progress Bar: Updated to track scroll position within .collection-reader-pane instead of window
  • Scroll Margin: Implemented dynamic scroll margin application via JavaScript for proper header positioning
  • Event Listeners: Enhanced scroll event handling to target specific content containers
  • Collapsed State: Increased collapsed TOC width from 48px to 60px for better usability

Technical Details

Scroll Behavior Improvements

javascript
// Before: Custom scroll calculation with manual offset
window.scrollTo({
  top: offsetPosition,
  behavior: 'smooth'
});

// After: Native scrollIntoView with dynamic margin
element.style.scrollMarginTop = '150px';
element.scrollIntoView({ behavior: 'smooth', block: 'start' });

Progress Bar Container Targeting

javascript
// Updated to use collection reader pane instead of window
const collectionReaderPane = document.querySelector('.collection-reader-pane');
if (collectionReaderPane) {
  const scrollTop = collectionReaderPane.scrollTop;
  const scrollHeight = collectionReaderPane.scrollHeight;
  const clientHeight = collectionReaderPane.clientHeight;
  const scrollPercentage = Math.min(100, Math.max(0, (scrollTop / (scrollHeight - clientHeight)) * 100));
}

Scroll Event Listener Enhancement

javascript
// Primary: Collection reader pane scroll tracking
const collectionReaderPane = document.querySelector('.collection-reader-pane');
if (collectionReaderPane) {
  collectionReaderPane.addEventListener('scroll', () => {
    // Update active link and progress bar
  }, { passive: true });
} else {
  // Fallback to window scroll
  window.addEventListener('scroll', () => {
    // Fallback implementation
  }, { passive: true });
}

CSS Updates

css
/* Increased collapsed TOC width for better usability */
.toc-sidebar.collapsed {
  width: 60px;
  min-width: 60px;
  max-width: 60px;
}

/* Added scroll margin for headings */
.heading-with-copy,
h1, h2, h3, h4, h5, h6 {
  scroll-margin-top: 500px;
}

Integration Points

  • Content Container: Now properly integrates with .collection-reader-pane scroll behavior
  • Header Positioning: Accounts for fixed headers through dynamic scroll margin application
  • Progress Tracking: Provides real-time feedback on reading progress within the content area
  • Responsive Design: Maintains compatibility with existing responsive breakpoints

Documentation

  • Native Scrolling: Uses browser-native scrollIntoView() for better performance and reliability
  • Scroll Margin: JavaScript-applied scroll margin ensures proper positioning regardless of CSS loading
  • Event Handling: Robust fallback system ensures functionality even if content container structure changes
  • Performance: Uses requestAnimationFrame for smooth scroll event handling without performance impact

Technical Decisions

  1. Native vs Custom Scrolling: Chose native scrollIntoView() over custom calculations for better browser compatibility and performance
  2. JavaScript Scroll Margin: Applied scroll margin via JavaScript instead of CSS due to timing and specificity issues
  3. Container-Specific Tracking: Targeted .collection-reader-pane for more accurate progress tracking
  4. Fallback Strategy: Implemented window scroll fallback to ensure functionality in all scenarios

Performance Impact

  • Positive: Reduced scroll calculation overhead by using native browser methods
  • Positive: More efficient event handling with container-specific listeners
  • Neutral: Minimal impact from additional progress bar updates during scroll
  • Positive: Better user experience with smoother scrolling and accurate positioning