開發佈景主題

建立並散佈自訂佈景主題的指南。


註事項

若您正在尋找現有的第三方佈景主題,它們會列在 社群 Wiki 頁面和 MkDocs 專案目錄 中。如果您想分享自己建立的佈景主題,應該將它列在其中。

在建立新的佈景主題時,您可以依照本指南中的步驟從頭開始建立一個,或下載 mkdocs-basic-theme 當成一個基礎且完整的佈景主題,它具備所有必要的樣板程式碼。您可以在 GitHub 上找到這個基本佈景主題。其程式碼中包含詳細註解,用於說明不同的功能和用法。

建立自訂佈景主題

自訂佈景主題所需要的最少檔案為 main.html Jinja2 範本 檔案,它放置在不是 docs_dir 子目錄的目錄中。在 mkdocs.yml 中,將 theme.custom_dir 選項設定為包含 main.html 的目錄路徑。路徑應該是相對於設定檔而言。例如,看看這個專案範例佈局

mkdocs.yml
docs/
    index.md
    about.md
custom_theme/
    main.html
    ...

... 您必須在 mkdocs.yml 中加入以下設定,才能使用自訂佈景主題目錄

theme:
  name: null
  custom_dir: 'custom_theme/'

註事項

通常,在建置自己的自訂佈景主題時,佈景主題.name 設定應該設定成 null。然而,若佈景主題.custom_dir 設定值與現有佈景主題一起使用,則可以利用佈景主題.custom_dir 取代內建佈景主題的特定部分。例如,如果您採用上述佈局並設定 name: "mkdocs",則 main.html 檔案中的佈景主題.custom_dir 會取代 mkdocs 佈景主題中同名的檔案,但 mkdocs 佈景主題的其他部分則保持不變。如果您想對現有佈景主題進行微調,這項功能便會相當有用。

如需更詳細的資訊,請參閱 自訂佈景主題

警告

mkdocs_theme.yml 檔案中設定的主題設定檔並不會由 theme.custom_dir 載入。當整體主題存在於 theme.custom_dir 中,且 theme.name 設定為 null 時,整體主題設定檔就必須在 mkdocs.yml 檔案中的主題設定選項中定義。

不過,如果將主題打包用於發行,且使用 theme.name 設定選項載入,那麼,主題會需要 mkdocs_theme.yml 檔案。

基本主題

最基本的 main.html 檔案如下所示

<!DOCTYPE html>
<html>
  <head>
    <title>{% if page.title %}{{ page.title }} - {% endif %}{{ config.site_name }}</title>
    {%- for path in config.extra_css %}
      <link href="{{ path | url }}" rel="stylesheet">
    {%- endfor %}
  </head>
  <body>
    {{ page.content }}

    {%- for script in config.extra_javascript %}
      {{ script | script_tag }}
    {%- endfor %}
  </body>
</html>

mkdocs.yml 中指定的每頁主體內容都會使用 {{ page.content }} 標籤插入。樣式表和腳本可透過與一般 HTML 檔案相同的方式帶入此主題。導覽列和目錄表也可透過 navtoc 物件自動產生並包含。如果您想撰寫自己的主題,建議從其中一個內建主題開始,並根據需要修改。

註事項

由於 MkDocs 使用Jinja作為其範本引擎,因此,你可以運用 Jinja 的所有強大功能,包括範本繼承。你可能會注意到 MkDocs 附帶的主題大量使用範本繼承和區塊,這樣,使用者就可以輕鬆地覆寫主題custom_dir中範本的小部分內容。因此,內建主題是在 base.html 檔案中實作,main.html 會延伸這個檔案。雖然不需要這麼做,但建議第三方範本作者遵循類似的模式,並可能會想要定義與內建主題中所用的區塊區塊相同的內容,以維持一致性。

從設定檔中挑選 CSS 和 JavaScript

Mkdocs 定義了最上層的extra_cssextra_javascript設定檔。這些檔案都是清單。

主題必須包含連結這些設定檔項目的 HTML,否則這些設定檔將無法運作。你可以在以上基本範例中看到建議用來呈現兩者的方法。

在 1.5 版中變更

config.extra_javascript 清單中的項目以前是單純的字串,但現在已變成具有以下欄位的物件:pathtypeasyncdefer

在該版本中,MkDocs 也加入了script_tag 濾鏡

過時的樣式
  {%- for path in extra_javascript %}
    <script src="{{ path }}"></script>
  {%- endfor %}

這個舊式範例甚至使用了過時的 top-level extra_javascript 清單。請務必永遠使用 config.extra_javascript 來取代。

因此,稍微現代一點的方式如下所示,但它仍然過時,因為它忽略了腳本的額外屬性

  {%- for path in config.extra_javascript %}
    <script src="{{ path | url }}"></script>
  {%- endfor %}
範例:**新樣式:**

  {%- for script in config.extra_javascript %}
    {{ script | script_tag }}
  {%- endfor %}

如果你希望能夠使用新的自訂內容,同時讓你的主題與舊版 Mkdocs 相容,請使用這個片段

向下相容的樣式
  {%- for script in config.extra_javascript %}
    {%- if script.path %}  {# Detected MkDocs 1.5+ which has `script.path` and `script_tag` #}
      {{ script | script_tag }}
    {%- else %}  {# Fallback - examine the file name directly #}
      <script src="{{ script | url }}"{% if script.endswith(".mjs") %} type="module"{% endif %}></script>
    {%- endif %}
  {%- endfor %}

主題檔案

主題會以某種特殊方式處理各種檔案。在建立網站時,其他任何檔案都會從主題目錄複製到 site_dir 中的相同路徑。例如,圖片和 CSS 檔案沒有特別意義,因此會原樣複製。不過,請注意,如果使用者在他們的 docs_dir 中提供具有相同路徑的檔案,那麼使用者的檔案將取代主題檔案。

範本檔案

任何副檔名為 .html 的檔案都被視為範本檔案,不會從主題目錄或任何子目錄複製。此外,在 static_templates 中列出的任何檔案都將視為範本,而不考慮其副檔名。

主題元資料檔

封裝主題所需的各種檔案也會被忽略。具體而言,就是 mkdocs_theme.yml 設定檔和任何 Python 檔案。

開頭為點的檔案

主題作者可以透過以點號開頭的檔案或目錄名稱,明確強制 MkDocs 忽略檔案。以下任何檔案都會被忽略

.ignored.txt
.ignored/file.txt
foo/.ignored.txt
foo/.ignored/file.txt

文件檔案

所有文件檔案都被忽略。具體而言,是指任何 Markdown 檔(使用 MkDocs 支援的任何副檔名)。此外,主題目錄中可能存在的任何 README 檔案也會被忽略。

範本變數

主題中的每個範本都是使用範本內容建立的。這些是可供主題使用的變數。內容會根據所建立的範本來而有所不同。目前,範本是使用全域內容或針對特定頁面的內容建立。全域內容用於不表示個別 Markdown 文件的 HTML 頁面,例如 404.html 頁面或 search.html。

全域內容

以下變數在任何範本上都可用於全域。

config

config 變數是從 mkdocs.yml 設定檔產生的 MkDocs 設定物件的實例。雖然你可以使用任何設定選項,但有些常用的選項包括

nav 變數用於建立文件的導覽。nav 物件是由 導覽物件 所組成的可迭代,正如 nav 設定所定義的。

除了 導覽物件 的可迭代之外,nav 物件還包含以下屬性

homepage: Page | None instance-attribute

page 物件,用於網站首頁。

pages: list[Page] instance-attribute

包含於導覽中的所有 page 物件的扁平清單。

由於此清單不包含未包含於導覽中的頁面,此清單並不一定是所有網站頁面的完整清單。此清單會符合用於所有「下一頁」和「上一頁」連結的頁面清單和順序。若要檢視所有頁面的清單,請使用 pages 範本變數。

下列是將第一和第二層導覽作為巢狀清單輸出的基本使用範例。

{% if nav|length > 1 %}
    <ul>
    {% for nav_item in nav %}
        {% if nav_item.children %}
            <li>{{ nav_item.title }}
                <ul>
                {% for nav_item in nav_item.children %}
                    <li class="{% if nav_item.active %}current{% endif %}">
                        <a href="{{ nav_item.url|url }}">{{ nav_item.title }}</a>
                    </li>
                {% endfor %}
                </ul>
            </li>
        {% else %}
            <li class="{% if nav_item.active %}current{% endif %}">
                <a href="{{ nav_item.url|url }}">{{ nav_item.title }}</a>
            </li>
        {% endif %}
    {% endfor %}
    </ul>
{% endif %}

base_url

base_url 提供 MkDocs 專案根目錄的相對路徑。雖然您可以直接透過在其前面加上本機相對 URL 來使用,但最好使用 url 範本過濾器,它會更明智地套用 base_url

mkdocs_version

包含目前 MkDocs 版本。

build_date_utc

Python datetime 物件,表示文件建立於 UTC 的日期和時間。這對於顯示文件最後更新時間很有用。

pages

專案中所有頁面的 File 物件扁平清單。此清單可能包含未包含於全球 導覽 中的頁面,且可能與該導覽中的頁面順序不符。可以從 file.page 存取每個 Filepage 物件。

page

在非從 Markdown 原始檔呈現的範本中,page 變數為 None。在從 Markdown 原始檔呈現的範本中,page 變數包含 page 物件。相同的 page 物件用於全球 導覽 中的 導覽物件pages 範本變數。

基礎:StructureItem

所有 page 物件都包含下列屬性

title() -> str | None

傳回當前頁面的標題。

在呼叫 read_source() 之前,此值為空。也可以透過 render() 來更新。

依序檢查並使用最早傳回有效標題的標題

  • 在 init 上提供的值(從設定檔傳入)
  • metadata 的 'title' 值
  • Markdown 內容中第一個 H1 的內容
  • 將檔案名稱轉換為標題
content: str | None instance-attribute

Markdown 呈現為 HTML 的內容,這是範文的內容。

.render() 後填入。

toc: TableOfContents instance-attribute

表示頁面目錄的 iterable 物件。toc 中的每個項目都是 AnchorLink

下列範例會顯示頁面目錄的前兩層。

<ul>
{% for toc_item in page.toc %}
    <li><a href="{{ toc_item.url }}">{{ toc_item.title }}</a></li>
    {% for toc_item in toc_item.children %}
        <li><a href="{{ toc_item.url }}">{{ toc_item.title }}</a></li>
    {% endfor %}
{% endfor %}
</ul>
meta: MutableMapping[str, Any] instance-attribute

封裝在 markdown 頁面開頭的元資料。

在此範例中,我們定義頁面標題上方有一個 source 屬性。

source: generics.py
        mixins.py

# Page title

Content...

範本可以使用 meta.source 變數來存取頁面的這些元資料。接著,可以使用它來連結至與文件頁面相關的原始檔。

{% for filename in page.meta.source %}
  <a class="github" href="https://github.com/.../{{ filename }}">
    <span class="label label-info">{{ filename }}</span>
  </a>
{% endfor %}
url: str 特性

相對於 MkDocs site_dir 網址的頁面網址。

預期要結合 url 篩選器來使用,以確保網址與目前的頁面相關。

<a href="{{ page.url|url }}">{{ page.title }}</a>
file: File 實體屬性

製作頁面的文件 File

abs_url: str | None 實體屬性

伺服器根目錄的頁面絕對網址,依據 site_url 組態設定值決定。此值包含 site_url 中的任何子目錄,但不包含網域。base_url 不應與這個變數搭配使用。

例如,如果 site_url: https://example.com/,則頁面 foo.mdpage.abs_url 值為 /foo/。然而,如果 site_url: https://example.com/bar/,則頁面 foo.mdpage.abs_url 值為 /bar/foo/

canonical_url: str | None 實體屬性

依據 site_url 組態設定值決定,至目前頁面的完整正規網址。此值包含網域和 site_url 中的任何子目錄。base_url 不應與這個變數搭配使用。

edit_url: str | None 實體屬性

原始資料庫中的原始頁面完整網址。通常用來提供連結以編輯原始頁面。base_url 不應與這個變數搭配使用。

is_homepage: bool 特性

評估網站首頁為 True,其他所有頁面為 False

它可以與 Page 物件的其他屬性一起使用來改變行為。例如,首頁顯示不同的標題

{% if not page.is_homepage %}{{ page.title }} - {% endif %}{{ site_name }}
previous_page: Page | None 實體屬性

上一個頁面的頁面物件,或為 `None`。當前頁面為網站導覽中的第一個項目,或當前頁面根本未包含在導覽中時,值將為 `None`。

next_page: Page | None 實例屬性

下一個頁面的頁面物件,或為 `None`。當前頁面為網站導覽中的最後一個項目,或當前頁面根本未包含在導覽中時,值將為 `None`。

parent: Section | None = None 類別屬性 實例屬性

此項目在導覽網站中的直接父物件。如果在最上層,則為 `None`。

children: None = None 類別屬性 實例屬性

頁面不包含子項目,且屬性總是為 `None`。

active: bool 屬性 可寫入

如果是 `True`,表示此頁面為目前檢視的頁面。預設為 `False`。

is_section: bool = False 類別屬性 實例屬性

表示此導覽物件為「區段」物件。對於頁面物件,總是為 `False`。

is_page: bool = True 類別屬性 實例屬性

表示此導覽物件為「頁面」物件。對於頁面物件,總是為 `True`。

表示此導覽物件為「連結」物件。對於頁面物件,總是為 `False`。

目錄中的單一項目。

title: str 實例屬性

項目的文字,以 HTML 呈現。

url: str 屬性

指向項目的 URL 的 hash 片段。

等級:int 執行個體屬性

該項目的 0 基礎等級。

子項:list[AnchorLink] 執行個體屬性

任何子項的可迭代清單。

nav 範本變數中包含的導覽物件可能是其中一個 區段 物件、頁面 物件,以及 連結 物件。雖然區段物件可能包含嵌套的導覽物件,但頁面和連結不會。

頁面物件是完整的頁面物件,就像用於目前 頁面 的物件一樣,具有可用的相同所有屬性。區段和連結物件包含的屬性子集定義如下

區段

區段 導覽物件定義導覽中的命名區段,並包含子導覽物件的清單。請注意,區段不包含 URL,也不是任何類型的連結。但預設上,MkDocs 將索引頁面排序到最上方,如果某個佈景主題選擇這麼做,第一個子項可以做為區段的 URL。

基礎:StructureItem

區段 物件提供了以下屬性

標題:str 執行個體屬性

區段的標題。

parent: Section | None = None 類別屬性 實例屬性

此項目在導覽網站中的直接父物件。如果在最上層,則為 `None`。

子項:list[StructureItem] 執行個體屬性

所有子導覽物件的可迭代清單。子項可能包括嵌套的區段、頁面和連結。

已啟用:bool 屬性 可寫

True 時,表示這個區段的子頁面是目前頁面,可做為目前已檢視的區段來突顯這個區段。預設值為 False

is_section:bool = True 類別屬性 執行個體屬性

表示導覽物件是「區段」物件。對區段物件永遠為 True

is_page:bool = False 類別屬性 執行個體屬性

表示導覽物件是「頁面」物件。對區段物件永遠為 False

表示導覽物件是「連結」物件。對區段物件永遠為 False

連結 導覽物件包含一個連結,不會指向 MkDocs 內部頁面。

基礎:StructureItem

連結 物件提供了以下屬性

標題:str 執行個體屬性

連結的標題。這一般來說都會用作連結標籤。

url: str 實例屬性

連結指標的 URL。URL 永遠都應該是絕對路徑 URL,不應該需要在前面加上 base_url

parent: Section | None = None 類別屬性 實例屬性

此項目在導覽網站中的直接父物件。如果在最上層,則為 `None`。

children: None = None 類別屬性 實例屬性

連結不包含子物件,屬性總是為 None

active: bool = False 類別屬性 實例屬性

外部連結無法「啟用」,屬性永遠都是 False

is_section: bool = False 類別屬性 實例屬性

表示導覽物件是「區段」物件。針對連結物件,永遠都是 False

is_page: bool = False 類別屬性 實例屬性

表示導覽物件是「頁面」物件。針對連結物件,永遠都是 False

表示導覽物件是「連結」物件。針對連結物件,永遠都是 True

額外內容

可以用 extra 組態選項將其他變數傳遞給範本。這是一組可以讓自訂範本有更佳彈性的關鍵值配對。

例如,這可以用來包含所有頁面的專案版本和與專案有關的一堆連結。這可以用以下的 extra 組態實作:

extra:
  version: 0.13.0
  links:
    - https://github.com/mkdocs
    - https://docs.readthedocs.org/en/latest/builds.html#mkdocs
    - https://mkdocs.dev.org.tw/

然後顯示在自訂佈景主題的這段 HTML 中。

{{ config.extra.version }}

{% if config.extra.links %}
  <ul>
  {% for link in config.extra.links %}
      <li>{{ link }}</li>
  {% endfor %}
  </ul>
{% endif %}

範本篩選器

除了 Jinja 的預設篩選器 外,也可以在 MkDocs 範本中使用下列自訂篩選器

url

正規化 URL。絕對路徑 URL 會直接通過。如果 URL 是相對路徑,而範本內容包括頁面物件,則會以該頁面物件為相對值傳回 URL。否則,會預先加上 base_url 傳回 URL。

<a href="{{ page.url|url }}">{{ page.title }}</a>

tojson

安全地將 Python 物件轉換為 JavaScript 程式碼中的值。

<script>
    var mkdocs_page_name = {{ page.title|tojson|safe }};
</script>

script_tag

1.5 版的新功能

將 `extra_javascript` 中的項目轉換為 `