Skip to main content

Sphinx extension that shows full glossary term definitions in a popup (no truncation, offline, vanilla JS).

Project description

PyPI Python versions License: MIT 日本語

A Sphinx extension that shows full glossary term definitions in a popup — no truncation, offline, vanilla JS.

When a reader hovers or clicks a :term: reference, the complete glossary definition appears in a popover right where they are reading, so they never have to jump to the glossary page and back.

Features

  • No truncation — the full glossary definition is shown, with a configurable max height/width and scrolling for long entries.

  • Offline — definitions are rendered at build time and injected as inline <template> elements. No network requests, no CDN.

  • Vanilla JS — no third-party runtime libraries are bundled.

  • Hover and click — open popovers on hover, click, or both.

  • Footnotes & citations — optionally show footnote and citation bodies in the same popover style.

  • Image lightbox — optionally open linked images in a focus-trapped lightbox.

  • Layered sanitization — HTML is sanitized with nh3 at build time and re-checked in the browser as a second line of defense.

  • Incremental build aware — pages that reference a term are rebuilt when the term’s home definition changes.

  • Nested popovers — a :term: link inside a popover opens a second-level popover (capped at two levels, configurable).

Requirements

  • Python >= 3.11

  • Sphinx >= 9

Installation

From PyPI:

pip install sphinx-riddle-whisper

From source:

pip install git+https://github.com/usaturn/sphinx-riddle-whisper.git

Or clone and install locally (this project uses uv):

git clone https://github.com/usaturn/sphinx-riddle-whisper.git
cd sphinx-riddle-whisper
uv pip install .

Quick Start

Add the extension to your conf.py:

extensions = ["sphinx_riddle_whisper"]

Define your terms with the standard glossary directive:

.. glossary::

   reStructuredText
       A plain-text markup syntax and parser system, and the default
       markup language used by Sphinx.

Then reference them from any page with the :term: role:

Sphinx documents are usually written in :term:`reStructuredText`.

Build your HTML as usual. Hovering or clicking the :term: reference now opens a popover containing the full definition — no extra markup required.

Configuration

All options are set in conf.py. The display and feature options and their defaults are:

Option

Type

Default

Description

riddle_trigger

str

"both"

How popovers open: "hover", "click", or "both".

riddle_max_height

str

"24rem"

Maximum popover height (any CSS length).

riddle_max_width

str

"32rem"

Maximum popover width (any CSS length).

riddle_open_delay_ms

int

150

Delay before opening on hover, in milliseconds (>= 0).

riddle_close_delay_ms

int

100

Delay before closing on hover-out, in milliseconds (>= 0).

riddle_interactive

bool

True

Keep the popover open while the pointer is over it.

riddle_include_term_title

bool

True

Show the term name as a heading at the top of the popover.

riddle_footnotes

bool

True

Enable popovers for footnote and citation references.

riddle_image_popup

bool

True

Enable the image lightbox.

riddle_nested

bool

True

Open a second, nested popover from :term: links inside a popover.

Example showing the defaults:

riddle_trigger = "both"
riddle_max_height = "24rem"
riddle_max_width = "32rem"
riddle_open_delay_ms = 150
riddle_close_delay_ms = 100
riddle_interactive = True
riddle_include_term_title = True
riddle_footnotes = True
riddle_image_popup = True
riddle_nested = True

Footnotes, Citations & Image Lightbox

With riddle_footnotes = True (the default), footnote and citation references open a popover containing the footnote or citation body, drawn from the same page — so readers can check a reference without losing their place.

With riddle_image_popup = True (the default), a linked image opens in a focus-trapped, scroll-locked lightbox. Only safe image URLs (recognized image extensions over allowed schemes) are opened.

Set either option to False to disable that feature.

Nested Popovers

With riddle_nested = True (the default), a :term: link inside an open popover opens a second, nested popover on top of the first, so definitions that reference other terms can be explored without leaving the page. Nesting is capped at two levels — :term: links inside the second popover behave as normal links — and a link pointing to the term already shown in the first popover does not open a duplicate. Press Esc to close the inner popover first, then the outer one. Set the option to False to keep popover-internal :term: links inert.

Security

Glossary definitions can contain arbitrary inline HTML, so the extension sanitizes content in two layers:

  • Build time — definition fragments are sanitized with nh3 against a built-in allowlist of tags, attributes, and URL schemes before being injected.

  • Runtime — before a popover is shown, the cloned fragment is walked again in the browser: elements outside the allowlist are removed, on* handlers and dangerous URL schemes are stripped, and target="_blank" links get rel="noopener noreferrer".

The safety-related options let you tune or replace the build-time allowlist:

Option

Type

Default

Description

riddle_sanitize

bool

True

Enable build-time HTML sanitization with nh3.

riddle_allowed_tags

tuple[str] | None

None

Allowed HTML tags. None uses the built-in allowlist.

riddle_allowed_attributes

dict[str, tuple[str]] | None

None

Allowed attributes per tag. None uses the built-in allowlist.

riddle_allowed_schemes

tuple[str] | None

None

Allowed URL schemes. None uses the built-in allowlist.

riddle_strip_classes

tuple[str]

("headerlink", "sd-stretched-link")

CSS class names whose anchors are removed from the definition.

Documentation

The full documentation is built with Sphinx from the docs/ directory of this repository.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sphinx_riddle_whisper-1.1.0.tar.gz (37.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sphinx_riddle_whisper-1.1.0-py3-none-any.whl (44.7 kB view details)

Uploaded Python 3

File details

Details for the file sphinx_riddle_whisper-1.1.0.tar.gz.

File metadata

  • Download URL: sphinx_riddle_whisper-1.1.0.tar.gz
  • Upload date:
  • Size: 37.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for sphinx_riddle_whisper-1.1.0.tar.gz
Algorithm Hash digest
SHA256 a6b27f60db4b0bc0f93546022d4b195c61c7de84c1d12d0cda37699f6461ad71
MD5 3e130c121dc077b8c0ac3e3fa09af698
BLAKE2b-256 74eccd487b1a3f35f1efdc99b918e0623b01543c4c234a5bcca4632c2ed45a29

See more details on using hashes here.

Provenance

The following attestation bundles were made for sphinx_riddle_whisper-1.1.0.tar.gz:

Publisher: publish-to-pypi.yml on usaturn/sphinx-riddle-whisper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sphinx_riddle_whisper-1.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for sphinx_riddle_whisper-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a5db67da2acfb6b3fa48317fa9effe1316ca18beeb3cb322c04cccfa0b5291e8
MD5 e021de3de4104e4a34d8faeb9ff792c9
BLAKE2b-256 21730a41b1882e51e979d1c944e707c849e1c6beb0966486e750ebaf4c186621

See more details on using hashes here.

Provenance

The following attestation bundles were made for sphinx_riddle_whisper-1.1.0-py3-none-any.whl:

Publisher: publish-to-pypi.yml on usaturn/sphinx-riddle-whisper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page