- Implement new timeline template with left sidebar and vertical timeline design - Add support for headline and reference lines in personal editor - Update printable HTML builder to support timeline template styling - Include timeline template thumbnail and registry entry
CV Editor Engine — Development Guide
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.
Description
Languages
TypeScript
84.9%
JavaScript
14.4%
CSS
0.5%
HTML
0.2%