Personal portfolio and printable CV built with SvelteKit, Tailwind CSS, Paraglide, Typst, and prerendering.
The site is bilingual (de, en), privacy-conscious, screen-reader friendly, and designed to print cleanly as a CV.
SvelteKitwith@sveltejs/adapter-staticTailwind CSSParaglidefor UI i18nTypstfor PDF generationBiomefor linting and formattingVitestfor unit testsNetlifyfor hosting, edge locale redirects, and short-link redirects
resume.i18n.json
The downloadable PDFs are generated from the same localized JSON sources through a dedicated Typst pipeline.
UI translations live in:
messages/de.jsonmessages/en.json
Use the tool versions declared in mise.toml. It installs:
nodepnpmtypst
Install dependencies:
pnpm installStart the dev server:
pnpm devThe app runs with Paraglide compilation as part of the dev workflow.
If you use mise, install the local tools before running project commands:
mise install
mise exec -- pnpm install
mise exec -- pnpm devCompile Paraglide messages:
pnpm build:paraglideLint:
pnpm lintTypecheck:
pnpm check:typesQuick pre-push check (paraglide + resume source + lint + typecheck + unit tests):
pnpm check:quickFull check, mirroring everything CI runs (slow - includes Playwright, PDFs, and a full build):
pnpm checkTest:
pnpm testBuild everything, including PDFs:
pnpm buildBuild PDFs only:
pnpm build:pdfFormat:
pnpm formatThe repo currently checks:
- tech-experience derivation
- locale preference parsing
- localized resume parity
- short-link parity between
src/lib/data/short-links.tsandnetlify.toml
Target hosting is Netlify.
The deployed PDFs are published as:
/de/robert-schaefer-resume.de.pdf/en/robert-schaefer-resume.en.pdf
Important deployment behavior:
/redirects to/deor/envia a Netlify edge function based onAccept-Language/resume.jsonredirects to/de/resume.jsonor/en/resume.json- human-readable short links like
/linkedinor/oss-contributors-35c3are defined innetlify.toml
- Netlify builds the site, including the PDFs, from its own repository checkout.
Typstis not part of Netlify's build image, soscripts/netlify-build.shinstalls it with mise first. It does that only on Netlify's build servers (NETLIFY=true); local builds, e.g. withnetlify deploy, need Typst onPATHalready.mise.tomlsets it tolatest, so every build uses its newest release.- Builds need no secrets: they use the redacted client names committed in
resume.i18n.json(see "Redacted Clients" inAGENTS.md). - Vendored
ttforotffonts undertypst/fonts/are required for the PDF build. Seetypst/README.md.
The site includes:
- localized
<title>and meta description tags - canonical URLs
hreflangtags for the localized portfolio pages- Open Graph tags
- Twitter card tags
The current social preview image is:
static/roschaefer.jpg
- Print output is optimized primarily for Firefox.
- The web version and print version share the same content source.
- Generated Paraglide files in
src/lib/paraglideare never committed (the directory has its own nested.gitignore) and are recompiled on everydev/build/check/testrun. - The redacted resume source (
.generated/resume-source.json,.generated/resume.de.json,.generated/resume.en.json) is likewise gitignored and always regenerated fresh - seeAGENTS.mdfor why. - After editing the encrypted client fields with
sops resume.i18n.json, runpnpm redact-clientsto update the committed redacted names.