HTML 主题开发

版本 0.6 中添加。

注意

本文档提供了有关创建您自己的主题的信息。如果您只是想使用现有的 HTML 主题,请参考 HTML 主题.

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

主题旨在与项目无关,因此可以在不同的项目中使用而无需更改。

注意

有关开发主题可能会有所帮助的更多信息,请参见 Sphinx API.

创建主题

主题的形式可以是目录或压缩文件(其名称为主题名称),其中包含以下内容

  • theme.toml 文件(首选)或 theme.conf 文件。

  • HTML 模板(如果需要)。

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

主题配置 (theme.toml)

theme.toml 文件是一个 TOML 文档,包含两个表:[theme][options]

[theme] 表定义了主题的设置

  • inherit (字符串): 要从中继承设置、选项、模板和静态文件的基主题的名称。将使用来自主题“祖先”的所有静态文件。该主题将使用在继承主题中定义的所有选项。最后,将使用继承主题来定位丢失的模板(例如,如果 "basic" 用作基主题,大多数模板将已定义)。

    如果设置为 "none",则该主题将不会从任何其他主题继承。继承是递归的,形成一个继承主题链(例如 default -> classic -> basic -> none)。

  • stylesheets (字符串列表): 将包含在生成的 HTML 标头中的 CSS 文件名列表。设置 html_style 配置值将覆盖此设置。

    包含多个样式表的其他机制包括 CSS 中的 @import 或使用具有适当 <link rel="stylesheet"> 标签的自定义 HTML 模板。

  • sidebars (字符串列表): 侧边栏模板列表。这可以通过用户通过 html_sidebars 配置值来覆盖。

  • pygments_style (表): 定义用于突出显示语法的 Pygments 样式名称的 TOML 表。该表有两个认可的键:defaultdark。当 CSS 媒体查询 (prefers-color-scheme: dark) 评估为真时,将在 dark 键中定义的样式将被使用。

    [theme.pygments_style.default] 可以通过用户通过 pygments_style 配置值来覆盖。

[options] 表定义了主题的选项。它的结构使得每个键值对对应于一个变量名称和相应的默认值。这些选项可以通过用户在 html_theme_options 中覆盖,并且可以通过所有模板作为 theme_<name> 访问。

版本 7.3 中添加: theme.toml 支持。

示例 theme.toml 文件

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

[options]
variable = "default value"

主题配置 (theme.conf)

theme.conf 文件采用 INI 格式 [1](可由标准 Python configparser 模块读取)并具有以下结构

[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 设置给出一个“基主题”的名称,或者 none。基主题将用于定位丢失的模板(如果大多数主题使用 basic 作为基主题,则它们将不必提供大多数模板),它的选项将被继承,并且它的所有静态文件也将被使用。如果您还想继承样式表,请通过 CSS’ @import 在您自己的样式表中包含它。

  • stylesheet 设置给出一个由逗号分隔的 CSS 文件名列表,这些文件名将在 HTML 标头中引用。您还可以使用 CSS’ @import 技术从一个文件中包含另一个文件,或者使用添加 <link rel="stylesheet"> 标签的自定义 HTML 模板。设置 html_style 配置值将覆盖此设置。

  • pygments_style 设置给出了用于突出显示的 Pygments 样式的名称。这可以通过用户在 pygments_style 配置值中覆盖。

  • pygments_dark_style 设置给出了用于突出显示的 Pygments 样式的名称,当 CSS 媒体查询 (prefers-color-scheme: dark) 评估为真时。它使用 add_css_file() 注入页面。

  • sidebars 设置给出了用于构建侧边栏的逗号分隔的侧边栏模板列表。这可以通过用户在 html_sidebars 配置值中覆盖。

  • options 部分包含变量名称和默认值的配对。这些选项可以通过用户在 html_theme_options 中覆盖,并且可以通过所有模板作为 theme_<name> 访问。

版本 1.7 中添加: 侧边栏设置

版本 5.1 中更改: stylesheet 设置接受多个 CSS 文件名

theme.conf 转换为 theme.toml

INI 样式的主题配置文件 (theme.conf) 可以通过与 Sphinx 一起发布的辅助程序转换为 TOML。这旨在用于一次性使用,并且可能会在 Sphinx 的未来版本中被删除而无需通知。

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

所需参数是包含 theme.conf 文件的目录的路径。该程序将在同一目录中写入一个 theme.toml 文件,并且不会修改原始 theme.conf 文件。

版本 7.3 中添加。

将您的主题作为 Python 包发布

作为发布主题的一种方式,您可以使用 Python 包。这使得用户更容易设置您的主题。

要将您的主题作为 Python 包发布,请在您的 pyproject.toml 文件中定义一个名为 sphinx.html_themes 的入口点,并编写一个 setup() 函数,使用 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() 两次或多次。

新增于版本 1.2: ‘sphinx_themes’ 入口点功能。

已于版本 1.6 中弃用: sphinx_themes 入口点已弃用。

新增于版本 1.6: sphinx.html_themes 入口点功能。

使用 CSS 进行样式设置

stylesheets 设置可用于向主题添加自定义 CSS 文件。

警告

HTML 元素的结构及其类目前并非定义明确的公共 API。请通过检查生成的 HTML 页面来推断它们。虽然我们无法保证完全稳定,但它们往往相当稳定。

按类别对搜索结果条目进行样式设置

新增于版本 8.0。

搜索结果项包含指示搜索词被发现的上下文的类。您可以使用以下 CSS 选择器:

  • ul.search li.context-index: 用于索引中的结果,例如词汇表

  • ul.search li.context-object: 用于源代码中的结果,例如 Python 函数定义

  • ul.search li.context-title: 用于在章节标题中找到的结果

  • ul.search li.context-text: 用于在文档文本中其他任何位置找到的结果

作为其他主题继承的基础,basic 主题有意地保持最简,并且不定义使用这些内容的 CSS 规则。鼓励派生主题根据需要使用这些选择器。例如,以下样式表将上下文图标添加到搜索结果列表

ul.search {
    padding-left: 30px;
}
ul.search li {
    padding: 5px 0 5px 10px;
    list-style-type: "\25A1";  /* Unicode: White Square */
}
ul.search li.context-index {
    list-style-type: "\1F4D1";  /* Unicode: Bookmark Tabs */
}
ul.search li.context-object {
    list-style-type: "\1F4E6";  /* Unicode: Package */
}
ul.search li.context-title {
    list-style-type: "\1F4C4";  /* Unicode: Page Facing Up */
}
ul.search li.context-text {
    list-style-type: "\1F4C4";  /* Unicode: Page Facing Up */
}

模板

如果您想编写自己的模板,模板指南 会很有帮助。需要牢记的是 Sphinx 搜索模板的顺序:

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

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

  • 然后,在其基础主题、其基础主题的基础主题等中。

当使用相同名称扩展基础主题中的模板时,请使用主题名称作为显式目录:{% extends "basic/layout.html" %}。从用户 templates_path 模板中,您仍然可以使用“感叹号”语法,如模板文档中所述

静态模板

由于主题选项旨在让用户更轻松地配置主题,而无需编写自定义样式表,因此有必要能够像 HTML 文件一样对静态文件进行模板化。因此,Sphinx 支持所谓的“静态模板”,如下所示:

如果主题的 static/ 目录(或用户静态路径)中的文件名以 .jinja_t 结尾,它将由模板引擎处理。该后缀将从最终文件名中删除。

例如,具有 static/theme_styles.css.jinja 文件的主题可以使用模板将选项放入样式表中。当使用该主题构建文档项目时,输出目录将包含一个 _static/theme_styles.css 文件,其中所有模板标签都已处理。

更改于版本 7.4

静态模板的首选后缀现在是 .jinja,与 Jinja 项目的推荐文件扩展名一致。

静态模板的 _t 文件后缀现在被认为是“旧版”,并且支持可能最终会被删除。

如果检测到具有 _t 后缀或 .jinja 后缀的静态模板,它将由模板引擎处理,并从最终文件名中删除后缀。

在 HTML 模板中使用自定义页面元数据

在页面标题之前放置的任何字段列表中的键/值对都将在页面内部构建时对 Jinja 模板可用 meta 属性。例如,如果一个页面在第一个标题之前有以下文本:

:mykey: My value

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

然后它可以在 Jinja 模板中像这样访问:

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

注意,检查 meta 是一个字典(在 Jinja 术语中是“映射”),以确保以这种方式使用它是有效的。

定义自定义模板函数

有时在 Python 中定义您自己的函数很有用,然后您希望在模板中使用它。例如,如果您想插入一个取决于项目中用户配置的逻辑的模板值,或者如果您想包含非平凡的检查并为模板中的错误配置提供友好的错误消息。

要定义您自己的模板函数,您需要在模块内定义两个函数:

  • 一个页面上下文事件处理程序(或注册)函数。它通过事件回调连接到 Sphinx 应用程序。

  • 一个模板函数,您将在 Jinja 模板中使用它。

首先,定义注册函数,它接受 html-page-context 的参数。

在注册函数内,定义您希望在 Jinja 中使用的模板函数。模板函数应返回一个字符串或包含字符串的 Python 对象(列表、字典),这些字符串在模板化过程中被 Jinja 使用

注意

模板函数将能够访问传递给注册函数的所有变量。

在注册函数的末尾,使用 context['template_func'] = template_func 将模板函数添加到 Sphinx 应用程序的上下文中。

最后,在扩展的 setup() 函数中,将您的注册函数添加为 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)

现在,您可以在 jinja 中访问此函数,如下所示:

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

将您自己的静态文件添加到构建资产

默认情况下,Sphinx 将模板目录的 static/ 目录中的静态文件复制到 _static/ 目录中。但是,如果您的包出于某些原因需要将静态文件放置在 static/ 目录之外,则需要通过事件钩子手动将它们复制到 HTML 输出的 _static/ 目录中。以下是一个执行此操作的代码示例:

import shutil

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

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

根据用户配置注入 JavaScript

如果您的扩展使用 JavaScript,允许用户使用他们的 Sphinx 配置来控制它的行为会很有用。但是,如果您的 JavaScript 采用静态库的形式(不会使用 Jinja 构建),这可能很难做到。

有两种方法可以根据用户配置将变量注入 JavaScript 空间。

首先,您可以将 _t 附加到扩展中包含的任何静态文件的末尾。这将导致 Sphinx 使用模板引擎处理这些文件,从而允许您嵌入变量和控制行为。

例如,以下 JavaScript 结构:

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

将导致在 HTML 的构建输出中放置以下静态文件:

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

有关详细信息,请参见静态模板

其次,您可以使用 Sphinx.add_js_file() 方法,而不指向文件。通常,此方法用于将新的 JavaScript 文件插入您的站点。但是,如果您传递文件路径,而是将字符串传递给“body”参数,那么此文本将作为 JavaScript 插入到您站点的头部。这允许您从 Python 将变量插入到项目的 JavaScript 中。

例如,以下代码将读取用户配置的值,然后将此值作为 JavaScript 变量插入,您的扩展的 JavaScript 代码可以使用它:

# 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)

因此,在您的主题中,您可以使用依赖于此变量存在的代码。用户可以通过在 conf.py 文件中定义它来控制变量的值。