| name | generate-resume |
| description | Generate a print-optimized, ATS-friendly single-page resume PDF from portfolio content. Use when user wants to create or regenerate their resume. (project) |
Resume Generator
Trigger phrases: "resume", "generate resume", "PDF", "export resume", "make resume"
Critical Files
| File | Purpose |
|---|---|
src/pages/ResumePage.tsx |
React component for print-optimized layout |
src/pages/ResumePage.css |
Print/screen styles with theme isolation |
scripts/generate-resume.ts |
Puppeteer PDF generation script |
content/profile.yaml |
Name, email, location, tagline, stats |
content/experience/index.yaml |
Jobs with highlights |
content/skills/index.yaml |
Categorized skills |
public/resume.pdf |
Default output file |
Design Principles
1. Never Cut Experience
Critical: Include ALL jobs from content/experience/index.yaml. Missing experience (especially major companies) damages credibility more than a slightly longer resume.
- If page is too long → reduce highlights per job, NOT number of jobs
- Set
MAX_JOBSto match total jobs in experience file - Every role adds signal; cutting roles removes career trajectory
2. Metrics Over Descriptions
Highlights should follow Action → Outcome format with quantified results:
❌ "Managed product development"
✅ "Shipped Advanced API from 0→1: 17 methods serving 1M+ daily requests"
3. ATS-First Design
- Plain text (no images, icons, or complex layouts)
- Standard fonts: Georgia, Times New Roman (serif) or Arial, Helvetica (sans)
- Clear section headers: Header, Summary, Experience, Skills
- All text must be selectable/copyable
4. Single Page Target
One-page resumes get 2x more callbacks. Fit content by:
- Reducing highlights per job (2-3 is sufficient)
- Tightening typography (9-10pt base)
- Never by cutting jobs
Format Guidelines
Layout Constants (ResumePage.tsx)
const RESUME_CONFIG = {
SUMMARY_SKILL_CATEGORIES: 2, // Categories in impact summary
SKILLS_PER_CATEGORY: 3, // Skills per category in summary
SUMMARY_COMPANIES: 4, // Companies to list in summary
MAX_JOBS: 6, // Include ALL jobs - adjust to match your experience
MAX_HIGHLIGHTS_PER_JOB: 3, // Bullets per job (action → outcome format)
};
Adjust MAX_JOBS to match the total number of jobs in your experience file.
Resume Structure
- Header: Name / Current Role + contact info (email, location)
- Impact Summary: One-liner tagline + top skills + notable companies + key stats
- Professional Experience: Each job with company, role, period, location, and bullets
- Skills: 2-column grid organized by category
Skills Section Layout
Skills are displayed in a 2-column grid with category names in bold:
Category A: skill1, skill2, skill3... | Category B: skill1, skill2...
Category C: skill1, skill2, skill3... | Category D: skill1, skill2...
Categories are pulled from content/skills/index.yaml. Common category structures:
For Engineers:
- Languages & Frameworks, Infrastructure, Databases, Tools
For Product Managers:
- Domain Expertise, Product Leadership, Technical Execution, Tools
For Designers:
- Design Tools, Research Methods, Platforms, Collaboration
Typography (ResumePage.css)
- Font: Georgia / Times New Roman (ATS-safe serif)
- Base size: 9.5pt
- Line height: 1.35
- Page: Letter size (8.5in × 11in)
- Margins: 0.4in top/bottom, 0.5in left/right
Known Issues & Fixes
1. Black Box at Bottom of PDF
Cause: ThemeProvider applies dark background (#08080a) to body/#root, which bleeds into the PDF outside the .resume-page container.
Fix: CSS must override parent elements in @media print:
@media print {
html,
body,
#root {
background: white !important;
min-height: auto !important;
padding: 0 !important;
margin: 0 !important;
}
}
2. PDF Exceeds One Page
Fix: Reduce MAX_HIGHLIGHTS_PER_JOB in ResumePage.tsx. Do NOT reduce MAX_JOBS - missing experience is worse than a slightly longer resume.
3. Missing Experience/Jobs
Cause: MAX_JOBS was set too low, cutting off important companies.
Fix: Always set MAX_JOBS to the total number of jobs in content/experience/index.yaml. Count your jobs and update the config.
4. Yellow/Colored Underline on Role Title
Fix: Ensure .resume-role-highlight has no border-bottom or background-color.
5. Theme Colors Bleeding Through
Fix: Reset all children with:
.resume-page * {
background: transparent;
color: inherit;
}
6. Fonts Not Loading in PDF
Fix: The script waits for document.fonts.ready + 500ms buffer. If fonts still don't load, increase POST_FONT_BUFFER_MS in generate-resume.ts.
Generation Workflow
Prerequisites
- Dev server running:
npm run dev - Content files populated:
content/profile.yaml(name, email, location, tagline, stats)content/experience/index.yaml(jobs with highlights)content/skills/index.yaml(categorized skills)
Steps
- Validate content:
npm run validate - Preview at http://localhost:5173/resume
- Verify single-page fit (no vertical scrollbar)
- Check all experience is included
- Generate:
npm run generate:resume
- Verify output at
public/resume.pdf
Custom Filename
npm run generate:resume -- --name "Jane Smith"
# Output: public/jane-smith.pdf
Custom URL
npm run generate:resume -- --url http://localhost:3000
# Uses different dev server port
Troubleshooting
| Issue | Solution |
|---|---|
| "net::ERR_CONNECTION_REFUSED" | Start dev server: npm run dev |
| PDF is blank | Check browser console at /resume for React errors |
| Wrong content displayed | Verify YAML files, run npm run validate |
| Fonts look wrong | Increase POST_FONT_BUFFER_MS in script |
| Two pages instead of one | Reduce MAX_HIGHLIGHTS_PER_JOB, NOT MAX_JOBS |
| Dark background in PDF | Verify print CSS overrides html/body/#root |
| Jobs missing | Increase MAX_JOBS to match total in experience file |
| Skills look cramped | Reduce number of skills per category or categories |
After Generation
Update hero CTA in content/profile.yaml to link to the resume:
hero:
cta:
secondary:
label: "Download Resume"
href: "/resume.pdf"
Technical Details
Puppeteer Configuration (generate-resume.ts)
const PAGE_DIMENSIONS = {
LETTER: {
WIDTH_PX: 816, // 8.5" at 96dpi
HEIGHT_PX: 1056, // 11" at 96dpi
},
};
const TIMING = {
POST_FONT_BUFFER_MS: 500, // Wait after fonts.ready
PAGE_LOAD_TIMEOUT_MS: 30000, // Navigation timeout
};
PDF Export Settings
await page.pdf({
path: output,
format: 'letter',
printBackground: true,
margin: {
top: '0.4in',
right: '0.5in',
bottom: '0.4in',
left: '0.5in',
},
preferCSSPageSize: true,
});
Content Sources
The resume pulls data from these content files:
| Data | Source | Field |
|---|---|---|
| Name | profile.yaml |
name |
profile.yaml |
email |
|
| Location | profile.yaml |
location |
| Tagline | profile.yaml |
about.tagline |
| Stats | profile.yaml |
about.stats[] |
| Jobs | experience/index.yaml |
jobs[] |
| Skills | skills/index.yaml |
categories[].skills[] |
Quality Checklist
Before Generating
- Content YAML files pass validation (
npm run validate) - Preview at /resume shows correct info
- ALL jobs are included (count them!)
- Resume fits on single page (no scrollbar)
- All highlights follow "Action → Outcome" format
- Metrics are quantified (%, $, numbers)
After Generating
- Open PDF and verify visual appearance
- Check all text is selectable (ATS-friendly)
- Verify no dark backgrounds or visual artifacts
- Confirm all companies/roles are present
- Test PDF in an ATS simulator if available