Expand README to include detailed developer onboarding information covering: - Quick start instructions - Repository layout and core concepts - Data model and state management - App flow and component architecture - Template system and theming - Printable HTML and export process - Contribution guidelines and troubleshooting
19 KiB
CV Engine — Developer Onboarding Guide
This guide helps new developers ramp up quickly on the CV Engine. It explains the architecture, how components work, data flow, theming, templates, and how to run and extend the project.
Quick Start
- Prereqs: Node.js 18+ and npm.
- Client setup:
cd cv-enginenpm installnpm run dev- Open
http://localhost:5173/.
- (Optional) Export server for PDF:
cd cv-export-servernpm installnpm run dev- Endpoint:
POST /export/pdf(consumes CV JSON, returns PDF).
Repository Layout
cv-engine/— React + Vite app: editors, templates, preview, theming, thumbnails.shared-printable/— Shared HTML builder used by client and server.cv-export-server/— Express service that renders PDFs with Puppeteer.
Core Concepts
- Single source of truth: CV is a JSON model validated by Zod (
cv-engine/src/schema/cvSchema.ts). - Inline preview renders React templates; printable preview uses a shared HTML builder for A4 output.
- Theming is applied via CSS variables so templates and thumbnails react to color selection.
Data Model & State
src/schema/cvSchema.ts- Fields:
personal,summary,work,education,skills,languages,certifications,templateId,colorTheme. - Validation: bullets ≤160 chars, summary ≤600 chars; required fields for jobs/education.
- Helpers:
sanitizeHtml,escapeText,createEmptyCV.
- Fields:
src/store/cvStore.ts- Holds CV data and UI state (
activeStep,isDirty, save status). - Actions:
updatePersonal,addWorkItem,updateWorkItem, reorder functions, section validators,updateTemplateId,updateColorTheme. - Selectors (via hooks): used across editors and preview to read/write state.
- Holds CV data and UI state (
App Flow & Top-Level Components
src/App.tsx- Orchestrates the stepper and current editor panel (template, personal, work, education, skills, summary).
- Right panel shows live preview (
PreviewPanel). Autosaves usinguseLocalAutosave.
src/components/Stepper.tsx- Displays steps and controls navigation.
- Validates the current section before moving forward.
src/components/TemplateGallery.tsx- Shows a grid of template thumbnails with filters (
ATSvsVisual). - Clicking a card sets
templateIdin state.
- Shows a grid of template thumbnails with filters (
src/components/PreviewPanel.tsx- Toggle between
inlineandprintablemodes. - Inline: renders the selected React template via
templatesMap[templateId]insideThemeProvider. - Printable: builds full A4 HTML with
buildPrintableHtml(cv, templateId)and loads it in an iframe. - Also includes
TemplateGalleryand export controls.
- Toggle between
src/components/ThemeProvider.tsx- Applies CSS variables from the selected
ColorTheme(getThemeCSS) to make templates/theme-aware.
- Applies CSS variables from the selected
src/components/ExportControls.tsx- Buttons to kick off export to the server, track job status, and download the result.
Editors (data entry)
- PersonalEditor (path:
src/editors/PersonalEditor.tsx)- Collects name, contact details, location, links; updates
cv.personal.
- Collects name, contact details, location, links; updates
src/editors/WorkEditor.tsx- CRUD jobs, reorder, and manage bullets with length guidance.
- Validates required fields (
title,company,startDate) and prevents more than 6 bullets.
src/editors/EducationEditor.tsx- CRUD education entries; degree, school, dates, optional notes; reorder.
src/editors/SkillsEditor.tsx- Add skills with optional level; reorder skills; chips-style display.
src/editors/SummaryEditor.tsx- Rich text editor (Tiptap) with bold/italic and lists, character count, and sanitization.
Templates (inline preview)
src/templates/ATSTemplate.tsx- ATS-friendly, serif typography, underlined section headings.
- Sections: Header (centered), Professional Summary, Professional Experience (company, title, date range, bullets), Education, Skills, Languages.
- Uses helpers:
escapeText,sanitizeHtml,formatDateand CSS variables (var(--theme-*)).
src/templates/ClassicTemplate.tsx- Clean, traditional layout; accent color on headings; chip-style skills.
src/templates/ModernTemplate.tsx- Visual layout with colored header and two-column sections; optional photo.
src/templates/MinimalTemplate.tsx- Bare, whitespace-forward layout; serif headings; simple chips for skills and languages.
src/templates/TimelineTemplate.tsx- Visual timeline with a left sidebar for contact/skills and right-column timeline content.
src/templates/registry.ts- Declares
templatesRegistryandtemplatesMap(id → component, name, category, thumbnail). - Thumbnails can be static SVGs in
src/assets/templatesor dynamically generated.
- Declares
Thumbnails & Theming
src/utils/thumbnailGenerator.ts- Contains
baseSVGswith SVG blueprints per template using placeholders like{{primary}},{{text}},{{background}}. - Generates data URLs by replacing placeholders with the active theme palette.
- ATS thumbnail is sectioned and theme-aware to reflect the template’s look.
- Contains
Printable HTML & Export
- Client wrapper:
src/utils/printable.ts- Sanitizes summary and calls
shared-printable/buildPrintableHtml.jsto build full A4 HTML.
- Sanitizes summary and calls
- Shared builder:
shared-printable/buildPrintableHtml.js- Returns a complete HTML document with print-safe styles; supports multiple templates (ATS, Timeline).
- Server:
cv-export-server/server.jsPOST /export/pdfconsumes CV JSON, builds HTML, renders PDF via Puppeteer, and streams it back.- You can later move this to a worker or serverless with proper Chromium setup.
Theming (colors)
src/types/colors.ts(referenced via imports in the app)- Defines
ColorThemeand named palettes. getThemeCSS(theme)returns CSS variables (--theme-primary,--theme-secondary,--theme-text, etc.) applied byThemeProvider.- Templates and thumbnails consume these via
var(--theme-*)or placeholder substitution.
- Defines
Typical Development Tasks
- Add a new template
- Create
src/templates/MyTemplate.tsx(React component accepting{ cv }). - Add a thumbnail (static SVG under
src/assets/templatesor abaseSVGs.myTemplateentry inthumbnailGenerator.ts). - Register in
src/templates/registry.tswith id, name, category, and component. - If printable output differs significantly, add a variant to
shared-printable/buildPrintableHtml.jskeyed bytemplateId.
- Create
- Add a new field to the CV model
- Update
cvSchema.ts(Zod schema and default values). - Extend relevant editors and templates to read/write/render the field.
- Update
buildPrintableHtml.jsif needed for print parity.
- Update
- Add a new color theme
- Extend palettes in
types/colors.ts. - The rest of the app picks up the theme via
ThemeProviderand SVG thumbnail generator.
- Extend palettes in
Commands
- Client:
npm run dev,npm run build,npm run preview,npm run lint(insidecv-engine). - Server:
npm run dev,npm run start(insidecv-export-server).
Testing & Quality
- Unit/integration: suggested in docs (Vitest/Jest, React Testing Library).
- Visual regression: consider Percy/Chromatic for templates and printable HTML screenshots.
- E2E: Playwright covering editor flow, preview, export.
- Linting: ESLint config in
cv-engine/eslint.config.jswith TS + React rules.
Troubleshooting
- Dev server not reachable: ensure
npm run devis running and check for port conflicts. - Summary HTML issues: verify
sanitizeHtmlusage and allowed tags. - PDF export errors: confirm the server is running; check console logs; verify Puppeteer can launch (server or CI environment may need Chromium flags).
- Template thumbnails: if colors don’t change, verify placeholders in
thumbnailGenerator.tsand theme CSS variables are applied.
Contribution Guidelines
- Keep changes scoped; follow existing code style.
- Maintain inline ↔ printable parity when modifying templates.
- Sanitize and validate user-provided content; avoid introducing unsafe HTML.
- Prefer small, focused PRs with clear rationale.
Appendix: Original Development Guide
The original, more detailed plan and background documentation follows below for deeper context.
This guide turns the CV editor engine plan into concrete, incremental tasks you can implement. It defines scope, architecture, data model, workflows, templates, preview/export, validation, accessibility, security, performance, testing, and milestones with acceptance criteria.
Objectives
- Deliver a fast, accessible CV builder with live preview and high-quality PDF export.
- Keep data structured and templates decoupled for ATS-friendly and visual layouts.
- Ensure printable parity between inline preview and final PDF output.
Tech Stack
- Client: React, TailwindCSS, Zustand, TanStack Query, Tiptap (+ DOMPurify)
- Server: Node.js + Express for export, Puppeteer/Playwright for PDF rendering
- Validation: Zod (or Yup)
- Testing: Vitest/Jest, React Testing Library, Playwright for E2E
Repo Layout (proposed)
src/
components/ # UI building blocks (forms, lists, stepper)
editors/ # Step editors (Heading, Work, Education, Skills, Summary)
templates/ # Inline templates (ATS, Visual) + shared formatters
printable/ # Printable HTML builder and styles (@page, A4)
store/ # Zustand store, mutations and selectors
services/ # API calls (save/load/export) and helpers
utils/ # escapeHtml, sanitizers, validators, formatters
hooks/ # autosave, debounced change tracking, accessibility
server/
export/ # Express route for PDF export
tests/ # Unit, integration, E2E, visual regression
Conventions
- Strictly sanitize user HTML via
DOMPurifyand escape all text in templates. - Keep templates pure: accept CV JSON and return JSX/HTML; no side effects.
- Scope styles via CSS modules or Tailwind; avoid global leakage.
- Maintain preview ↔ printable parity using shared formatters and data.
Data Model (Zod-style)
const CvSchema = z.object({
personal: z.object({
firstName: z.string().min(1),
lastName: z.string().min(1),
email: z.string().email(),
phone: z.string().optional(),
street: z.string().optional(),
city: z.string().optional(),
country: z.string().optional(),
postcode: z.string().optional(),
links: z.array(z.object({ label: z.string(), url: z.string().url() })).optional(),
extras: z.array(z.string()).optional(), // e.g., LinkedIn, Website, Driving licence
}),
summary: z.string().max(600).optional(),
work: z.array(z.object({
id: z.string(),
title: z.string().min(1),
company: z.string().min(1),
location: z.string().optional(),
startDate: z.string(), // ISO or YYYY-MM
endDate: z.string().optional(),
bullets: z.array(z.string().max(160)).max(6),
employmentType: z.enum(['full_time','part_time','contract','intern','freelance']).optional(),
})),
education: z.array(z.object({
id: z.string(),
degree: z.string().min(1),
school: z.string().min(1),
startDate: z.string(),
endDate: z.string().optional(),
notes: z.string().optional(),
})),
skills: z.array(z.object({ name: z.string(), level: z.enum(['Beginner','Intermediate','Advanced']).optional() })),
languages: z.array(z.object({ name: z.string(), level: z.enum(['Basic','Conversational','Fluent','Native']) })).optional(),
certifications: z.array(z.object({ name: z.string(), issuer: z.string().optional(), date: z.string().optional() })).optional(),
templateId: z.string().default('ats'),
});
Validation rules
- Required:
firstName,lastName,email; at least one ofworkoreducation. - Bullets: 1–6 per role; each ≤160 chars; avoid trailing punctuation.
- Summary: ≤600 chars; discourage emojis; sanitize on save/render.
- Links: normalize to
https://; restrict protocols tohttp,https.
Editor Flow
- Stepper: Heading → Work → Education → Skills → Summary → Finalize
- Each step runs field validation on blur and pre-navigation; critical errors block Next (e.g., invalid email).
- Drag-and-drop reorder for Work and Skills; inline error messages with
aria-describedby.
Template System
- Template API:
{ id, name, renderer(cv), thumbnail? } - Shared formatters:
formatDate,formatLocation,escapeText,sanitizeHtml. - Templates
- ATS: semantic lists, neutral styles, no sidebars, simple typographic accents.
- Visual: sidebar contact, accented headings, subtle color blocks (similar to sample CV).
Inline Preview
- Render chosen template directly from CV JSON; debounce heavy updates.
- Memoize template components; isolate styles via Tailwind scopes or modules.
Printable Preview (Iframe)
buildPrintableHtml(cv)returns a complete HTML document:<html>with embedded CSS:@page { size: A4; margin: 18mm }and print-safe font stack.- No external scripts; all assets inline; background printing enabled.
- Load via
iframe srcDoc; maintain parity with inline by sharing formatters and sanitized summary.
Export Service
- Express
POST /export/pdf- Input: CV JSON (validated on server)
- Compose:
buildPrintableHtml(cv) - Render: Puppeteer
page.setContent(html, { waitUntil: 'networkidle0' }),page.pdf({ format: 'A4', printBackground: true }) - Output:
application/pdfstream with filenamecv-<lastName>-<YYYYMMDD>.pdf
- Client: Export button calls endpoint; stream download; show progress & errors.
Persistence & Autosave
- Autosave: debounce 1–2s after idle; mark dirty while saving; show last-saved timestamp.
- API
GET /cv/:id→ load draftPUT /cv/:id→ save draftPOST /export/pdf→ export
- Store integration via TanStack Query mutations and selectors in Zustand.
Accessibility
- Labels with
htmlFor; inputs witharia-describedbyfor errors; ensure WCAG AA contrast. - Keyboard navigation for stepper and list CRUD; focus management after add/delete.
- Templates use semantic elements:
header,section,ul, avoiding purely presentational markup.
Security
- Sanitize summary on input and before render; escape all template-bound text.
- Restrict allowed URL protocols; validate
mailto:only for email display links (never user-provided). - No inline scripts; printable builder is self-contained HTML/CSS.
Performance
- Debounce preview/autosave; throttle rapid list operations.
- Lazy-load noncritical components (template gallery); memoize heavy subtrees.
- Avoid large assets; prefer CSS accents; keep DOM size lean.
Testing Strategy
- Unit
escapeHtml,sanitizeHtml, formatters, Zod validators, store selectors
- Integration
- Inline ↔ printable parity across templates
- Autosave flows and error handling
- Export success/failure
- E2E (Playwright)
- Create CV → navigate steps → validate errors → switch templates → preview → export PDF
- Visual Regression
- Printable HTML screenshots per template and key data permutations
Error Handling
- Centralized error boundary; step-level error summaries; non-blocking for non-critical issues.
- Graceful server errors with retry; maintain local draft if save/export fails.
Milestones & Tasks
M1 — Foundations
Tasks
- Initialize client app and Tailwind; set up routing.
- Create
CvSchema, types, and helpers (normalization, IDs). - Implement Zustand store: CV draft, active step, templateId, UI flags.
- Build Stepper and Heading editor form with validation.
- Implement ATS inline template and preview panel. Acceptance
- User can enter heading details with inline validation and see preview update.
- Data stored in Zustand; template renders from CV JSON.
M2 — Content Steps
Tasks
- Work editor: CRUD, reorder, bullet editor with length guidance.
- Education editor: CRUD; reuse shared list components.
- Skills editor: tag input + quick-add chips; optional level. Acceptance
- Add/update/delete/reorder across steps; errors appear in-place; preview updates live.
M3 — Summary & Printable
Tasks
- Integrate Tiptap (StarterKit + Placeholder); toolbar with basic marks.
- Sanitize output; store HTML + optional JSON.
- Implement
buildPrintableHtml(cv)and iframe preview with A4 styles. Acceptance - Printable preview matches inline layout (content, spacing, formatting); summary sanitized.
M4 — Persistence
Tasks
- Wire autosave with debounced mutations; load on mount.
- Add last-saved timestamp and dirty state.
- Handle save/load errors gracefully. Acceptance
- Draft persists across reloads; autosave feels responsive and safe.
M5 — Export
Tasks
- Server: Express route for
POST /export/pdfusing Puppeteer. - Client: Export button; download with progress; error UI. Acceptance
- PDF export produces a visually faithful A4 document with correct filename.
M6 — Templates
Tasks
- Add Visual template; gallery picker with thumbnails and filters (ATS vs Visual).
- Persist
templateId; ensure both inline and printable support switching. Acceptance - Users switch templates instantly; parity holds across preview and PDF.
M7 — QA & Polish
Tasks
- Accessibility pass; performance tuning; code cleanup.
- Unit, integration, E2E, visual tests; CI workflow. Acceptance
- Tests green; meets accessibility and performance targets.
M8 — Enhancements (optional)
Tasks
- Import structured CV (JSON); version history; i18n; AI assist. Acceptance
- Optional features are behind flags and do not regress core flows.
Setup (placeholder)
Commands to run once you scaffold the app:
npm create vite@latest cv-engine -- --template react-ts
cd cv-engine
npm i tailwindcss @tiptap/react @tiptap/starter-kit dompurify zustand @tanstack/react-query zod puppeteer express
npx tailwindcss init -p
Adapt paths to your chosen structure. This repo currently contains planning documents only.
Acceptance Criteria Checklist (global)
- Structured CV JSON validated end-to-end
- Live inline preview with ATS template by M1
- Printable HTML parity by M3 (layout, spacing, typography, sanitization)
- Autosave with clear feedback by M4
- Reliable PDF export by M5
- At least two templates with instant switching by M6
- Accessibility AA and test coverage by M7
User Journey
- User starts by selecting a template from the gallery.
- They then navigate through the stepper to fill out their personal information, work experience, education, skills, and summary.
- Photo upload should only show if the selected template has a placeholder for a profile photo.
- After completing the steps, they can preview their CV in real-time with the selected template.
- If they are satisfied with the preview, they can export their CV as a PDF file.
Glossary
- ATS: Applicant Tracking System; favors semantic, minimal styling.
- Parity: Inline preview and exported PDF display equivalent content/layout.
- Printable builder: Function that returns a self-contained HTML document for PDF rendering.
Use this README as the source of truth during implementation. Update sections as architecture evolves, but keep template parity, sanitization, and accessibility as non-negotiable constraints.