Files
garden-astro/src/components/Toc.astro
T
wesandClaude Opus 4.8 a05e3f7661 Footer quotes + right-hand table of contents
- two static quotes above the name line in every footer
- Toc component renders h1-h3 from Astro's render() headings as a sticky
  right column on >=88rem screens (hidden below); scroll-position
  highlighting via a small passive listener; shown only when a page has
  2+ headings, so listings and short notes stay clean

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 15:34:20 -04:00

48 lines
1.2 KiB
Plaintext

---
interface Props {
headings: { depth: number; slug: string; text: string }[];
}
const { headings } = Astro.props;
const shown = headings.filter((h) => h.depth <= 3);
const min = Math.min(...shown.map((h) => h.depth));
---
<nav class="toc">
<p class="toc-title">On this page</p>
<ul>
{
shown.map((h) => (
<li class={`toc-d${h.depth - min}`}>
<a href={'#' + h.slug}>{h.text}</a>
</li>
))
}
</ul>
</nav>
<script>
// highlight the section currently in view
const toc = document.querySelector('.toc');
if (toc) {
const links = [...toc.querySelectorAll('a[href^="#"]')] as HTMLAnchorElement[];
const heads = links
.map((a) => ({
a,
el: document.getElementById(decodeURIComponent(a.hash.slice(1))),
}))
.filter((x) => x.el);
const update = () => {
let cur = heads[0];
for (const h of heads) {
if (h.el!.getBoundingClientRect().top <= 110) cur = h;
else break;
}
heads.forEach((h) => h.a.classList.toggle('toc-active', h === cur));
};
if (heads.length) {
document.addEventListener('scroll', update, { passive: true });
update();
}
}
</script>