Files
blog-eleventy-indiekit/_includes/components/widgets/toc.njk
Ricardo 48160a5b13 feat: client-side TOC widget with Alpine.js scroll spy
Replace the server-side toc.njk placeholder (which never rendered because
no code populated the `toc` variable) with a client-side Alpine.js component
that scans .e-content headings at page load, builds a dynamic table of
contents, and highlights the current section via IntersectionObserver.

- Only appears on articles/notes with 3+ headings (h2-h4)
- Excluded at build time for bookmarks, likes, and reposts
- Scroll spy activates heading in top 30% of viewport

Confab-Link: http://localhost:8080/sessions/cc343b15-8d10-43cd-a48f-ca912eb79b83
2026-03-10 13:01:53 +01:00

28 lines
1.1 KiB
Plaintext

{# Table of Contents Widget — client-side Alpine.js heading scanner with scroll spy #}
{# Only renders on Articles/Notes with 3+ headings (h2-h4) #}
{% if title or (not (bookmarkOf or bookmark_of or likeOf or like_of or repostOf or repost_of)) %}
<div x-data="tocScanner" x-show="items.length > 0" x-cloak>
<div class="widget">
<h3 class="widget-title">Contents</h3>
<nav class="toc" aria-label="Table of contents">
<ul class="space-y-1 text-sm">
<template x-for="item in items" :key="item.id">
<li :class="{
'ml-3': item.level === 3,
'ml-6': item.level === 4
}">
<a :href="'#' + item.id"
x-text="item.text"
class="transition-colors hover:underline"
:class="item.active
? 'text-accent-600 dark:text-accent-400 font-medium'
: 'text-surface-600 dark:text-surface-400 hover:text-accent-600 dark:hover:text-accent-400'"
></a>
</li>
</template>
</ul>
</nav>
</div>
</div>
{% endif %}