HTML theme development

Added in version 0.6.

备注

本文档提供有关创建您自己的主题的信息。如果您只想使用预先存在的HTML主题,请参阅:doc:/usage/themming

Sphinx支持通过*themes*更改其HTML输出的外观。主题是HTML模板、样式表和其他静态文件的集合。此外,它还有一个配置文件,指定从哪个主题继承,使用哪种突出显示样式,以及自定义主题外观的选项。

主题是指不受项目影响的,因此它们可以用于不同的项目而无需更改。

备注

See Sphinx Extensions API for more information that may be helpful in developing themes.

创建主题

主题采用目录或zipfile(其名称为主题名称)的形式,包含以下内容:

  • Either a theme.toml file (preferred) or a theme.conf file.

  • HTML模板,如果需要。

  • “static/`”目录,其中包含将在生成时复制到输出静态目录的任何静态文件。这些可以是图像、样式、脚本文件。

Theme configuration (theme.toml)

The theme.toml file is a TOML document, containing two tables: [theme] and [options].

The [theme] table defines the theme’s settings:

  • inherit (string): The name of the base theme from which to inherit settings, options, templates, and static files. All static files from theme ‘ancestors’ will be used. The theme will use all options defined in inherited themes. Finally, inherited themes will be used to locate missing templates (for example, if "basic" is used as the base theme, most templates will already be defined).

    If set to "none", the theme will not inherit from any other theme. Inheritance is recursive, forming a chain of inherited themes (e.g. default -> classic -> basic -> none).

  • stylesheets (list of strings): A list of CSS filenames which will be included in generated HTML header. Setting the html_style config value will override this setting.

    Other mechanisms for including multiple stylesheets include @import in CSS or using a custom HTML template with appropriate <link rel="stylesheet"> tags.

  • sidebars (list of strings): A list of sidebar templates. This can be overridden by the user via the html_sidebars config value.

  • pygments_style (table): A TOML table defining the names of Pygments styles to use for highlighting syntax. The table has two recognised keys: default and dark. The style defined in the dark key will be used when the CSS media query (prefers-color-scheme: dark) evaluates to true.

    [theme.pygments_style.default] can be overridden by the user via the pygments_style config value.

The [options] table defines the options for the theme. It is structured such that each key-value pair corresponds to a variable name and the corresponding default value. These options can be overridden by the user in html_theme_options and are accessible from all templates as theme_<name>.

Added in version 7.3: theme.toml support.

Exemplar theme.toml file:

[theme]
inherit = "basic"
stylesheets = [
    "main-CSS-stylesheet.css",
]
sidebars = [
    "localtoc.html",
    "relations.html",
    "sourcelink.html",
    "searchbox.html",
]
# Style names from https://pygments.org/styles/
pygments_style = { default = "style_name", dark = "dark_style" }

[options]
variable = "default value"

Theme configuration (theme.conf)

The theme.conf file is in INI format [1] (readable by the standard Python configparser module) and has the following structure:

[theme]
inherit = base theme
stylesheet = main CSS name
pygments_style = stylename
sidebars = localtoc.html, relations.html, sourcelink.html, searchbox.html

[options]
variable = default value
  • **inherit**设置提供“基本主题”或“无”的名称。基本主题将用于查找缺少的模板(如果大多数主题使用“basic”作为基本主题,则大多数主题不必提供大多数模板),它的选项将被继承,并且它的所有静态文件也将被使用。如果您还想继承样式表,可以通过CSS’`@import``将其包含在自己的样式表中。

  • The stylesheet setting gives a list of CSS filenames separated commas which will be referenced in the HTML header. You can also use CSS’ @import technique to include one from the other, or use a custom HTML template that adds <link rel="stylesheet"> tags as necessary. Setting the html_style config value will override this setting.

  • **pygmentsustyle**设置提供用于突出显示的pygments样式的名称。用户可以在:confval:`pygmentsustyle`配置值中重写此设置。

  • The pygments_dark_style setting gives the name of a Pygments style to use for highlighting when the CSS media query (prefers-color-scheme: dark) evaluates to true. It is injected into the page using add_css_file().

  • **sidebars**设置提供了用逗号分隔的侧边栏模板列表,用于构造提要栏。用户可以在:confval:`html_sidebars`配置值中覆盖此值。

  • **options**部分包含变量名和默认值对。用户可以在以下位置重写这些选项:confval:html_theme_options`并且可以从所有模板中以“theme_1”的形式访问这些选项。

Added in version 1.7: 边栏设置

在 5.1 版本发生变更: The stylesheet setting accepts multiple CSS filenames

Convert theme.conf to theme.toml

INI-style theme configuration files (theme.conf) can be converted to TOML via a helper programme distributed with Sphinx. This is intended for one-time use, and may be removed without notice in a future version of Sphinx.

$ python -m sphinx.theming conf_to_toml [THEME DIRECTORY PATH]

The required argument is a path to a directory containing a theme.conf file. The programme will write a theme.toml file in the same directory, and will not modify the original theme.conf file.

Added in version 7.3.

将主题作为Python包分发

As a way to distribute your theme, you can use a Python package. This makes it easier for users to set up your theme.

To distribute your theme as a Python package, please define an entry point called sphinx.html_themes in your pyproject.toml file, and write a setup() function to register your theme using the add_html_theme() API:

# pyproject.toml

[project.entry-points."sphinx.html_themes"]
name_of_theme = "your_theme_package"
# your_theme_package.py
from os import path

def setup(app):
    app.add_html_theme('name_of_theme', path.abspath(path.dirname(__file__)))

如果您的主题包包含两个或多个主题,请调用“add_html_theme()`”两次或更多。

Added in version 1.2: “sphinx_themes”入口点功能。

自 1.6 版本弃用: ``sphinx_themes“入口点”已被弃用。

Added in version 1.6: sphinx.html_themes 进入点特征。

模板

如果您想编写自己的模板,:doc:`guide to templating 1`很有帮助。重要的是要记住Sphinx搜索模板的顺序:

  • 首先,在用户的“templates_path”目录中。

  • 然后,在选定的主题中。

  • 然后,在它的基主题中,它的基主题,等等。

When extending a template in the base theme with the same name, use the theme name as an explicit directory: {% extends "basic/layout.html" %}. From a user templates_path template, you can still use the “exclamation mark” syntax as described in the templating document.

静态模板

因为主题选项是为了让用户更容易地配置一个主题,而不必编写自定义样式表,所以必须能够模板化静态文件和HTML文件。因此,Sphinx支持所谓的“静态模板”,如下所示:

If the name of a file in the static/ directory of a theme (or in the user’s static path) ends with .jinja or _t, it will be processed by the template engine. The suffix will be removed from the final file name.

For example, a theme with a static/theme_styles.css.jinja file could use templating to put options into the stylesheet. When a documentation project is built with that theme, the output directory will contain a _static/theme_styles.css file where all template tags have been processed.

在 7.4 版本发生变更:

The preferred suffix for static templates is now .jinja, in line with the Jinja project’s recommended file extension.

The _t file suffix for static templates is now considered ‘legacy’, and support may eventually be removed.

If a static template with either a _t suffix or a .jinja suffix is detected, it will be processed by the template engine, with the suffix removed from the final file name.

Use custom page metadata in HTML templates

Any key / value pairs in field lists that are placed before the page’s title will be available to the Jinja template when building the page within the meta attribute. For example, if a page had the following text before its first title:

:mykey: My value

My first title
--------------

Then it could be accessed within a Jinja template like so:

{%- if meta is mapping %}
    {{ meta.get("mykey") }}
{%- endif %}

Note the check that meta is a dictionary (“mapping” in Jinja terminology) to ensure that using it in this way is valid.

Defining custom template functions

Sometimes it is useful to define your own function in Python that you wish to then use in a template. For example, if you’d like to insert a template value with logic that depends on the user’s configuration in the project, or if you’d like to include non-trivial checks and provide friendly error messages for incorrect configuration in the template.

To define your own template function, you’ll need to define two functions inside your module:

  • A page context event handler (or registration) function. This is connected to the Sphinx application via an event callback.

  • A template function that you will use in your Jinja template.

First, define the registration function, which accepts the arguments for html-page-context.

Within the registration function, define the template function that you’d like to use within Jinja. The template function should return a string or Python objects (lists, dictionaries) with strings inside that Jinja uses in the templating process

备注

The template function will have access to all of the variables that are passed to the registration function.

At the end of the registration function, add the template function to the Sphinx application’s context with context['template_func'] = template_func.

Finally, in your extension’s setup() function, add your registration function as a callback for html-page-context.

# The registration function
 def setup_my_func(app, pagename, templatename, context, doctree):
     # The template function
     def my_func(mystring):
         return "Your string is %s" % mystring
     # Add it to the page's context
     context['my_func'] = my_func

 # Your extension's setup function
 def setup(app):
     app.connect("html-page-context", setup_my_func)

Now, you will have access to this function in jinja like so:

<div>
{{ my_func("some string") }}
</div>

Add your own static files to the build assets

By default, Sphinx copies static files on the static/ directory of the template directory. However, if your package needs to place static files outside of the static/ directory for some reasons, you need to copy them to the _static/ directory of HTML outputs manually at the build via an event hook. Here is an example of code to accomplish this:

from os import path
from sphinx.util.fileutil import copy_asset_file

def copy_custom_files(app, exc):
    if app.builder.format == 'html' and not exc:
        staticdir = path.join(app.builder.outdir, '_static')
        copy_asset_file('path/to/myextension/_static/myjsfile.js', staticdir)

def setup(app):
    app.connect('build-finished', copy_custom_files)

Inject JavaScript based on user configuration

If your extension makes use of JavaScript, it can be useful to allow users to control its behavior using their Sphinx configuration. However, this can be difficult to do if your JavaScript comes in the form of a static library (which will not be built with Jinja).

There are two ways to inject variables into the JavaScript space based on user configuration.

First, you may append _t to the end of any static files included with your extension. This will cause Sphinx to process these files with the templating engine, allowing you to embed variables and control behavior.

For example, the following JavaScript structure:

mymodule/
├── _static
│   └── myjsfile.js_t
└── mymodule.py

Will result in the following static file placed in your HTML’s build output:

_build/
└── html
    └── _static
        └── myjsfile.js

See 静态模板 for more information.

Second, you may use the Sphinx.add_js_file() method without pointing it to a file. Normally, this method is used to insert a new JavaScript file into your site. However, if you do not pass a file path, but instead pass a string to the “body” argument, then this text will be inserted as JavaScript into your site’s head. This allows you to insert variables into your project’s JavaScript from Python.

For example, the following code will read in a user-configured value and then insert this value as a JavaScript variable, which your extension’s JavaScript code may use:

# This function reads in a variable and inserts it into JavaScript
def add_js_variable(app):
    # This is a configuration that you've specified for users in `conf.py`
    js_variable = app.config['my_javascript_variable']
    js_text = "var my_variable = '%s';" % js_variable
    app.add_js_file(None, body=js_text)
# We connect this function to the step after the builder is initialized
def setup(app):
    # Tell Sphinx about this configuration variable
    app.add_config_value('my_javascript_variable', 0, 'html')
    # Run the function after the builder is initialized
    app.connect('builder-inited', add_js_variable)

As a result, in your theme you can use code that depends on the presence of this variable. Users can control the variable’s value by defining it in their conf.py file.