0
# Content Transforms
1
2
Post-processing transforms that modify document structure after parsing. The primary transform converts footnotes to margin/side notes for enhanced book-style presentation.
3
4
## Capabilities
5
6
### Footnote to Sidenote Transform
7
8
The main transform class that converts standard footnotes into interactive margin notes.
9
10
```python { .api }
11
class HandleFootnoteTransform(SphinxPostTransform):
12
"""
13
Transform footnotes into side/margin notes for book-style presentation.
14
15
Converts standard reStructuredText footnotes into interactive
16
margin content that appears beside the main text on desktop
17
and below on mobile devices.
18
"""
19
20
default_priority: int = 1 # Transform priority
21
formats: tuple = ("html",) # Only applies to HTML output
22
23
def run(self, **kwargs: Any) -> None:
24
"""
25
Execute the footnote transformation.
26
27
Process:
28
1. Check if sidenotes are enabled in theme options
29
2. Find all footnote references in the document
30
3. Locate corresponding footnote definitions
31
4. Convert footnotes to SideNoteNode instances
32
5. Handle marginnotes (starting with {-}) differently from sidenotes
33
6. Position notes appropriately in document structure
34
7. Remove original footnote definitions
35
36
Parameters:
37
- kwargs: Additional keyword arguments from Sphinx
38
"""
39
```
40
41
### Compatibility Utilities
42
43
Utility functions for cross-version compatibility with docutils.
44
45
```python { .api }
46
def findall(node: Element, *args, **kwargs) -> Iterator[Element]:
47
"""
48
Compatibility wrapper for docutils node traversal.
49
50
Uses findall() in newer docutils versions, falls back to traverse()
51
in older versions.
52
53
Parameters:
54
- node: Document node to search
55
- args: Arguments passed to findall/traverse
56
- kwargs: Keyword arguments passed to findall/traverse
57
58
Returns:
59
Iterator over matching nodes
60
"""
61
```
62
63
## Usage Examples
64
65
### Enabling Sidenote Transform
66
67
```python
68
# In Sphinx conf.py
69
html_theme_options = {
70
"use_sidenotes": True # Enable footnote to sidenote conversion
71
}
72
```
73
74
### Standard Footnote Usage
75
76
In reStructuredText source:
77
78
```rst
79
This is regular text with a footnote reference [#note1]_.
80
81
This is text with a margin note [#margin1]_.
82
83
.. [#note1] This will become a sidenote with a number.
84
85
.. [#margin1] {-} This will become a marginnote without a number.
86
```
87
88
### Transform Registration
89
90
```python
91
from sphinx.application import Sphinx
92
from sphinx_book_theme._transforms import HandleFootnoteTransform
93
94
def setup(app: Sphinx):
95
# Register the post-transform
96
app.add_post_transform(HandleFootnoteTransform)
97
98
return {"parallel_read_safe": True}
99
```
100
101
### Custom Transform Implementation
102
103
```python
104
from sphinx_book_theme._transforms import HandleFootnoteTransform
105
from sphinx_book_theme._compat import findall
106
from docutils import nodes as docutil_nodes
107
108
class CustomFootnoteTransform(HandleFootnoteTransform):
109
"""Custom footnote transform with additional processing."""
110
111
def run(self, **kwargs):
112
# Get theme options
113
theme_options = get_theme_options_dict(self.app)
114
115
if not theme_options.get("use_sidenotes", False):
116
return None
117
118
# Use compatibility function for node traversal
119
for ref_node in findall(self.document, docutil_nodes.footnote_reference):
120
# Custom processing logic here
121
pass
122
```
123
124
### Manual Node Traversal
125
126
```python
127
from sphinx_book_theme._compat import findall
128
from docutils import nodes
129
130
# Find all emphasis nodes in a document
131
for emphasis in findall(document, nodes.emphasis):
132
print(f"Emphasized text: {emphasis.astext()}")
133
134
# Find nodes with specific attributes
135
for node in findall(document, nodes.paragraph):
136
if 'highlight' in node.attributes.get('classes', []):
137
print(f"Highlighted paragraph: {node.astext()}")
138
```
139
140
## Transform Behavior
141
142
### Sidenote Processing
143
144
1. **Detection**: Finds footnote references like `[#note1]_`
145
2. **Matching**: Locates corresponding footnote definitions `.. [#note1] content`
146
3. **Classification**:
147
- Regular sidenotes: Show with number
148
- Marginnotes: Content starts with `{-}`, no number shown
149
4. **Positioning**: Places notes adjacent to references in document structure
150
5. **Nested Handling**: Handles footnotes inside containers (admonitions, etc.)
151
152
### HTML Output Structure
153
154
#### Sidenote Output
155
```html
156
<label for='sidenote-role-1' class='margin-toggle'>
157
<span class='sidenote'>
158
<sup>1</sup>
159
Sidenote content here
160
</span>
161
</label>
162
<input type='checkbox' id='sidenote-role-1' class='margin-toggle'>
163
```
164
165
#### Marginnote Output
166
```html
167
<label for='marginnote-role-1' class='margin-toggle marginnote-label'>
168
</label>
169
<input type='checkbox' id='marginnote-role-1' class='margin-toggle'>
170
```
171
172
### Configuration Options
173
174
```python
175
# Theme options affecting transform behavior
176
html_theme_options = {
177
"use_sidenotes": True, # Enable/disable the transform
178
}
179
```