Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEW: Add sidenote and marginnote syntax #546

Merged
merged 48 commits into from
May 19, 2022
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
692f2f7
simple sidenote in footnote example
AakashGfude Mar 30, 2022
652f0c9
adding footnote in get started
AakashGfude Mar 30, 2022
ec9f164
replacing footnote and footnote reference with sidenote Node
AakashGfude Apr 5, 2022
4c51efc
added use_sidenotes config
AakashGfude Apr 5, 2022
3a955f1
adding comments and checks
AakashGfude Apr 6, 2022
9b128a3
undoing docs changes here
AakashGfude Apr 6, 2022
6c06065
removed use_sidenotes: True
AakashGfude Apr 6, 2022
eaad995
added marginnotes as well
AakashGfude Apr 7, 2022
41b52b2
Updating documentation about marginnotes
choldgraf Mar 16, 2022
426d77a
marginnote unique label
AakashGfude Apr 7, 2022
bcd7d29
color for clickable buttons
AakashGfude Apr 7, 2022
8743e2f
Merge branch 'master' into footnote-sidenote
AakashGfude Apr 8, 2022
9b2ddca
checking for latexbuilder
AakashGfude Apr 8, 2022
b783e9e
checking for html builder
AakashGfude Apr 8, 2022
a2ab275
removed latex handling in node
AakashGfude Apr 8, 2022
6b5d97b
formats = html
AakashGfude Apr 8, 2022
73a1da4
testing sidenotes and marginnotes
AakashGfude Apr 12, 2022
8cc71d9
adding pandoc-sidenote in docs
AakashGfude Apr 13, 2022
3195e22
Update docs/content-blocks.md
choldgraf Apr 25, 2022
7db02f1
Merge branch 'master' into footnote-sidenote
choldgraf Apr 25, 2022
e1800a6
Merge branch 'master' into footnote-sidenote
choldgraf Apr 25, 2022
83a5bc7
Update docs/content-blocks.md
AakashGfude May 9, 2022
75d40af
user-select
AakashGfude May 9, 2022
fa96ad5
Merge branch 'master' into footnote-sidenote
choldgraf May 9, 2022
dcfc91d
Merge branch 'footnote-sidenote' of https://github.com/AakashGfude/sp…
AakashGfude May 11, 2022
ac9dcab
nested footnotes/sidenotes
AakashGfude May 11, 2022
593c0a1
Update docs/reference/special-theme-elements.md
AakashGfude May 11, 2022
bb485b6
reducing indentation
AakashGfude May 11, 2022
77e2cdb
Update src/sphinx_book_theme/_transforms.py
AakashGfude May 11, 2022
037e36b
Update src/sphinx_book_theme/_transforms.py
AakashGfude May 11, 2022
ac408dd
merging
AakashGfude May 11, 2022
1a4a531
mobile for inside admonition
AakashGfude May 13, 2022
ff53668
Update src/sphinx_book_theme/_transforms.py
AakashGfude May 13, 2022
d7d1a0d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 13, 2022
8098e03
Update src/sphinx_book_theme/_transforms.py
AakashGfude May 13, 2022
3319a18
Update src/sphinx_book_theme/_transforms.py
AakashGfude May 13, 2022
a3bfaa8
Update src/sphinx_book_theme/_transforms.py
AakashGfude May 13, 2022
b11129a
Update src/sphinx_book_theme/assets/styles/content/_margin.scss
AakashGfude May 13, 2022
a87245f
sidenoteNode docstring, renaming variables in transform, d-none
AakashGfude May 13, 2022
ceae725
resolving conflicts
AakashGfude May 13, 2022
f5efddf
added comments for foot_node
AakashGfude May 13, 2022
cb2da87
Update src/sphinx_book_theme/assets/styles/base/_base.scss
AakashGfude May 13, 2022
b6a9585
Merge branch 'master' into footnote-sidenote
choldgraf May 17, 2022
4282271
d-none
AakashGfude May 18, 2022
76aa1d8
keeping d-n for now
AakashGfude May 18, 2022
d2ea70a
Clean up docs and add examples
choldgraf May 19, 2022
9522bce
Popout -> margin
choldgraf May 19, 2022
293c8aa
Update src/sphinx_book_theme/assets/styles/base/_base.scss
choldgraf May 19, 2022
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
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
"use_issues_button": True,
"use_repository_button": True,
"use_download_button": True,
"use_sidenotes": True,
"logo_only": True,
"show_toc_level": 2,
"announcement": (
Expand Down
142 changes: 104 additions & 38 deletions docs/content-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,87 @@ but I'll stop here.
```
````

## Margin content
(margin:sidenote)=
## Sidenotes and marginnotes

You can specify content that should exist in the right margin. This will behave
like a regular sidebar until the screen hits a certain width, at which point this
content will "pop out" to the right white space.
This theme has support for [Tufte-style margin / side notes](https://edwardtufte.github.io/tufte-css/), with a UX similar to [pandoc-sidenote](https://github.com/jez/pandoc-sidenote).

There are two ways to add content to the margin: via the `{margin}` directive, and via adding CSS classes to your own content.
Sidenotes are numbered, and behave like footnotes, except they live in the margin and don’t force the reader to jump their eye to the bottom of the page.
For example, here is a sidenote[^ex].
On narrow screens, sidenotes are hidden until a user clicks the number.
If you're on a mobile device, try clicking the sidenote number above.

### Use a `{margin}` directive to add margin content
[^ex]: Here's my sidenote text!

The `{margin}` directive allows you to create margin content with your own title and content block.
On narrow screens, this text won't show up unless you click the superscript number!

Marginnotes are not numbered, but behave the same way as sidenotes.
On mobile devices a symbol will be displayed that will display the marginnote when clicked[^exmn].
For example, there's a marginnote in the previous sentence, and you should see a symbol show to display it on mobile screens.

[^exmn]: {-} This is a margin note. Notice there isn’t a number preceding the note.

:::{seealso}
AakashGfude marked this conversation as resolved.
Show resolved Hide resolved
Sidenotes and marginnotes are inline content - you cannot use block-level content inside of these notes.
If you'd like to use block-level content in the margins, see [](margin:block).
:::

### Activate sidenotes and marginnotes

The theme activates sidenotes and marginnotes by over-riding footnote syntax to instead exist in the margin.

To convert your footnotes to *instead* be sidenotes/marginnotes, use this configuration:

```python
html_theme_options = {
...
"use_sidenotes": True,
...
}
```

This will turn your **footnotes** into **sidenotes** or **marginnotes**.

### Create a sidenote

For example, the following sentence defines a sidenote and its respective content:

```markdown
Here's my sentence and a sidenote[^sn1].

[^sn1]: And here's my sidenote content.
```

Here's my sentence and a sidenote[^sn1].

[^sn1]: And here's my sidenote content.

### Create a marginnote

Marginnotes are defined by adding `{-}` at the beginning of the content block.
For example, the following syntax defines a marginnote:

```markdown
Here's my sentence and a marginnote[^mn1].

[^mn1]: {-} And here's my marginnote content.
```

Here's my sentence and a marginnote[^mn1].

[^mn1]: {-} And here's my marginnote content.


(margin:block)=
## Block margin content with the `{margin}` directive

The `{margin}` directive allows you to create block-level margin content with an optional title.
It is a wrapper around the Sphinx `{sidebar}` directive, and largely does its magic via CSS classes (see below).

:::{seealso}
If you'd like in-line margin content with numbered references, see [](margin:sidenote).
:::

Here's how you can use the `{margin}` directive:

````{example}
Expand All @@ -85,7 +153,29 @@ It is pretty cool!
```
````

### Use CSS classes to add margin content
### Figure captions in the margin

You can configure figures to use the margin for captions.
Here is a figure with a caption to the right.

::::{example}
:no-container:

```{figure} images/cool.jpg
---
width: 60%
figclass: margin-caption
alt: My figure text
name: myfig5
---
And here is my figure caption, if you look to the left, you can see that COOL is in big red letters. But you probably already noticed that, really I am just taking up space to see how the margin caption looks like when it is really long :-).
```
::::

We can reference the figure with {ref}`this reference <myfig5>`. Or a numbered reference like
{numref}`myfig5`.

### CSS classes for custom margin content

You may also directly add CSS classes to elements on your page in order to make them behave like margin content.
To do so, add the `margin` CSS class to any element on the page.
Expand All @@ -104,11 +194,7 @@ This note will be in the margin!


This works for most elements on the page, but in general this works best for "parent containers" that are the top-most element of a bundle of content.

For example, we can even put a whole figure in the margin like so:


You can also put the whole figure in the margin if you like.
For example, you can also put the whole figure in the margin if you like.
Here is a figure with a caption below. We'll add a note below to create
some vertical space to see better.

Expand All @@ -129,37 +215,17 @@ And here is my figure caption
We can reference the figure with {ref}`myfig4`. Or a numbered reference like
{numref}`myfig4`.

### Figure captions in the margin

You can configure figures to use the margin for captions.
Here is a figure with a caption to the right.
### Content examples in the margin

````{example}
:no-container:
:reverse:
Margin content can include all kinds of things, such as code blocks:

```{figure} images/cool.jpg
---
width: 60%
figclass: margin-caption
alt: My figure text
name: myfig5
---
And here is my figure caption, if you look to the left, you can see that COOL is in big red letters. But you probably already noticed that, really I am just taking up space to see how the margin caption looks like when it is really long :-).
````{margin} Code blocks in margins
```python
print("here is some python")
```
````

We can reference the figure with {ref}`this reference <myfig5>`. Or a numbered reference like
{numref}`myfig5`.

### Examples of margin content

There are many kinds of content you can put in the margin, here are some examples.

`````{example} Code blocks in the margin
:no-container:
:reverse:

````{margin} Code blocks in margins
```python
print("here is some python")
Expand Down
25 changes: 24 additions & 1 deletion docs/reference/special-theme-elements.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,6 @@ And here is my figure caption, if you look to the left, you can see that COOL is
This note should not overlap with the margin caption!
:::


Entire figures in the margin:

```{figure} ../images/cool.jpg
Expand All @@ -271,6 +270,30 @@ alt: My figure text
This figure should be entirely in the margin.
```

## Sidenotes and marginnotes

Here's a sentence[^sn1] with multiple [^sn2] sidenotes.

[^sn1]: Test sidenote 1.
[^sn2]: Test sidenote 2.

Here's a sentence[^mn1] with multiple marginnotes[^mn2].

[^mn1]: {-} Test marginnote 1.
[^mn2]: {-} Test marginnote 2.


Sidenotes inside of admonitions should behave the same:

:::{note}
An admonition with a sidenote defined in the admonition[^snam1] and another defined outside of the admonition [^snam2].

[^snam1]: Sidenote defined in the admonition.

:::

[^snam2]: Sidenote defined outside the admonition.


## Nested admonitions

Expand Down
12 changes: 10 additions & 2 deletions src/sphinx_book_theme/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
from functools import lru_cache

from docutils.parsers.rst.directives.body import Sidebar
from docutils import nodes
from docutils import nodes as docutil_nodes
from sphinx.application import Sphinx
from sphinx.locale import get_translation
from sphinx.util import logging

from .nodes import SideNoteNode
from .header_buttons import prep_header_buttons, add_header_buttons
from .header_buttons.launch import add_launch_buttons
from ._transforms import HandleFootnoteTransform

__version__ = "0.3.2"
"""sphinx-book-theme version"""
Expand Down Expand Up @@ -43,7 +45,7 @@ def add_metadata_to_page(app, pagename, templatename, context, doctree):
# Add a shortened page text to the context using the sections text
if doctree:
description = ""
for section in doctree.traverse(nodes.section):
for section in doctree.traverse(docutil_nodes.section):
description += section.astext().replace("\n", " ")
description = description[:160]
context["page_description"] = description
Expand Down Expand Up @@ -177,6 +179,9 @@ def setup(app: Sphinx):
app.connect("html-page-context", add_metadata_to_page)
app.connect("html-page-context", hash_html_assets)

# Nodes
SideNoteNode.add_node(app)

# Header buttons
app.connect("html-page-context", prep_header_buttons)
app.connect("html-page-context", add_launch_buttons)
Expand All @@ -186,6 +191,9 @@ def setup(app: Sphinx):
# Directives
app.add_directive("margin", Margin)

# Post-transforms
app.add_post_transform(HandleFootnoteTransform)

# Update templates for sidebar
app.config.templates_path.append(os.path.join(theme_dir, "components"))

Expand Down
81 changes: 81 additions & 0 deletions src/sphinx_book_theme/_transforms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from sphinx.transforms.post_transforms import SphinxPostTransform
from typing import Any
from docutils import nodes as docutil_nodes
from sphinx import addnodes as sphinx_nodes
from .nodes import SideNoteNode
import copy


class HandleFootnoteTransform(SphinxPostTransform):
"""Transform footnotes into side/marginnotes."""

default_priority = 1
formats = ("html",)

def run(self, **kwargs: Any) -> None:
theme_options = self.env.config.html_theme_options
if theme_options.get("use_sidenotes", False) is False:
return None
# Cycle through footnote references, and move their content next to the
# reference. This lets us display the reference in the margin,
# or just below on narrow screens.
for ref_node in self.document.traverse(docutil_nodes.footnote_reference):
parent = None
# Each footnote reference should have a single node it points to via `ids`
for foot_node in self.document.traverse(docutil_nodes.footnote):
# matching the footnote reference with footnote
if (
len(foot_node.attributes["backrefs"])
and foot_node.attributes["backrefs"][0]
== ref_node.attributes["ids"][0]
):
parent = foot_node.parent
# second children of footnote node is the content text
text = foot_node.children[1].astext()

sidenote = SideNoteNode()
para = docutil_nodes.inline()
# first children of footnote node is the label
label = foot_node.children[0].astext()

if text.startswith("{-}"):
# marginnotes will have content starting with {-}
# remove the number so it doesn't show
para.attributes["classes"].append("marginnote")
para.append(docutil_nodes.Text(text.replace("{-}", "")))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we do a .strip() here as well since there will be a space after the {-}?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can I guess. But without strip() it seems to be stripping spaces as well, no matter how much space we give.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough - maybe this is a sphinx parsing thing under the hood

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. possibly.


sidenote.attributes["names"].append(f"marginnote-role-{label}")
else:
# sidenotes are the default behavior if no {-}
# in this case we keep the number
superscript = docutil_nodes.superscript("", label)
para.attributes["classes"].append("sidenote")
para.extend([superscript, docutil_nodes.Text(text)])

sidenote.attributes["names"].append(f"sidenote-role-{label}")
sidenote.append(superscript)

# If the reference is nested (e.g. in an admonition), duplicate
# the content node And place it just before the parent container,
# so it works w/ margin. Only show one or another depending on
# screen width.
node_parent = ref_node.parent
para_dup = copy.deepcopy(para)
# looping to check parent node
while not isinstance(
node_parent, (docutil_nodes.section, sphinx_nodes.document)
):
# if parent node is another container
if not isinstance(
node_parent,
(docutil_nodes.paragraph, docutil_nodes.footnote),
):
node_parent.replace_self([para, node_parent])
para_dup.attributes["classes"].append("d-n")
break
node_parent = node_parent.parent

ref_node.replace_self([sidenote, para_dup])
break
if parent:
parent.remove(foot_node)
2 changes: 2 additions & 0 deletions src/sphinx_book_theme/assets/scripts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ var initTocHide = () => {
// Set up the intersection observer to watch all margin content
let tocObserver = new IntersectionObserver(hideTocCallback);
const selectorClasses = [
"marginnote",
"sidenote",
"margin",
"margin-caption",
"full-width",
Expand Down
4 changes: 4 additions & 0 deletions src/sphinx_book_theme/assets/styles/base/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
width: 1px !important;
}

.d-n {
choldgraf marked this conversation as resolved.
Show resolved Hide resolved
display: none;
}

// Print-specific utility classes
.onlyprint {
display: none;
Expand Down
Loading