{% extends "base.html" %} {% block title %}{{ page.title or site_name }}{% endblock %} {# The display title used both by `og:title` below and by the `

` in `{% block content %}` -- one fallback chain (title -> first heading -> filename stem), not two copies that could drift. Set here, outside any block, so both `{% block head %}` and `{% block content %}` (of THIS child template) can read it: a Jinja child template's own top-level `{% set %}` statements are visible inside its block bodies, confirmed by direct testing against Jinja's real scoping behavior before relying on it here. #} {% set page_title = page.title or (page.headings[0].text if page.headings else page.path.stem) %} {% set page_description = page.description or site_description %} {# Per-page SEO tags. Meta description is unconditional -- it needs no absolute URL, just `page.description` (empty string, never `None`, when a page has no `description:` frontmatter -- see `content/page.py`) falling back to `site_description` (`Config.SITE_DESCRIPTION`, injected by `mdzero/web/app.py`'s context processor). Canonical + the full OpenGraph block are gated together on `site_url` (`Config.SITE_URL`, same context processor) being set: every one of these tags needs an absolute URL to be meaningful (`og:title`/ `og:description`/`og:type` included -- a documented choice, not an oversight: emitting a partial, URL-less OpenGraph block when the site has no known public base URL is arguably more misleading to a scraper than omitting OpenGraph entirely, matching how canonical itself is omitted rather than pointing at a broken `http://None/...` URL). Scoped to `page.html` specifically (not `base.html`, where `{% block head %}` is declared empty) so the bundled `404.html` -- which extends `base.html` directly and never overrides this block -- never inherits any of these tags, regardless of `site_url`. `is_error_page`: true only for a custom `docs/404.md` page rendered through this same template on a 404 response (`mdzero/web/errors.py` and `mdzero/build/generator.py` both pass it). Such a page IS routable at some route (e.g. "/404") but is never the canonically correct way to reach that content -- a scraper following `canonical`/`og:url` there would land on a 404. So on an error page: emit `robots: noindex` and skip the canonical/OpenGraph block entirely, regardless of `site_url`. #} {% block head %} {% if is_error_page %} {% elif site_url %} {% set canonical_url = build_absolute_url(site_url, page.route) %} {% endif %} {% endblock %} {# Sidebar nav rendering. `icon:` (optional frontmatter, a literal character/emoji, e.g. `icon: 🚀` -- see `NavNode.icon`) renders in a fixed-width `.nav-icon` span so pages without one don't shift the label of siblings that have one. Any node with children (a synthetic group with no `index.md`, OR a real page that also has subpages) is collapsible: its children sit in a `hidden`-toggled `.nav-group-children` div (same attribute `search-dialog.js` already uses for the search overlay). `_branch_is_active` decides the pre-JS default (a section containing the current page starts expanded, so a reader is never dropped onto a page whose own section looks collapsed); the blocking script right after `#sidebar-nav` then applies any per-visitor `localStorage` override before first paint, same technique as the dark-mode script above. `data-nav-id` is a tree position path (`"0-2-1"`, ...) rather than a title slug -- stable across renders with no risk of collisions or awkward characters. #} {% macro _branch_is_active(nodes, target_route) %} {%- for node in nodes -%} {%- if node.route == target_route -%}1{%- endif -%} {%- if node.children -%}{{ _branch_is_active(node.children, target_route) }}{%- endif -%} {%- endfor -%} {% endmacro %} {% macro render_nav(nodes, path_prefix="") %} {% endmacro %} {% block content %}
{% if breadcrumbs %} {% endif %} {% if is_draft %}
Draft: this page is not published. It is only visible because the site is running in dev mode.
{% endif %}
{# `page.title` is "" when a page has no `title:` frontmatter -- mirror `mdzero/content/navigation.py`'s `_display_title` fallback order (title -> first heading -> filename stem) here rather than leaving an empty

, so the page heading always matches what the sidebar nav entry shows for the same page. (This fallback chain is computed once, as `page_title`, near the top of this template -- see the comment above `{% block head %}` -- and reused here rather than duplicated, so `og:title` can never diverge from the visible `

`.) #}

{{ page_title }}

{{ page.html | safe }}
{% if page.headings %} {% endif %}
{% endblock %}