Skip to content

fix(seo): stop repeating the org name in page titles, and give /abos one at all - #391

Merged
github-actions[bot] merged 2 commits into
mainfrom
fix/page-titles
Aug 26, 2026
Merged

fix(seo): stop repeating the org name in page titles, and give /abos one at all#391
github-actions[bot] merged 2 commits into
mainfrom
fix/page-titles

Conversation

@catomean

Copy link
Copy Markdown
Collaborator

The layout sets title.template = '%s | evig', so every page title already gets the org name appended. Three separate places added it a second time, each by a different mechanism, and each produced a visible duplicate in the browser tab, in search results and in shared links:

page rendered title cause
/ evig – evig — Intelligenz, für alle bezahlbar … | evig a page-level prefix and a message value opening with "evig —"
/about Über uns - evig | evig generateMetadata appending ${ORG.name}
/knowhow Knowhow - Guides, Blog, Workshops | evig | evig the message value itself ended in "| evig"

I fixed the homepage one earlier, then found the other two by reading rendered titles rather than code. Three instances of one mistake is where the class gets closed rather than the instance.

page-titles-not-doubled.test.ts now asserts that no title string in the message files carries the brand. Mutation-proved: putting "| evig" back into knowhow.meta.title fails with the offending key named.

The component half — appending ORG.name inside generateMetadata — isn't statically checkable from the message files, so each page carries a note saying the template owns the name. openGraph keeps the full form on purpose: a shared card has no template to expand.

Two related title defects, fixed with it

/abos had no metadata at all. A live product page — join a pool, pay a share — fell through to the layout default and wore the homepage's title. In a tab, in search, and in a shared link it was indistinguishable from the homepage.

/blog was the only page on the site missing the suffix, reading a bare Blog. Its layout and its index page both set a title; the layout's was redundant and now contributes only the description and OG defaults.

Verification

npm run verify green: lint, umlauts, chrome, docs, compliance, typecheck, 7757 tests (536 suites), production build.

I'll re-probe the rendered <title> of every page after deploy — that's how these were found in the first place, and it's the only way to confirm Next's template actually resolved as intended.

🤖 Generated with Claude Code

catomean and others added 2 commits August 26, 2026 13:54
…one at all

The layout sets `title.template = '%s | evig'`, so every page title already
gets the org name appended. Three separate places added it a second time, each
by a different mechanism, and each produced a visible duplicate in the browser
tab, in search results and in shared links:

  /          "evig – evig — Intelligenz, für alle bezahlbar … | evig"
             a page-level prefix AND a message value opening with "evig —"
  /about     "Über uns - evig | evig"
             generateMetadata appending `${ORG.name}`
  /knowhow   "Knowhow - Guides, Blog, Workshops | evig | evig"
             the MESSAGE VALUE itself ended in "| evig"

I fixed the homepage, then found the other two later by reading rendered
titles rather than code. Three instances of one mistake is where the class
gets closed, so `page-titles-not-doubled.test.ts` now asserts that no title
string in the message files carries the brand. Mutation-proved: putting
"| evig" back into knowhow.meta.title fails with the offending key named.

The component half (appending ORG.name inside generateMetadata) is not
statically checkable from the message files, so each page carries a note
saying the template owns the name. openGraph keeps the full form on purpose —
a shared card has no template to expand.

Two related title defects fixed with it:

  /abos had NO metadata at all, so a live product page — join a pool, pay a
  share — fell through to the layout default and wore the homepage's title.
  In a tab, in search and in a shared link it was indistinguishable from the
  homepage.

  /blog was the only page on the site MISSING the suffix, reading a bare
  "Blog". Its layout and its index page both set a title; the layout's is
  redundant and now only contributes the description and OG defaults.

npm run verify green: lint, umlauts, chrome, docs, compliance, typecheck,
7757 tests (536 suites), production build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The "Dual-Persona Inventory Smoke" turned red on PRs that had nothing to do
with it, and the reason is worth stating precisely: **it runs against the LIVE
site, not the branch.** So when #389 retired hardware-recycling and deleted
the `[service]/repair` route, this check stayed green through that PR and only
went red once the deletion DEPLOYED — after which it failed on every
subsequent PR, blaming whoever came next.

Four dead assertions:

    /services/hardware-recycling                    HTTP 404
    /services/computer-repair-upgrades/repair       HTTP 404
    /services/data-recovery-transfer/repair         HTTP 404
    /services/linux-open-source/repair              HTTP 404

The list is now the four service pages that exist, plus one assertion that a
retired service URL still REDIRECTS into /it-hilfe — the behaviour #389
deliberately chose over a 404, and which nothing was checking.

Gated so it cannot drift again, in either direction:
`inventory-routes-match-services.test.ts` asserts every `/services/*` path the
smoke expects to be live is an available SERVICE_CONFIGS entry, AND that every
available service is smoke-tested. Redirect assertions (those carrying a
`urlPattern`) are exempt, because keeping a retired URL alive is the point.
It runs in jest, on the branch, before the deploy that would otherwise arm the
trap. Mutation-proved: re-adding /services/hardware-recycling fails with that
path named.

Also folded in here, because it belongs with the title work rather than with
workshops (it briefly landed on the wrong branch after a switch with
uncommitted changes):

  - `services/layout.tsx` set `title: { absolute: … }`, which hand-rolled the
    brand suffix for the hub AND replaced the inherited title.template for the
    ENTIRE /services subtree. Every service page shipped with no suffix —
    including the /services/ai-robotics page added hours earlier, which
    rendered a bare "KI und Robotik im Betrieb". Found by counting "evig" in
    the rendered <title>, not by reading code; `absolute` is a perfectly valid
    metadata field, so nothing could flag it. The layout no longer sets a
    title and the hub declares its own in page.tsx.

  - `services/web-design-development/layout.tsx` deleted: 21 lines of
    HARDCODED ENGLISH title and meta description shipping on all eight
    locales, entirely overridden by its own page, wrapping children in a
    fragment and doing nothing else.

npm run verify green: lint, umlauts, chrome, docs, compliance, typecheck,
7758 tests, production build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions
github-actions Bot merged commit ad41ee9 into main Aug 26, 2026
9 checks passed
@github-actions
github-actions Bot deleted the fix/page-titles branch August 26, 2026 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant