ghost-toc-plugin: a floating, scroll-following table of contents for Ghost

Hi everyone :waving_hand:

I built a small widget I wanted to share: ghost-toc-plugin, a floating table of contents that sits next to your post, follows the scroll, and highlights the section a reader is currently on. It is free and open source (MIT).

You paste one snippet into Code Injection once, and every post that has headings gets a table of contents automatically. There is nothing to add or maintain inside individual posts.

Try it live first

There is a demo page that is the widget, plus a configurator: change any option in the left panel and it redraws in real time and updates the embed code you copy. What you see is exactly what you paste.

:backhand_index_pointing_right: Demo and configurator: ghost-toc-plugin: Floating table of contents for Ghost · configurator & demo

What makes it nice to use

  • Floating and scroll-aware. The panel stays beside your content and highlights the current heading as the reader moves down the page.
  • Set it once, forget it. It reads your article container (.gh-content by default), so it applies to every post with no per-post work.
  • Tiny and self-contained. About 3KB, no dependencies, no external requests, no build step.
  • Matches your theme. It picks up your Ghost accent color automatically, and light and dark modes are handled for you.
  • Never in the way. When there is no room beside the text (narrow layouts, mobile), it hides itself, so it never covers your content.
  • Independent of your theme. It runs from Code Injection and keeps every class and CSS variable under its own namespace, so it survives theme updates and switches and never fights your theme’s CSS.

Install

Ghost Admin → Settings → Code injection → Site Footer:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/GreedyLabs/ghost-toc-plugin@1/toc.css">
<script src="https://cdn.jsdelivr.net/gh/GreedyLabs/ghost-toc-plugin@1/toc.min.js"
        data-content=".gh-content"
        data-headings="h2,h3"
        data-title="Contents"></script>

Save, and it is live.

Customize with data-* attributes

Everything is configured on the script tag, so there is nothing to touch in CSS unless you want to:

data-content       article container selector       (.gh-content, article, main, ...)
data-headings      which headings to include        (h2,h3)
data-title         panel title, empty to hide it    (Contents)
data-position      right | left                     (right)
data-accent        active-item color                (theme accent)
data-min-width     hide below this viewport width   (1200)
data-min-headings  do not render below this many    (2)
data-top           offset from the top              (100)
data-width         panel width                      (240)
data-gap           gap between content and panel    (28)

Want deeper headings in the list? Just add them, for example data-headings="h2,h3,h4".

Good to know

  • It is a reading enhancement for the web view, so it does not appear in email newsletters or RSS, which do not run JavaScript.
  • It is designed to live in the margin, so on narrow layouts it hides by design. Lower data-min-width if you want it to show on smaller screens.

Beyond Ghost

It started as a Ghost widget, but it works anywhere you can inject a script. For Notion-based hosts (oopy, super.so) set data-content=".notion-page-content" and paste the snippet into the host’s custom-code area.

Links

Feedback and feature requests are very welcome. Hope it is useful to someone here :raising_hands:

4 Likes