feat(seo): rich JSON-LD — ProfilePage, enriched Person, BreadcrumbList#7
Merged
feat(seo): rich JSON-LD — ProfilePage, enriched Person, BreadcrumbList#7
Conversation
…List The previous JSON-LD was a flat Person object, identical on every page. This rewrites it as a per-page @graph with three (or four) named nodes: ProfilePage (one per page, the canonical schema for personal profile pages — supported by Google rich results since Sep 2023) - @id, url, name, inLanguage, dateModified - isPartOf -> WebSite - about / mainEntity -> Person - breadcrumb -> BreadcrumbList (variant pages only) Person (shared by @id across all pages, written once) - existing: name, givenName, familyName, jobTitle, address, worksFor, url, sameAs (LinkedIn + GitHub) - added: description, alumniOf (Berufsschule Erlangen), knowsLanguage (de + en as Schema.org Language objects), knowsAbout (~30 explicit skill/topic keywords drawn from the CV content), hasOccupation (Occupation node with location, experience, skills), and a richer worksFor with Netresearch DTT's address. WebSite (publisher = Person, in_language = de) BreadcrumbList (only on cv-executive.de.html and cv-technical.de.html) - Übersicht -> <Variant> CV Each ProfilePage now references the shared Person and WebSite by @id rather than duplicating them inline, which is the canonical pattern for linked data and keeps the payload deduplicated for crawlers. build.py refactor: - New PERSON_JSONLD, WEBSITE_JSONLD module constants. - New page_jsonld() builder that returns a per-page @graph payload. - New variant_breadcrumb() builder. - render_html() and render_index() construct the per-page payload before calling json.dumps (no template change needed; the template still receives a serialized string via the jsonld variable). Verified locally with Lighthouse desktop preset on all three pages: - Performance / A11y / Best practices / SEO 100 / 100 / 100 / 100 - CLS 0 - Lighthouse 'structured-data' audit is scoreDisplayMode='manual', so it always shows the placeholder card — Lighthouse does not validate JSON-LD itself. Validate via Google's Rich Results Test or validator.schema.org once deployed. Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
There was a problem hiding this comment.
Pull request overview
This PR upgrades the site’s structured data from a single flat Person JSON-LD blob to per-page Schema.org @graph JSON-LD, adding ProfilePage (per page), a richer shared Person, a shared WebSite, and BreadcrumbList for CV variant pages to improve rich-result eligibility and entity understanding.
Changes:
- Replace the single
PersonJSON-LD payload with a per-page@graphpayload that includesProfilePage, sharedPerson, and sharedWebSite. - Enrich the
Personentity with additional attributes (e.g.,description,alumniOf,knowsLanguage,knowsAbout,hasOccupation, and expanded org address). - Add
BreadcrumbListgeneration for CV variant pages and wire JSON-LD generation intorender_html()/render_index().
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Today every page ships an identical flat `Person` JSON-LD. That is valid and helps Knowledge Graph entity disambiguation, but it doesn't describe the page itself (Google supports `ProfilePage` for personal-profile pages since Sep 2023 and recognises `BreadcrumbList` for navigation), and the `Person` was missing several attributes that exist in plain text in the CV body.
What
Replaces the flat `Person` payload with a per-page `@graph` payload that the canonical Schema.org pattern uses for typed, deduplicated linked data.
Every page emits:
Cross-references use `@id` so Person and WebSite are written once per page even though every node references them. Standard linked-data hygiene.
Why this matters
The realistic effect for a CTO's CV is mostly better entity matching in Google's Knowledge Graph and the chance of a breadcrumb in SERPs — visible Person rich-snippets aren't generally shown for non-celebrities. The structured data is also useful for any AI agent or crawler indexing the page.
Pure refactor of one file
Only `scripts/build.py` changed. New module-level constants `PERSON_JSONLD`, `WEBSITE_JSONLD`, plus `page_jsonld()` and `variant_breadcrumb()` builders. `render_html()` and `render_index()` call those before `json.dumps` — no template change.
Verification
Local Lighthouse desktop preset, all three pages:
Lighthouse's `structured-data` audit is `scoreDisplayMode=manual` (it just reminds you to validate externally; doesn't score). Recommend running the deployed `https://cybottm.github.io/cv/\` through Google's Rich Results Test once this lands to confirm Google's parser is happy and which rich-result types it recognises.