additional_samples_with_very_long_name
Various examples of styling applied to Sphinx constructs. You can view the source of this page to see the specific reStructuredText used to create these examples.
Subpages
Suppages get bread crumbs when they are not at the top level.
Headings
This is a second level heading (h2).
Sub-Heading
This is a third level heading (h3).
Sub-Sub-Heading
This is a fourth level heading (h4).
Code
The theme uses pygments for inline code text and
multiline
code text
Here’s an included example with line numbers.
1"""OpenFF Sphinx theme."""
2
3import hashlib
4import inspect
5import os
6import sys
7from multiprocessing import Manager
8from pathlib import Path
9from typing import List, Optional
10from xml.etree import ElementTree
11
12import bs4
13import sass
14import slugify
15from bs4 import BeautifulSoup
16from css_html_js_minify.html_minifier import html_minify
17from sass import SassColor
18from sphinx.util import console, logging
19import sphinx
20
21from ._version import get_versions
22
23__version__ = get_versions()["version"]
24del get_versions
25
26ROOT_SUFFIX = "--page-root"
27
28
29def setup(app):
30 """Setup connects events to the sitemap builder"""
31 app.connect("builder-inited", register_template_functions)
32 app.connect("config-inited", set_default_settings)
33 app.connect("env-get-outdated", register_outdated)
34 app.connect("build-finished", postproc_html)
35 app.connect("build-finished", compile_css)
36 app.site_pages = []
37 app.add_html_theme(
38 "openff_sphinx_theme", os.path.join(html_theme_path()[0], "openff_sphinx_theme")
39 )
40 return {
41 "version": __version__,
42 "parallel_read_safe": True,
43 "parallel_write_safe": True,
44 }
45
46
47def set_default_settings(app, config):
48 if not config.html_sidebars:
49 config.html_sidebars["**"] = [
50 "globaltoc.html",
51 "localtoc.html",
52 "searchbox.html",
53 ]
54 if config.html_permalinks_icon == "¶":
55 config.html_permalinks_icon = "<i class='fas fa-link'></i>"
56
57
58def compile_css(app, exception):
59 """Compile Bulma SASS into CSS"""
60 if exception is not None:
61 return
62
63 theme_path = Path(html_theme_path()[0]) / "openff_sphinx_theme"
64 src = theme_path / "sass/site.sass"
65 dest = Path(app.outdir) / "_static/site.css"
66
67 if not dest.parent.exists():
68 return
69
70 accent_color = app.config["html_theme_options"].get(
71 "color_accent", "openff-toolkit-blue"
72 )
73 accent_color = {
74 "openff-blue": (1, 84, 128),
75 "openff-toolkit-blue": (47, 158, 210),
76 "openff-dataset-yellow": (240, 133, 33),
77 "openff-evaluator-orange": (240, 58, 33),
78 "aquamarine": (44, 218, 157),
79 "lilac": (228, 183, 229),
80 "amaranth": (164, 14, 76),
81 "grape": (171, 146, 191),
82 "violet": (141, 107, 148),
83 "pink": (238, 66, 102),
84 "pale-green": (238, 66, 102),
85 "green": (4, 231, 98),
86 "crimson": (214, 40, 57),
87 "eggplant": (117, 79, 91),
88 "turquoise": (45, 225, 194),
89 }.get(accent_color, accent_color)
90
91 if app.config["html_theme_options"].get("css_minify", False):
92 output_style = "compressed"
93 source_comments = False
94 else:
95 output_style = "expanded"
96 source_comments = True
97
98 css = sass.compile(
99 filename=str(src),
100 output_style=output_style,
101 custom_functions={"accent_color": lambda: SassColor(*accent_color, 1)},
102 )
103
104 print(f"Writing compiled SASS to {console.colorize('blue', str(dest))}")
105
106 with open(dest, "w") as f:
107 print(css, file=f)
108
109
110def register_outdated(app, env, added, changed, removed):
111 if isinstance(app.builder, sphinx.builders.html.StandaloneHTMLBuilder):
112 env.openff_docs_to_postproc = added | changed
113 else:
114 env.openff_docs_to_postproc = None
115 return ()
116
117
118def postproc_html(app, exception):
119 """Prettify or minify the HTML, as well as wrap tables with .table-container"""
120 if exception is not None:
121 return
122
123 target_files = app.env.openff_docs_to_postproc
124
125 if target_files is None:
126 return
127
128 outdir = Path(app.outdir)
129
130 minify = app.config["html_theme_options"].get("html_minify", False)
131 prettify = app.config["html_theme_options"].get("html_prettify", False)
132 last = -1
133 npages = len(target_files)
134 print(f"Post-processing {npages} HTML files")
135
136 # TODO: Consider using parallel execution
137 for i, doc in enumerate(target_files):
138 try:
139 page = outdir / app.builder.get_target_uri(doc)
140 except sphinx.errors.NoUri as e:
141 print(doc, "has no URI; skipping")
142 continue
143
144 if int(100 * (i / npages)) - last >= 25:
145 last = int(100 * (i / npages))
146 color_page = console.colorize("blue", str(page))
147 msg = f"Post-processing files... [{last}%] {color_page}"
148 print("\033[K", msg, sep="", end="\r")
149
150 if not page.exists():
151 print(page, "does not exist; skipping")
152 continue
153
154 with open(page, "r", encoding="utf-8") as content:
155 soup = BeautifulSoup(content, "lxml")
156 for table in soup.find_all("table"):
157 if "table-container" not in table.parent.get("class", ()):
158 table.wrap(soup.new_tag("div", **{"class": "table-container"}))
159
160 if minify:
161 html = html_minify(str(soup))
162 elif prettify:
163 html = soup.prettify()
164 else:
165 html = str(soup)
166
167 with open(page, "w", encoding="utf-8") as content:
168 content.write(html)
169
170 msg = f"Post-processing files... [100%]"
171 sys.stdout.write("\033[K" + msg + "\r")
172
173
174def register_template_functions(app):
175 config = app.config
176 config.html_context = {**get_html_context(), **config.html_context}
177
178
179def html_theme_path():
180 return [os.path.dirname(os.path.abspath(__file__))]
181
182
183def ul_to_list(node: bs4.element.Tag, fix_root: bool, page_name: str) -> List[dict]:
184 out = []
185 for child in node.find_all("li", recursive=False):
186 if callable(child.isspace) and child.isspace():
187 continue
188 formatted = {}
189 if child.a is not None:
190 formatted["href"] = child.a["href"]
191 formatted["contents"] = "".join(map(str, child.a.contents))
192 if fix_root and formatted["href"] == "#" and child.a.contents:
193 slug = slugify.slugify(page_name) + ROOT_SUFFIX
194 formatted["href"] = "#" + slug
195 formatted["current"] = "current" in child.a.get("class", [])
196 if child.ul is not None:
197 formatted["children"] = ul_to_list(child.ul, fix_root, page_name)
198 else:
199 formatted["children"] = []
200 out.append(formatted)
201 return out
202
203
204def derender_toc(
205 toc_text, fix_root=True, page_name: str = "md-page-root--link"
206) -> List[dict]:
207 nodes = []
208 try:
209 toc = BeautifulSoup(toc_text, features="html.parser")
210 for child in toc.children:
211 if callable(child.isspace) and child.isspace():
212 continue
213 if child.name == "p":
214 nodes.append({"caption": "".join(map(str, child.contents))})
215 elif child.name == "ul":
216 nodes.extend(ul_to_list(child, fix_root, page_name))
217 else:
218 raise NotImplementedError
219 except Exception as exc:
220 logger = logging.getLogger(__name__)
221 logger.warning(
222 "Failed to process toctree_text\n" + str(exc) + "\n" + str(toc_text)
223 )
224
225 return nodes
226
227
228# These final lines exist to give sphinx a stable str representation of
229# this function across runs, and to ensure that the str changes
230# if the source does.
231derender_toc_src = inspect.getsource(derender_toc)
232derender_toc_hash = hashlib.sha512(derender_toc_src.encode()).hexdigest()
233
234
235class DerenderTocMeta(type):
236 def __repr__(self):
237 return f"derender_toc, hash: {derender_toc_hash}"
238
239 def __str__(self):
240 return f"derender_toc, hash: {derender_toc_hash}"
241
242
243class DerenderToc(object, metaclass=DerenderTocMeta):
244 def __new__(cls, *args, **kwargs):
245 return derender_toc(*args, **kwargs)
246
247
248def get_html_context():
249 return {"derender_toc": DerenderToc}
It also works with existing Sphinx highlighting:
<html>
<body>Hello World</body>
</html>
def hello():
"""Greet."""
return "Hello World"
/**
* Greet.
*/
function hello(): {
return "Hello World";
}
Admonitions
The theme uses the admonition classes for the standard Sphinx admonitions.
Warning
Warning
This is a warning.
Attention
Attention
Do I have your attention?
Caution
Caution
Use caution!
Danger
Danger
This is danger-ous.
Error
Error
You have made a grave error.
Hint
Hint
Can you take a hint?
Important
Important
It is important to correctly use admonitions.
Note
Note
This is a note.
Tip
Tip
Please tip your waiter.
Custom Admonitions
An admonition of my own making
You can create your own admonitions with the accent color.
Example
But lots of custom admonition styles are also defined.
Quote
The needs of the many outweigh the needs of the few
Bug
Bugs weren’t always a metaphor
Success
Woohoo!
Footnotes
I have footnoted a first item 1 and second item 2. This also references the second item 2.
Footnotes
Icons
Font Awesome and Academicons are both available:
<i class="fa fa-camera-retro fa-lg"></i>
<i class="fa fa-camera-retro fa-2x"></i>
<i class="fa fa-camera-retro fa-3x"></i>
<i class="fa fa-camera-retro fa-4x"></i>
<i class="fa fa-camera-retro fa-5x"></i>
<i class="ai ai-google-scholar-square ai-3x"></i>
<i class="ai ai-zenodo ai-3x" style="color: red"></i>
Tables
Here are some examples of Sphinx tables.
Grid
A grid table:
Header1 |
Header2 |
Header3 |
Header4 |
|---|---|---|---|
row1, cell1 |
cell2 |
cell3 |
cell4 |
row2 … |
… |
… |
|
… |
… |
… |
Simple
A simple table:
H1 |
H2 |
H3 |
|---|---|---|
cell1 |
cell2 |
cell3 |
… |
… |
… |
… |
… |
… |
List Tables
Alignment
Code Documentation
An example Python function.
- format_exception(etype, value, tb[, limit=None])
Format the exception with a traceback.
- Parameters
etype – exception type
value – exception value
tb – traceback object
limit (integer or None) – maximum number of stack frames to show
- Return type
list of strings
An example JavaScript function.
Glossaries
- environment
A structure where information about all documents under the root is saved, and used for cross-referencing. The environment is pickled after the parsing stage, so that successive runs only need to read and parse new and changed documents.
- source directory
The directory which, including its subdirectories, contains all source files for one Sphinx project.
Math
Production Lists
try_stmt ::= try1_stmt | try2_stmt try1_stmt ::= "try" ":"suite("except" [expression[","target]] ":"suite)+ ["else" ":"suite] ["finally" ":"suite] try2_stmt ::= "try" ":"suite"finally" ":"suite