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

1

My first footnote.

2(1,2)

My second footnote.

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

A List Table

Column 1

Column 2

Item 1

Item 2

Alignment

Center Aligned

Column 1

Column 2

Item 1

Item 2

Right Aligned

Treat

Quantity

Albatross

2.99

Crunchy Frog

1.49

Gannet Ripple

1.99

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.

class MyAnimal(name[, age])
Arguments
  • name (string()) – The name of the animal

  • age (number()) – an optional age for the animal

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

\[ \begin{align}\begin{aligned}(a + b)^2 = a^2 + 2ab + b^2\\(a - b)^2 = a^2 - 2ab + b^2\end{aligned}\end{align} \]
\[\begin{split}(a + b)^2 &= (a + b)(a + b) \\ &= a^2 + 2ab + b^2\end{split}\]
\begin{eqnarray} y & = & ax^2 + bx + c \\ f(x) & = & x^2 + 2xy + y^2 \end{eqnarray}

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