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.

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={
102            "accent_color": lambda: SassColor(*accent_color, 1),
103            "hyphenate": lambda: app.config["html_theme_options"].get(
104                "html_hyphenate_and_justify", False
105            ),
106        },
107    )
108
109    print(f"Writing compiled SASS to {console.colorize('blue', str(dest))}")
110
111    with open(dest, "w") as f:
112        print(css, file=f)
113
114
115def register_outdated(app, env, added, changed, removed):
116    if isinstance(app.builder, sphinx.builders.html.StandaloneHTMLBuilder):
117        env.openff_docs_to_postproc = added | changed
118    else:
119        env.openff_docs_to_postproc = None
120    return ()
121
122
123def postproc_html(app, exception):
124    """Prettify or minify the HTML, as well as wrap tables with .table-container"""
125    if exception is not None:
126        return
127
128    target_files = app.env.openff_docs_to_postproc
129
130    if target_files is None:
131        return
132
133    outdir = Path(app.outdir)
134
135    minify = app.config["html_theme_options"].get("html_minify", False)
136    prettify = app.config["html_theme_options"].get("html_prettify", False)
137    last = -1
138    npages = len(target_files)
139    print(f"Post-processing {npages} HTML files")
140
141    # TODO: Consider using parallel execution
142    for i, doc in enumerate(target_files):
143        try:
144            page = outdir / app.builder.get_target_uri(doc)
145        except sphinx.errors.NoUri as e:
146            print(doc, "has no URI; skipping")
147            continue
148
149        if int(100 * (i / npages)) - last >= 25:
150            last = int(100 * (i / npages))
151            color_page = console.colorize("blue", str(page))
152            msg = f"Post-processing files... [{last}%] {color_page}"
153            print("\033[K", msg, sep="", end="\r")
154
155        if not page.exists():
156            print(page, "does not exist; skipping")
157            continue
158
159        with open(page, "r", encoding="utf-8") as content:
160            soup = BeautifulSoup(content, "lxml")
161            for table in soup.find_all("table"):
162                container_attributes = {
163                    "class": ["table-container"] + table.get("class", []),
164                }
165                if "table-container" not in table.parent.get("class", ()):
166                    table.wrap(soup.new_tag("div", **container_attributes))
167
168            if minify:
169                html = html_minify(str(soup))
170            elif prettify:
171                html = soup.prettify()
172            else:
173                html = str(soup)
174
175        with open(page, "w", encoding="utf-8") as content:
176            content.write(html)
177
178    msg = f"Post-processing files... [100%]"
179    sys.stdout.write("\033[K" + msg + "\r")
180
181
182def register_template_functions(app):
183    config = app.config
184    config.html_context = {**get_html_context(), **config.html_context}
185
186
187def html_theme_path():
188    return [os.path.dirname(os.path.abspath(__file__))]
189
190
191def ul_to_list(node: bs4.element.Tag, fix_root: bool, page_name: str) -> List[dict]:
192    out = []
193    for child in node.find_all("li", recursive=False):
194        if callable(child.isspace) and child.isspace():
195            continue
196        formatted = {}
197        if child.a is not None:
198            formatted["href"] = child.a["href"]
199            formatted["contents"] = "".join(map(str, child.a.contents))
200            if fix_root and formatted["href"] == "#" and child.a.contents:
201                slug = slugify.slugify(page_name) + ROOT_SUFFIX
202                formatted["href"] = "#" + slug
203            formatted["current"] = "current" in child.a.get("class", [])
204        if child.ul is not None:
205            formatted["children"] = ul_to_list(child.ul, fix_root, page_name)
206        else:
207            formatted["children"] = []
208        out.append(formatted)
209    return out
210
211
212def derender_toc(
213    toc_text, fix_root=True, page_name: str = "md-page-root--link"
214) -> List[dict]:
215    nodes = []
216    try:
217        toc = BeautifulSoup(toc_text, features="html.parser")
218        for child in toc.children:
219            if callable(child.isspace) and child.isspace():
220                continue
221            if child.name == "p":
222                nodes.append({"caption": "".join(map(str, child.contents))})
223            elif child.name == "ul":
224                nodes.extend(ul_to_list(child, fix_root, page_name))
225            else:
226                raise NotImplementedError
227    except Exception as exc:
228        logger = logging.getLogger(__name__)
229        logger.warning(
230            "Failed to process toctree_text\n" + str(exc) + "\n" + str(toc_text)
231        )
232
233    return nodes
234
235
236# These final lines exist to give sphinx a stable str representation of
237# this function across runs, and to ensure that the str changes
238# if the source does.
239derender_toc_src = inspect.getsource(derender_toc)
240derender_toc_hash = hashlib.sha512(derender_toc_src.encode()).hexdigest()
241
242
243class DerenderTocMeta(type):
244    def __repr__(self):
245        return f"derender_toc, hash: {derender_toc_hash}"
246
247    def __str__(self):
248        return f"derender_toc, hash: {derender_toc_hash}"
249
250
251class DerenderToc(object, metaclass=DerenderTocMeta):
252    def __new__(cls, *args, **kwargs):
253        return derender_toc(*args, **kwargs)
254
255
256def get_html_context():
257    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.

Deprecated

Deprecated since version 999.999.999: This API point was deprecated.

Todo

Todo

This is something we are yet to do.

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!

Experimental

Experiments are the heart of science

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

A List Table

Column 1

Column 2

Item 1

Item 2

50% width list table

One fifth width column

Four fifths width column

Item 1

Item 2

Alignment

Left Aligned

Column 1

Column 2

Item 1

Item 2

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