HTML 主题开发

0.6 新版功能.

备注

本文档提供了关于创建你自己的主题的信息。如果你只是想使用一个预先存在的 HTML 主题,请参考 HTML 主题化

Sphinx 支持通过 themes 改变其 HTML 输出的外观。主题是 HTML 模板、样式表和其他静态文件的集合。此外,它有配置文件,指定从哪个主题继承,使用哪种高亮风格,以及有哪些选项可以定制主题的外观和感觉。

主题是为了不受项目影响,所以它们可以用于不同的项目而不需要改变。

备注

参见 为 Sphinx 开发插件,了解更多信息,可能对开发主题有帮助。

创建主题

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

  • theme.conf 文件。

  • 如果需要,可提供 HTML 模板。

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

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 设置给出了 “base theme” 的名称,或者是 none。基础主题将被用来定位缺少的模板(如果使用 basic 作为基础主题,大多数主题将不必提供大多数模板),它的选项将被继承,它的所有静态文件也将被使用。如果你想同时继承样式表,通过 CSS 的 @import 在你自己的样式表中包含它。

  • stylesheet 设置给出了 CSS 文件的名称,它将在 HTML header 中被引用。如果你需要一个以上的 CSS 文件,可以通过 CSS 的 @import 从其他文件中包含一个,或者使用自定义的 HTML 模板,根据需要添加 <link rel="stylesheet"> 标签。设置 html_style 配置值将覆盖此设置。

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

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

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

  • options 部分包含一对变量名称和默认值。这些选项可以由用户在 html_theme_options 中重写,并且可以从所有模板中以 theme_<name> 的形式访问。

1.7 新版功能: 侧边栏设定

将你的主题作为一个Python包发布

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

要将你的主题作为 Python包 发布,请在你的 setup.py 文件中定义名为 sphinx.html_themes 的切入点,并在其中编写 setup() 函数,使用 add_html_theme() API 注册你的主题

# 'setup.py'
setup(
    ...
    entry_points = {
        'sphinx.html_themes': [
            'name_of_theme = your_package',
        ]
    },
    ...
)

# 'your_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 切入点特征

模板化

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

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

  • 接着,在选定的主题中。

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

当扩展基本主题中的同名模板时,使用主题名称作为明确的目录: {% extends "basic/layout.html" %}。从用户的 templates_path 模板,你仍然可以使用 “exclamation mark” 语法,如模板文件中所述。

静态模板

由于主题选项是为了让用户更容易地配置一个主题,而不需要写一个自定义的样式表,所以有必要能够对静态文件和 HTML 文件进行模板化。因此,Sphinx 支持所谓的 “静态模板”,像这样:

如果主题的 static/ 目录中的文件名(或者用户的静态路径中的文件名)以 _t 结尾,它将被模板引擎处理。_t 将从最终的文件名中留下。例如,classic 主题有文件 static/classic.css_t,它使用模板将颜色选项放入样式表。当用 classic 主题构建文档时,输出目录将包含 _static/classic.css 文件,其中所有模板标签都已被处理。

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

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

: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>

将你自己的静态文件添加到构建 assets 中

如果你将自己的构建 assets 用插件打包(例如,CSS 或 JavaScript 文件),你需要确保它们被放置在 HTML 输出的 _static/ 文件夹中。要做到这一点,你可以在构建时将它们直接复制到构建的 _static/ 文件夹中,一般是通过一个事件钩。下面是一些实现这一目的的示例代码:

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('builder-inited', copy_custom_files)

根据用户配置注入 JavaScript

如果你的扩展使用了 JavaScript,允许用户使用他们的 Sphinx 配置来控制其行为是很有用的。

有两种方法可以根据用户的配置向 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')
    # Run the function after the builder is initialized
    app.connect('builder-inited', add_js_variable)

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

1

它不是可执行的 Python 文件,与 conf.py 相反,因为如果主题被共享,这将构成不必要的安全风险。