Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions site/_ext/footnote_previews.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 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
Expand All @@ -25,12 +27,25 @@ 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 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
Expand All @@ -44,7 +59,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")


Expand Down
38 changes: 38 additions & 0 deletions site/_static/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,44 @@ h1 img.heading-icon {
}
}

/* 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 {
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) {
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
Expand Down
Loading