feat: editorial CV pipeline — real HTML/PDF rendering, Lighthouse 100#3
Merged
Conversation
Replaces the deliberately-broken fallback renderers with a Python + Jinja2 + Markdown + WeasyPrint pipeline. Drops committed public/ and builds it fresh in CI. Pipeline - scripts/build.py reads src/cv-*.de.md (YAML front-matter + Markdown), renders through templates/, writes public/*.html and public/*.pdf, plus a public/index.html landing page. - requirements.txt pins markdown, jinja2, weasyprint, pyyaml. - Makefile gains install / build / serve / lighthouse / clean targets. Templates - templates/base.html.j2 emits a complete <head>: title, description, author, robots, theme-color, canonical, favicon, alternate (PDF), Open Graph profile tags, Twitter card, JSON-LD schema.org/Person, font preloads, stylesheet. - templates/cv.html.j2 wraps body in semantic <header>/<main>/<footer> with a skip-link, descriptive aria-labels on PDF links, aria-current on the active variant link, rel=me on profile links. - templates/index.html.j2 is a proper landing page with lede, versions list, contact section, all sectioned with aria-labelledby. Visual (editorial / serif / typographic) - Self-hosted variable fonts (OFL): Source Serif 4 (body), Inter (UI). - Tokens: warm off-white page (#fbfaf6), high-contrast text (>16:1), deep wine accent (#7d2d3a, >5.7:1), hairline rules. - Generous whitespace, single-column max 36em, small-caps section labels, bullet markers in accent color, descriptive link underlines. - Print: A4, 18mm margins, page numbers via @page footer, page-break control on h2/h3/li, link URLs revealed after link text on print. CI / Lighthouse - .github/workflows/build.yml installs Python + WeasyPrint native deps, runs build.py, uploads public/ as Pages artifact for main, and as a workflow artifact for PRs. - New 'lighthouse' job runs treosh/lighthouse-ci-action against the built artifact for every PR; .lighthouserc.json asserts a11y / BP / SEO at 1.0 and performance at 0.95. - Pages deploy job is unchanged behavior, just decoupled. Cleanup - Drops scripts/render_html.py, scripts/render_pdf.py (the stub generators that produced the unstyled HTML and the 1-page text-only PDFs). - Drops committed public/*.html and public/*.pdf (.gitignored now). - Removes the PANDOC=__missing_pandoc__ WEASYPRINT=__missing_weasyprint__ override that forced the fallbacks in CI. Verified locally with Lighthouse desktop preset on all three pages (index, executive, technical): performance 100, accessibility 100, best-practices 100, seo 100. Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Lighthouse rejects file:// URLs with INVALID_URL. The treosh action's staticDistDir input spins up a local server for the public/ folder and runs the audits against http://localhost:<port>/<path>. Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
There was a problem hiding this comment.
Pull request overview
This PR replaces the prior “forced fallback” CV build with a Python-based pipeline that renders Markdown + YAML front-matter through Jinja2 templates into SEO/a11y-friendly HTML and real PDFs via WeasyPrint, and adds CI Lighthouse gating for PRs.
Changes:
- Introduces
scripts/build.pyto generatepublic/(HTML + PDF + landing page) fromsrc/cv-*.de.mdusing Jinja2 + Markdown + WeasyPrint. - Adds new Jinja2 templates with richer
<head>metadata (canonical/OG/Twitter/JSON-LD) and a redesigned editorial CSS with self-hosted fonts. - Updates CI to build via Python + system libs and run Lighthouse CI on PRs; stops committing
public/artifacts.
Reviewed changes
Copilot reviewed 16 out of 25 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| templates/index.html.j2 | New landing page template listing variants + contact links. |
| templates/cv.html.j2 | New CV page template with header nav and injected rendered Markdown body. |
| templates/base.html.j2 | Shared <head> metadata + font preloads + skip-link wrapper. |
| src/cv-technical.de.md | Adds YAML front-matter for technical variant metadata. |
| src/cv-executive.de.md | Adds YAML front-matter for executive variant metadata. |
| scripts/render_pdf.py | Removes legacy PDF stub generator. |
| scripts/render_html.py | Removes legacy Markdown-to-HTML fallback renderer. |
| scripts/build.py | New build entrypoint: parse front-matter, render templates, write HTML/PDF, copy assets. |
| requirements.txt | Pins Python dependencies for the new pipeline. |
| README.md | Documents new layout, build steps, Lighthouse, and publishing behavior. |
| public/index.html | Removes committed generated artifact. |
| public/cv-technical.de.html | Removes committed generated artifact. |
| public/cv-executive.de.html | Removes committed generated artifact. |
| Makefile | Replaces Pandoc/WeasyPrint CLI flow with venv + scripts/build.py and Lighthouse helper target. |
| assets/style.css | Replaces styling with editorial screen/print CSS and self-hosted font usage. |
| assets/favicon.svg | Adds SVG favicon asset. |
| .lighthouserc.json | Adds Lighthouse CI configuration and assertions. |
| .gitignore | Ignores public/, venv, and Lighthouse artifacts. |
| .github/workflows/build.yml | Updates CI to use Python deps, build with scripts/build.py, and run Lighthouse on PRs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The treosh action's configPath input takes precedence over the staticDistDir/urls inputs, so the action saw staticDistDir as null and tried to load /index.html as an absolute URL (INVALID_URL). Putting both into the .lighthouserc.json collect block keeps a single source of truth that local 'lhci autorun' and the GH Actions step both honor. Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
- scripts/build.py: drop the leading '../' on the CV asset() prefix. Both index.html and cv-*.de.html sit at the root of public/ next to assets/, so plain 'assets/...' resolves correctly under any subpath (including the GitHub Pages /cv/ project URL). The '../assets/...' form happened to work locally because python http.server collapses '..' past root, but on Pages it would escape outside /cv/. - templates/cv.html.j2 + templates/index.html.j2: outer <article> becomes <div class="cv">. The HTML spec forbids <main> as a descendant of <article>; in cv.html.j2 the body content is now wrapped in <article class="cv-body"> *inside* <main id="main">. - .lighthouserc.json: switch performance assertion from 'warn' to 'error' (warn does not fail the run, which contradicted the PR description). a11y/BP/SEO already at error. Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Comment on lines
+6
to
+8
| "http://localhost/index.html", | ||
| "http://localhost/cv-executive.de.html", | ||
| "http://localhost/cv-technical.de.html" |
Comment on lines
+205
to
+208
| favicon = ASSETS / "favicon.svg" | ||
| if favicon.exists(): | ||
| shutil.copy2(favicon, PUBLIC / "favicon.svg") | ||
|
|
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
PDFs were broken (the CI workflow deliberately forced a fallback Python script that emitted a 1-page text-only PDF stub) and the HTML had none of the metadata/semantics needed for decent Lighthouse scores. This rebuild swaps the whole pipeline for a Python + Jinja2 + Markdown + WeasyPrint setup with an editorial visual treatment.
What changes
Pipeline (replaces forced-fallback build):
scripts/build.py— single entrypoint, readssrc/cv-*.de.md(YAML front-matter + Markdown), renders through Jinja2 templates, writespublic/cv-*.de.htmland matching PDFs via WeasyPrint, pluspublic/index.htmllanding page.templates/base.html.j2+cv.html.j2+index.html.j2own the full<head>: title, description, canonical, OG profile tags, Twitter card, JSON-LDschema.org/Person, favicon, font preloads.__missing_pandoc__/__missing_weasyprint__overrides; deletesscripts/render_html.pyandscripts/render_pdf.py.public/*— it's now gitignored and built fresh in CI on every push/PR.Visual (editorial / serif / typographic):
#fbfaf6, high-contrast text (>16:1), deep wine accent#7d2d3a(>5.7:1).@pagefooter,break-inside: avoidon roles, link URLs revealed on print.Accessibility / SEO / best-practices wins:
<main>/<header>/<footer>, single<h1>per page, descriptivearia-labelon PDF links,aria-currenton active variant,rel=meon profile links.<title>,<meta name=description>,theme-color,link rel=canonical,link rel=iconfavicon.svg,link rel=alternate type=application/pdf.Personfor richer LinkedIn/Twitter/Google previews.CI:
lighthousejob runstreosh/lighthouse-ci-action@v12on every PR with the builtpublic/as fixture..lighthouserc.jsonasserts a11y / BP / SEO at 1.0 and performance at 0.95 (will fail the PR check if any drops).Local Lighthouse results (desktop preset)
Verified on all three pages before opening this PR:
index.htmlcv-executive.de.htmlcv-technical.de.htmlCompare to the previous numbers (a11y 87, SEO 80, best-practices 96).
PDFs
Both PDFs now render through real WeasyPrint, not the 1-page text stub. ~36 KB each, 2 pages, with running headers, page numbers, and the same editorial typography as the screen version.
How to verify locally
Notes / trade-offs
public/is no longer in git history going forward (already removed in this PR; old commits keep their copies — fine).