Implement Sticky Table of Contents Sidebar.
Summary
Added a persistent, scrollable, right-aligned Table of Contents (ToC) sidebar across Markdown-rendered article pages using
mdast-util-toc
.Changelog Entry 1: Implement Sticky Table of Contents Sidebar with mdast-util-toc
Why Care
This improves long-form content readability and navigation by allowing users to jump to any section without losing context, particularly on large screens.
Implementation
Changes Made
- Integrated
mdast-util-toc
to generate an MDAST ToC map. - Created a custom MDAST node type:
tableOfContents
. - Modified the AstroMarkdown renderer to handle this new node and conditionally render it in a right sidebar.
- Added logic to suppress rendering of the original page
<h1>
in the ToC block. - Refactored page layout to enable sticky sidebar positioning and ensure content remains centered.
text
📁 content/
📁 components/
📄 AstroMarkdown.astro
📄 OneArticle.astro
📄 OneArticleOnPage.astro
📁 utils/markdown/
📄 remark-toc.ts
Technical Details
- TOC generation:
remark-toc.ts
usestoc()
frommdast-util-toc
to generate a ToCmap
up tomaxDepth: 3
. - Custom MDAST Node: Injected a
tableOfContents
node at the top of the tree withdata.map
.tsconst tocNode: Content = { type: 'tableOfContents', data: { map: result.map } }; tree.children.unshift(tocNode);
Path:utils/markdown/remark-toc.ts
- Rendering: AstroMarkdown handles this node type with:astro
{node.type === "tableOfContents" && node.data?.map && ( <aside class="toc-sidebar"> <Astro.self node={node.data.map} data={data} /> </aside> )}
Path:components/AstroMarkdown.astro
- Layout Integration:
- Right-aligned ToC rendered in
OneArticleOnPage.astro
alongside main content. .toc-sidebar
usesposition: sticky
andalign-self: flex-start
inside adisplay: flex
wrapper for persistence.- Main
.content-wrapper
ensuresoverflow: visible
to allow sticky positioning.
- CSS Fixes:
.toc-sidebar
now has a.toc-scroll-area
child formax-height: 100vh - 4rem
andoverflow-y: auto
.- Responsive fallback hides sidebar on viewports narrower than
1024px
.
Integration Points
- Compatible with
remarkBacklinks
,remarkImages
, andremarkCitations
. - No breaking changes to MDAST structure for existing pages.
- Headings auto-anchor via
github-slugger
-style hash links — no extrarehype-slug
needed. - Future integration: Scroll spy, collapse/expand for subsections.
Documentation
- Internal:
utils/markdown/remark-toc.ts
(custom plugin) - Rendered: Visible
ul
structure in<aside>
sidebar
Changelog Entry 2: Introduce Essays Collection, and Reader Page with dynamic tag filtering
Why Care
This update enables users to view the "Essays" collection, founded in the content repository(
content/essays
), and filter essays by tag, improving SEO, navigation, and discoverability.Implementation
Changes Made
- Created pages for Essays collection, including
/site/src/pages/read/index.astro
and/site/src/pages/read/more-on/[tag].astro
. - Added tag-based filtering logic to
/site/src/pages/read/more-on/[tag].astro
replicating the robust filtering, sorting, and mapping from the main index page. - Ensured consistent data structure for essay items and DRY code between index and tag-filtered pages.
- Created and updated:
/site/src/pages/read/index.astro
/site/src/pages/read/more-on/[tag].astro
/site/components/articles/ArticleListColumn.astro
src/pages/read/essays/[...slug].astro
/site/astro.config.mjs
- Staged and committed all code changes, and pushed to the remote repository.
Technical Details
- All filtering, mapping, and sorting logic is now inside the Astro frontmatter for compliance and maintainability.
- Tag filtering is case-insensitive and only published essays are included.
- Debug output (
console.log(essayItems)
) is available for inspection in the browser console. - No new dependencies were added; all changes are within the existing Astro/TypeScript stack.
Integration Points
- TagChip and ArticleListColumn components are reused for DRY rendering.
- The new tag-filtered page integrates seamlessly with the unified routing and essay data structure.
- No migration steps required; no breaking changes introduced.