Standard Extension

The default page shell, section renderer, link renderer, shared templates, CSS, JS, and base assets.

webifier.standard is the foundation extension most sites enable first. It supplies the default page shell, section renderer, content-page shell, link renderer, freeform renderer, shared navigation/header/footer macros, base CSS, syntax highlighting, and code-block copy behavior.

config:
  webifier:
    extensions:
      site:
        uses: webifier.standard

A Webifier page starts as an ordered YAML mapping. Webifier walks it from top to bottom and renders each non-reserved key.

  • A string becomes Markdown.
  • A list becomes a rendered link list.
  • A dictionary becomes a page at the root, and a section below the root.
  • kind switches a dictionary to another renderer.
  • template switches the template used for a page or section.

That means a page is mostly a readable outline: write the content in the order you want people to read it, then add renderer hints only where the default behavior is not enough.

These top-level keys configure the page instead of rendering as ordinary sections:

  • title: browser title and fallback page title.
  • header: page hero/header data.
  • nav: navigation links.
  • footer: footer data.
  • meta: HTML meta tags for the page template.
  • config: page or site configuration.
  • search: search behavior for the page.
  • style: page-level style data for templates.
  • kind: page renderer id.
  • template: page template override.

Every other top-level key renders as content in YAML order.

Content-page renderers can read page-local controls from the reserved config key. The same contract is used for Markdown page prefaces, notebook first-cell page prefaces, PDF sibling page.yml files, and normal YAML pages:

title: Page title
config:
  content_pages:
    toc: true
  markdown:
    toc: true
  notebook:
    toc: true
    colab: false
  pdf:
    toc: false
    download: false

related:
  kind: links
  items:
    - text: Another page
      src: pages/user-guide/index.yml

config is never rendered as content. It is merged over the site-level config for that one page, then extensions read the parts they own. In the example above, content_pages, markdown, notebook, and pdf are extension-owned config namespaces.

Free-form keys outside config, such as related, are normal page sections. For Markdown and notebooks, those extra sections render after the main file body, which lets a content file carry authors, comments, related links, or custom extension sections without mixing them into the prose.

The important distinction is:

  • Put behavior under config.
  • Put visible structured content outside config.
---
title: A Markdown Page
config:
  content_pages:
    toc: true
  markdown:
    toc: true

authors:
  kind: people
  content:
    - name: Vahid Zehtab
      role: Author
      website: https://vahidz.com
comments:
  kind: comments
  label: false
---

# Body

In this example, config.markdown and config.content_pages control the generated content page. The authors and comments keys render after the body using normal section renderers.

A normal section is also just a dictionary. The standard section renderer consumes a few reserved keys and treats everything else as children.

section_name:
  label: Optional visible label
  background: optional/path/to/image.png
  template: optional-inner-template.html
  kind: section

  first_child: |
    Markdown content.

  second_child:
    label: Nested section
    content: |
      A nested child section.

Section-level reserved keys are:

  • label: string, false, or {text, position}.
  • background: image path copied into the built site and used behind the section.
  • template: inner template for this section, while keeping section chrome.
  • kind: renderer id for this block.
  • style, freeform, defaults: reserved for renderer/template use.

Any non-reserved key inside a section becomes a child. Here, content is not magic; it is simply a child key whose value is Markdown.

introduction:
  label: Introduction
  content: |
    This is a normal section. Markdown is allowed here.

    - Lists work.
    - Inline `code` works.
    - Inline math like \(E = mc^2\) and display math work.
    - Fenced blocks get highlighting and copy buttons.

    \[
    \nabla_\theta \mathcal{L}(\theta)
    =
    \frac{1}{N}\sum_{i=1}^{N}\nabla_\theta \ell_i(\theta)
    \]

    ```mermaid
    flowchart LR
      A["Markdown"] --> B["MathJax"]
      A --> C["Mermaid"]
      A --> D["Highlighted code"]
    ```

    ```yaml
    hello: world
    nested:
      value: 42
    ```

This is a normal section. Markdown is allowed here.

  • Lists work.
  • Inline code works.
  • Inline math like \(E = mc^2\) and display math work.
  • Fenced blocks get highlighting and copy buttons.
\[ \nabla_\theta \mathcal{L}(\theta) = \frac{1}{N}\sum_{i=1}^{N}\nabla_\theta \ell_i(\theta) \]
flowchart LR A["Markdown"] --> B["MathJax"] A --> C["Mermaid"] A --> D["Highlighted code"]
hello: world
nested:
  value: 42

Labels can be shown on the left, top, or bottom. They can also be disabled when a section should behave like a plain content panel.

label_examples:
  label:
    text: Label examples
    position: top

  left_label:
    label: Left label
    content: Left labels are the default.

  top_label:
    label:
      text: Top label
      position: top
    content: Top labels span the content width.

  bottom_label:
    label:
      text: Bottom label
      position: bottom
    content: Bottom labels appear after the content panel.

  no_label:
    label: false
    content: This child has no visible label.

Left labels are the default section style. They work well for scan-heavy pages where each content band needs a stable name.

Top labels are useful when the section content needs the full width.

Bottom labels are available when the label should feel like a caption.

Disabled labels keep the standard content panel but remove the visible label column.

Nesting is recursive. Any child dictionary without another kind becomes another standard section. This lets you structure pages into chapters, subchapters, callouts, examples, or result panels without memorizing a special schema.

chapter:
  label: Chapter
  opening: |
    Introductory Markdown for the chapter.

  subsection:
    label:
      text: Subsection
      position: top
    note: |
      Nested content can contain more Markdown.

    deeper:
      label: Deeper
      content: |
        This is a nested nested section.

This is the parent section. The blocks below are children rendered by the same section renderer.

Nested content can contain Markdown, lists, code, and more sections.

This is a nested nested section. The layout stays predictable because each dictionary is dispatched through the same renderer contract.

kind is how you switch behavior. The standard extension registers section, links, and freeform; other extensions register things like people, experience, publications, comments, notebook, or pdf.

resources:
  kind: links
  items:
    - text: User guide
      src: pages/user-guide/index.yml
    - text: Markdown example
      src: pages/user-guide/extensions/examples/tiny-markdown.md
    - text: Tiny PDF
      src: pages/user-guide/extensions/examples/tiny.pdf

raw_intro:
  kind: freeform
  html: |
    <div class="alert alert-secondary">
      Freeform content skips the section wrapper.
    </div>
Freeform renderer: this block is not wrapped by the standard labeled section container.

background accepts a local image path or an external image URL and applies it to the section. Local image paths are copied into the built site. It is a section-level visual option, so nested sections can use it too.

background_example:
  label:
    text: Background example
    position: top
  background: https://images.unsplash.com/photo-1515879218367-8466d910aaa4?auto=format&fit=crop&w=1600&q=80
  content: |
    This content is still ordinary Markdown.

This section uses an external background URL. The content remains regular Markdown inside the standard section chrome.

At the page root, template replaces the page template. Inside a section, template only replaces the section's inner content. The label, background, spacing, and section wrapper still come from the standard section renderer.

Templates are written in Jinja2, the Python templating engine Webifier uses under the hood. A section template receives the section's non-reserved YAML keys as data, and can use helpers such as markdown(...) to render Markdown inside the template.

For the full customization path, see Template Overrides, Template-Only Renderers, and the lower-level Renderer / Kind System.

templated_callout:
  label: Template
  template: renderers/standard-callout.html
  eyebrow: Example
  title: Inner template override
  body: |
    The template receives these keys as `data` and can call helpers such as
    `markdown(...)`.

Let's say your website has this renderer template at renderers/standard-callout.html:

<div class="border-start border-4 border-primary-subtle ps-3 py-2">
  {% if data.eyebrow %}
    <p class="text-uppercase small text-muted fw-semibold mb-1">
      {{ data.eyebrow }}
    </p>
  {% endif %}
  {% if data.title %}
    <h3 class="h5 mb-2">{{ data.title }}</h3>
  {% endif %}
  {% if data.body %}
    <div class="mb-0">{{ markdown(data.body) }}</div>
  {% endif %}
</div>

data.eyebrow, data.title, and data.body come directly from the YAML keys under the templated section. The {% if ... %} blocks are normal Jinja control flow, and {{ ... }} prints a value into the output.

Example

Inner template override

The section still owns the label and outer panel, while renderers/standard-callout.html controls the content inside it.

webifier.standard contributes default renderer ids:

defaults:
  page: page
  section: section
  links: links
  markdown: markdown

You can override those defaults in config, or install a later extension instance with override: true to replace renderer registrations.

config:
  webifier:
    extensions:
      site:
        uses: webifier.standard
      lab_site:
        uses: my_lab.standard
        override: true
    defaults:
      section: my-lab-section

The deeper extension contract is documented in the Extension and Customization Guide. For site-level defaults and extension configuration, see Configuration Basics.

The practical syntax is:

  • <page key>: <string> renders Markdown.
  • <page key>: <list> renders links.
  • <page key>: <dict> renders a section.
  • label: "Name" sets the visible section label.
  • label: false hides the label.
  • label: {text: "Name", position: top} puts the label above content.
  • label: {text: "Name", position: bottom} puts the label below content.
  • background: path/to/image sets a section background image.
  • template: path/to/template.html changes section inner rendering.
  • kind: links renders a direct link list.
  • kind: freeform renders content without the standard section wrapper.
  • Any other kind is handled by whichever extension registered that renderer.
  • config.markdown.toc, config.notebook.toc, and config.pdf.toc control generated tables of contents for those page types.
  • config.content_pages.toc is the shared lower-level content-page control.
  • config.content_pages.cleanup enables shared content cleanup for generated content pages.

When the standard renderer stops being enough, create an extension that registers a new kind and give that renderer the schema you actually want.