A privacy-first, browser-based CV / Lebenslauf builder tuned for German hiring conventions: DIN 5008 A4 layout, photo support, date-of-birth field, DE/EN bilingual UI, and ten carefully typeset templates. Everything runs locally in your browser — your data never leaves the device unless you explicitly export it.
License: PolyForm Noncommercial 1.0.0 — source-available, free for personal, educational, and nonprofit use. Commercial use is not permitted. See License & Commercial use.
- Features
- Screenshots
- Tech stack
- Quick start
- Environment variables
- Project structure
- Scripts
- Quality gates
- Contact
- Support the project (endorsement)
- Contributing
- License & Commercial use
- 10 A4 templates — single-column, sidebar, and accent variants; German section labels by default (Berufserfahrung, Ausbildung, Kenntnisse…)
- DIN 5008 / Lebenslauf conventions — photo block, DOB field, German date formatting, Ortsvorwahl-aware phone validation
- Live preview — WYSIWYG A4 canvas with resizable split pane
- Export anywhere
- PDF (pixel-perfect, DOM-captured via
html-to-image+jsPDF) - ATS PDF (text-based single-column PDF optimised for applicant-tracking scanners)
- DOCX (rebuilt via the
docxlibrary — fully editable in Word) - JSON (portable schema for backup / re-import)
- PDF (pixel-perfect, DOM-captured via
- Undo / redo — 20-state history (via
zundo) - Version snapshots — save named CV versions, import/export as JSON
- Appearance customization — accent color with WCAG-enforced contrast, font pairing, photo shape, section order
- Bilingual UI — full DE/EN translations with compile-time parity enforcement
- Dark / light mode — system-aware via
next-themes - Offline-capable — all state persists to
localStorage; no account, no backend required - Accessible — WCAG 2.2 AA: 4.5:1 contrast, 44×44 touch targets, keyboard-navigable resize handle, error boundaries around every template
Add screenshots of the editor, live preview, and a finished PDF to
docs/and link them here.
| Layer | Choice |
|---|---|
| UI | React 19 + TypeScript (strict) |
| Build | Vite 8 |
| Styling | Tailwind CSS v4 + shadcn/ui (Radix primitives) |
| State | Zustand + Zundo (undo/redo) + persist middleware |
| Validation | Zod 4 |
| Drag & drop | @dnd-kit |
jsPDF + html-to-image + pdf-lib |
|
| DOCX | docx |
| Routing | React Router 7 |
| Tests | Vitest + happy-dom |
| Hosting | Firebase Hosting |
# 1. Clone
git clone https://github.com/ibhf13/cv_maker.git
cd cv_maker
# 2. Install
npm install
# 3. (Optional) Configure env — see below
cp .env.example .env
# 4. Run the dev server
npm run devOpen http://localhost:5173 and start filling in your Lebenslauf. Data auto-saves to localStorage every keystroke.
All vars are optional — the app runs fully offline without any of them. Copy .env.example to .env and fill in the ones you need.
| Variable | Purpose |
|---|---|
VITE_GITHUB_REPO_URL |
Enables the ★ GitHub button in the endorsement dialog and contact section. Example: https://github.com/ibhf13/cv_maker |
VITE_SUPPORT_URL |
Enables the ☕ Buy-me-a-coffee button. Any donation URL (Ko-fi, BuyMeACoffee, GitHub Sponsors, PayPal.me…) |
VITE_CONTACT_FORM_ENDPOINT |
Optional FormSubmit (or compatible) endpoint for the in-app contact form. If unset, the form falls back to a mailto: link. |
VITE_FIREBASE_* |
Firebase keys (analytics / hosting only — no CV data is ever uploaded). Leave blank for local development. |
src/
├── features/
│ ├── editor/ # CV form editor (tabs: personal, summary, experience, …)
│ ├── preview/ # Live A4 preview + resize handle
│ ├── templates/ # 10 CV templates + shared section renderers
│ ├── toolbar/ # Undo/redo, download menu, reset dialog
│ ├── export/ # PDF, ATS PDF, DOCX, JSON pipelines (lazy-loaded)
│ ├── versions/ # Named snapshots, JSON import/export
│ ├── endorsement/ # ★ + ☕ dialog
│ └── landing/ # Marketing landing page + contact form
├── components/ # Shared UI (shadcn primitives, error boundary, …)
├── hooks/ # use-cv-selectors, use-cv-field, use-cv-period, …
├── lib/ # Pure utils: Zod schema, i18n, completeness score, …
├── stores/ # Zustand store (cvData + ui state + undo/redo)
└── pages/ # Route-level pages
| Command | Description |
|---|---|
npm run dev |
Start Vite dev server |
npm run build |
Type-check + production build |
npm run preview |
Preview the production build locally |
npm run lint |
Run ESLint |
npm run lint:fix |
Auto-fix lint issues |
npm run type-check |
tsc --noEmit |
npm run test |
Run all Vitest suites |
npm run test:watch |
Watch mode |
npm run test:coverage |
Coverage report |
npm run deploy |
Build + deploy to Firebase Hosting |
Before sending a PR, please make sure all four pass:
npm run lint
npm run type-check
npm run test
npm run buildHave a question, a bug report, or a feature idea? Here are all the ways to reach out:
- 📧 Email — ibrahim.klusmann@gmail.com — fastest for private questions
- 📧 Email — ibrahim.klusmann@gmail.com — fastest for private questions
- 💬 In-app contact form — click Contact on the landing page; delivered via FormSubmit (or
mailto:fallback) - 🐛 Bug / feature — open an issue
- 💡 Discussion — GitHub Discussions
- 🇩🇪 Deutsch willkommen — Fragen auf Deutsch sind ausdrücklich willkommen.
This project is free and built in my spare time. If it saved you an afternoon of wrestling with Word templates, a small gesture goes a long way:
- ⭐ Star the repo — github.com/ibhf13/cv_maker — the cheapest way to say thanks and helps others discover it
- ☕ Buy me a coffee — set
VITE_SUPPORT_URL(Ko-fi / BuyMeACoffee / GitHub Sponsors) or ask for the link via the contact form - 🗣️ Share it — post it to a friend, a Slack, a job-seeker subreddit
- 🐛 Report a bug — a clear issue is worth more than a coffee
- 🌍 Translate — help extend beyond DE / EN by adding a new label map in
src/lib/editor-labels.*.ts
The ★ / ☕ buttons are wired up directly inside the app — look for the Endorsement dialog in the toolbar and the Contact section on the landing page.
PRs are welcome! Please:
- Open an issue first for non-trivial changes so we can agree on scope.
- Add a test for new logic (
lib/→ unit test, store → mutation test, template → smoke test, export → integration test). - Make sure all quality gates pass.
- Keep commits focused; write the why, not the what.
By submitting a contribution you agree that it will be released under the project's license (PolyForm Noncommercial 1.0.0).
This project is released under the PolyForm Noncommercial License 1.0.0 — a source-available, OSI-adjacent license designed specifically for software. The full text lives in LICENSE. In plain English:
- Personal use — build your own CV, hack on the code, run it locally
- Education — use it in a classroom, bootcamp, or course
- Research & study — academic and scientific use
- Nonprofits & public institutions — charities, public schools, public research orgs, government bodies, public health / safety / environment organisations
- Modification & redistribution — fork it, change it, share it, as long as you keep this license and the copyright notice
- Hosting it as a paid or ad-supported SaaS
- Bundling it into a commercial product
- Offering it as a service to paying clients
- Any use "primarily intended for or directed towards commercial advantage or monetary compensation"
Reach out via iebo.testt@gmail.com — commercial licenses are available on reasonable terms, including for small businesses and startups.
- MIT would allow anyone to repackage and sell the work without contributing back.
- CC BY-NC is written for creative works, not software — it doesn't cover patents and institutions often refuse it.
- PolyForm Noncommercial 1.0.0 was drafted by software-licensing lawyers specifically for this case: clear definitions, explicit safe harbours for nonprofits and educational institutions, explicit patent grant, and SPDX-recognised.
Copyright © 2026 ibhf13. Made with ☕ in Germany.