Skip to content

Site name duplicated in <title> on every page ("Great Docs | Great Docs") #327

Description

@lqgentner

Description

apply_title_template() in post-render.py appends | {site_name} to titles that already
end in the site name, so every page ships a doubled title. This affects the Great Docs
documentation site itself.

Evidence

Page <title>
Homepage Great Docs | Great Docs
User Guide: Introduction Welcome to Great Docs – Great Docs | Great Docs

I reproduced the same behaviour on my own site (great-docs 0.17.0, Quarto 1.9.38).

Cause

great_docs/assets/post-render.py, apply_title_template(), line 430:

# Skip if already has a pipe (already templated)
if " | " in current_title or " - " in current_title:
    if current_title.endswith(site_name):
        return html_content

# Build new title from template
new_title = template.replace("{page_title}", current_title).replace("{site_name}", site_name)

The guard has two issues:

  1. The dash doesn't match on subpages. Quarto joins page and site titles with an en-dash "–" (U+2013), but the guard only tests for hyphen-minus "-" (U+002D).
  2. There's no separator on home pages. The generated index.qmd carries title: "", so Quarto falls back to the site title ("Great Docs"). That contains neither "|" nor a dash, so the outer if is skipped and the site name is appended to itself.

Reproducible example

Driving the shipped function directly:

import importlib.util, pathlib
import great_docs

path = pathlib.Path(great_docs.__file__).parent / "assets" / "post-render.py"
spec = importlib.util.spec_from_file_location("post_render", path)
m = importlib.util.module_from_spec(spec)
# needs empty _site/ dir in cwd; runs the full post-render pipeline
pathlib.Path("_site").mkdir(exist_ok=True)
spec.loader.exec_module(m)

def run(title, site="Great Docs"):
    m._gd_options = {"seo_enabled": True, "site_name": site,
                      "title_template": "{page_title} | {site_name}"}
    html = m.apply_title_template(f"<title>{title}</title>", "index.html")
    return html.removeprefix("<title>").removesuffix("</title>")

for title in ("Great Docs", "Intro – Great Docs", "Intro - Great Docs"):
    print(f"{title!r:24} -> {run(title)!r}")
'Great Docs'             -> 'Great Docs | Great Docs'           # bug 2
'Intro – Great Docs'     -> 'Intro – Great Docs | Great Docs'   # bug 1
'Intro - Great Docs'     -> 'Intro - Great Docs'                # working

Suggested fix

Return early whenever the title already ends in the site name preceded by one of the two possible separators: Quarto's own en dash or a pipe from a previous pass of this same template.

current_title = title_match.group(1).strip()

if (
    current_title == site_name
    or current_title.endswith(f" | {site_name}")
    or current_title.endswith(f" – {site_name}")
):
    return html_content

new_title = template.replace("{page_title}", current_title).replace("{site_name}", site_name)

A plain current_title.endswith(f" {site_name}") is too loose: it would also
match a page whose own title ends in the site name with no separator
ever inserted (e.g. a page titled "Welcome to Great Docs" with title-prefix disabled).

Workaround

Suppressing the append via config, for anyone hitting this before a fix lands:

seo:
  title_template: "{page_title}"

Development environment

  • great-docs 0.17.0
  • Quarto 1.9.38
  • Python 3.13, Linux

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions