Mobile Table of Contents Implementation and Layout Adjustments

Summary

Implemented a mobile-friendly table of contents component for devices below 1024px screen width, featuring a fixed dropdown header with H1/H2 heading navigation and real-time scroll tracking.

Why Care

This enhancement significantly improves mobile user experience by providing easy navigation through long-form content on smaller screens. The fixed header ensures navigation is always accessible, while the scroll tracking provides visual feedback about the user's current position in the document.

Implementation

Changes Made

New Component Creation

  • Created: site/src/components/markdown/MobileTableOfContents.astro
    • Mobile-specific table of contents component
    • Fixed positioning at 60px from top to avoid site header overlap
    • Dropdown interface with toggle button and expandable menu
    • Extracts H1 and H2 headings from markdown content using mdast parsing
    • Implements scroll tracking with requestAnimationFrame for smooth updates
    • Active heading detection with visual feedback and label updates

Component Integration

  • Modified: site/src/components/articles/OneArticleOnPage.astro
    • Added import: import MobileTableOfContents from '@components/markdown/MobileTableOfContents.astro';
    • Integrated component within .content-inner div (Line 213)
    • Added mobile-specific padding adjustments for fixed header

Layout Adjustments

  • Modified: site/src/layouts/CollectionReaderLayout.astro
    • Added padding-top: 60px to .collection-sidebar for mobile TOC accommodation
    • Added padding-top: 0 !important to .collection-reader-pane :global(.prose) to prevent double padding when nested

Bug Fixes

  • Fixed: site/src/components/markdown/TableOfContents.astro
    • Removed problematic import { Fragment } from 'astro'; (Line 4)
    • Replaced <Fragment> tags with <div> tags (Lines 174, 228)
    • Resolved runtime error: "Unable to render Fragment because it is undefined!"

Technical Details

Mobile Table of Contents Architecture

typescript
// Heading extraction from mdast content
interface HeadingItem {
  text: string;
  slug: string;
  depth: number;
  children?: HeadingItem[];
}

// Scroll tracking with requestAnimationFrame
let scrollTimeout: ReturnType<typeof requestAnimationFrame>;
window.addEventListener('scroll', () => {
  if (scrollTimeout) {
    cancelAnimationFrame(scrollTimeout);
  }
  scrollTimeout = requestAnimationFrame(() => {
    this.updateActiveLink();
  });
}, { passive: true });

Key Features Implemented

  1. Responsive Design: Only displays on screens below 1024px (@media (max-width: 1024px))
  2. Fixed Positioning: position: fixed; top: 60px; z-index: 100;
  3. Scroll Margin: element.style.scrollMarginTop = '160px' for proper scroll positioning
  4. Active State Tracking: Real-time detection of current heading with 200px threshold
  5. Label Updates: Dynamic button text showing current active heading
  6. Accessibility: ARIA attributes, keyboard navigation, focus management

CSS Structure

css
.mobile-toc-container {
  display: none; /* Hidden by default */
  position: fixed;
  top: 60px;
  background: var(--clr-primary-bg);
  backdrop-filter: blur(10px);
}

@media (max-width: 1024px) {
  .mobile-toc-container {
    display: block; /* Show on mobile */
  }
}

Scroll Tracking Algorithm

javascript
// Threshold calculation for active heading detection
const threshold = heading.top - 200; // Accounts for scroll margin
if (scrollTop >= threshold) {
  activeHeading = heading;
  break;
}

Integration Points

Content Processing

  • Integrates with existing markdown processing pipeline
  • Uses extractAllText and slugify utilities from @utils/slugify
  • Leverages mdast (markdown abstract syntax tree) for heading extraction
  • Compatible with existing TableOfContents.astro architecture

Layout System

  • Works within OneArticleOnPage.astro component structure
  • Compatible with CollectionReaderLayout.astro when nested
  • Maintains existing responsive breakpoints (1024px)
  • Preserves existing desktop TOC functionality

Styling Integration

  • Uses existing CSS custom properties (--clr-primary-bg, --clr-lossless-accent--brightest)
  • Consistent with site's design system and color scheme
  • Backdrop blur effects matching existing UI components
  • Responsive design patterns aligned with current breakpoints

Documentation

Component Usage

astro
<MobileTableOfContents content={safeContent} effectiveHeading={effectiveHeading} />

Props Interface

typescript
interface Props {
  content: {
    type?: string;
    children?: any[];
  };
  effectiveHeading?: string;
}

Debug Logging

Component includes extensive debug logging when DEBUG_TOC environment variable is enabled:
  • Heading extraction process
  • Scroll position calculations
  • Active heading detection
  • Event listener setup

Browser Compatibility

  • Modern browsers with requestAnimationFrame support
  • CSS backdrop-filter support for blur effects
  • Smooth scrolling behavior support
  • Passive event listeners for performance

Performance Considerations

  • Uses requestAnimationFrame for smooth scroll tracking
  • Passive event listeners to prevent blocking
  • Efficient DOM queries with querySelectorAll
  • Minimal reflows with CSS transforms for animations