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
requestAnimationFramefor 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-innerdiv (Line 213) - Added mobile-specific padding adjustments for fixed header
Layout Adjustments
- Modified:
site/src/layouts/CollectionReaderLayout.astro- Added
padding-top: 60pxto.collection-sidebarfor mobile TOC accommodation - Added
padding-top: 0 !importantto.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
- Responsive Design: Only displays on screens below 1024px (
@media (max-width: 1024px)) - Fixed Positioning:
position: fixed; top: 60px; z-index: 100; - Scroll Margin:
element.style.scrollMarginTop = '160px'for proper scroll positioning - Active State Tracking: Real-time detection of current heading with 200px threshold
- Label Updates: Dynamic button text showing current active heading
- 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
extractAllTextandslugifyutilities from@utils/slugify - Leverages mdast (markdown abstract syntax tree) for heading extraction
- Compatible with existing
TableOfContents.astroarchitecture
Layout System
- Works within
OneArticleOnPage.astrocomponent structure - Compatible with
CollectionReaderLayout.astrowhen 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
requestAnimationFramesupport - CSS backdrop-filter support for blur effects
- Smooth scrolling behavior support
- Passive event listeners for performance
Performance Considerations
- Uses
requestAnimationFramefor smooth scroll tracking - Passive event listeners to prevent blocking
- Efficient DOM queries with
querySelectorAll - Minimal reflows with CSS transforms for animations