From 21c6747c582e94696d9fed7e51dbe08858cce639 Mon Sep 17 00:00:00 2001 From: Jan Gleixner Date: Fri, 6 Nov 2026 07:26:41 +0100 Subject: [PATCH 1/2] site: footnotes of a table show below it as table notes, since a preview would be clipped by the table's scroll container --- site/_ext/footnote_previews.py | 23 +++++++++++++++++++++-- site/_static/css/custom.css | 12 ++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/site/_ext/footnote_previews.py b/site/_ext/footnote_previews.py index d44c65d..153143e 100644 --- a/site/_ext/footnote_previews.py +++ b/site/_ext/footnote_previews.py @@ -2,7 +2,9 @@ Each footnote reference gets a copy of its footnote's text beside it, which custom.css shows on hover or keyboard focus. On screen the previews replace the list of footnotes; printing hides them -and shows the list, at the end of the page. The source stays ordinary MyST footnotes. +and shows the list, at the end of the page. Footnotes referenced from a table instead show below +that table, as table notes, on screen and in print: a preview there would be clipped by the +table's scroll container. The source stays ordinary MyST footnotes. """ from docutils import nodes @@ -25,12 +27,28 @@ def _preview(footnote): return preview +def _table(node): + """The table a node is in, or None.""" + while node is not None and not isinstance(node, nodes.table): + node = node.parent + return node + + def add_previews(app, doctree, docname): footnotes = {id_: f for f in doctree.findall(nodes.footnote) for id_ in f["ids"]} for ref in list(doctree.findall(nodes.footnote_reference)): footnote = footnotes.get(ref.get("refid")) if footnote is None: continue + table = _table(ref) + if table is not None: + # a preview would be clipped by the table's scroll container (wide tables scroll + # sideways), so a footnote of a table shows below it, as a table note + if "table-note" not in footnote["classes"]: + footnote.parent.remove(footnote) + table.parent.insert(table.parent.index(table) + 1, footnote) + footnote["classes"].append("table-note") + continue anchor = nodes.inline(classes=["footnote-anchor"]) ref.replace_self(anchor) anchor["ids"] = [] # replace_self moves the reference's ids to its replacement @@ -44,7 +62,8 @@ def add_previews(app, doctree, docname): if not isinstance(c, (nodes.title, nodes.footnote, nodes.transition, nodes.target, nodes.comment)) and not (isinstance(c, nodes.compound) and not c.astext().strip()) # a hidden toctree ] - if not rest and any(isinstance(c, nodes.footnote) for c in section.children): + # ("Notes" may have lost all its footnotes to table notes) + if not rest and (any(isinstance(c, nodes.footnote) for c in section.children) or "notes" in section["ids"]): section["classes"].append("footnote-section") diff --git a/site/_static/css/custom.css b/site/_static/css/custom.css index c34e8eb..4918058 100644 --- a/site/_static/css/custom.css +++ b/site/_static/css/custom.css @@ -239,6 +239,18 @@ h1 img.heading-icon { } } +/* footnotes of a table show below it (a preview would be clipped by the table's scroll container) */ +@media screen { + aside.footnote-list:has(> .table-note) { + display: block; + } +} +.bd-content aside.footnote-list:has(> .table-note) { + margin: -0.5rem 0 1rem; + font-size: 0.85rem; + color: var(--pst-color-text-muted); +} + /* Versions in the main navigation (_ext/static_nav.py) ------------------------------ */ /* the versions below the entries of the abstract data model and the storage format: a line of From 2a122e36c75202fe6f54cb0908b7fc8c2f0cf339 Mon Sep 17 00:00:00 2001 From: Jan Gleixner Date: Fri, 6 Nov 2026 12:20:13 +0100 Subject: [PATCH 2/2] site: footnotes of a table show as previews again on screen, placed outside the table's scroll container; printing shows them below the table --- site/_ext/footnote_previews.py | 19 ++++++++----------- site/_static/css/custom.css | 32 +++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/site/_ext/footnote_previews.py b/site/_ext/footnote_previews.py index 153143e..8dfd073 100644 --- a/site/_ext/footnote_previews.py +++ b/site/_ext/footnote_previews.py @@ -2,9 +2,9 @@ Each footnote reference gets a copy of its footnote's text beside it, which custom.css shows on hover or keyboard focus. On screen the previews replace the list of footnotes; printing hides them -and shows the list, at the end of the page. Footnotes referenced from a table instead show below -that table, as table notes, on screen and in print: a preview there would be clipped by the -table's scroll container. The source stays ordinary MyST footnotes. +and shows the list, at the end of the page; footnotes referenced from a table it shows below that +table instead, as table notes. (The previews in a table are placed outside its scroll container, +which would clip them; custom.css.) The source stays ordinary MyST footnotes. """ from docutils import nodes @@ -41,14 +41,11 @@ def add_previews(app, doctree, docname): if footnote is None: continue table = _table(ref) - if table is not None: - # a preview would be clipped by the table's scroll container (wide tables scroll - # sideways), so a footnote of a table shows below it, as a table note - if "table-note" not in footnote["classes"]: - footnote.parent.remove(footnote) - table.parent.insert(table.parent.index(table) + 1, footnote) - footnote["classes"].append("table-note") - continue + if table is not None and "table-note" not in footnote["classes"]: + # a footnote of a table shows below it, as a table note + footnote.parent.remove(footnote) + table.parent.insert(table.parent.index(table) + 1, footnote) + footnote["classes"].append("table-note") anchor = nodes.inline(classes=["footnote-anchor"]) ref.replace_self(anchor) anchor["ids"] = [] # replace_self moves the reference's ids to its replacement diff --git a/site/_static/css/custom.css b/site/_static/css/custom.css index 4918058..c5c9457 100644 --- a/site/_static/css/custom.css +++ b/site/_static/css/custom.css @@ -239,10 +239,36 @@ h1 img.heading-icon { } } -/* footnotes of a table show below it (a preview would be clipped by the table's scroll container) */ +/* footnotes of a table show as previews too, on screen; the table's scroll container (wide tables + scroll sideways) would clip them, so they are placed relative to the viewport, at their + reference. Printing shows them below the table, as table notes; so does the screen of a browser + without anchor positioning, instead of the previews */ @media screen { - aside.footnote-list:has(> .table-note) { - display: block; + table .footnote-anchor { + anchor-name: --footnote-anchor; + anchor-scope: --footnote-anchor; + } + table .footnote-anchor:hover > .footnote-preview, + table .footnote-anchor:focus-within > .footnote-preview { + position: fixed; + position-anchor: --footnote-anchor; + inset: auto; + /* below the reference (above, if there is no room), centred on it but kept in the viewport */ + position-area: block-end span-all; + justify-self: anchor-center; + position-try-fallbacks: flip-block; + margin-inline: 0.5rem; + text-align: left; + } +} +@supports not ((anchor-name: --a) and (anchor-scope: --a)) { + table .footnote-preview { + display: none !important; + } + @media screen { + aside.footnote-list:has(> .table-note) { + display: block; + } } } .bd-content aside.footnote-list:has(> .table-note) {