npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

cv-pdf-gen

v0.2.0

Published

**A framework for building 1-page CV PDFs. Edit YAML. Pick a theme. Print to PDF. No React knowledge required.**

Downloads

273

Readme

cv-pdf-gen

A framework for building 1-page CV PDFs. Edit YAML. Pick a theme. Print to PDF. No React knowledge required.

npm version license


Quick Start

npm create cv-gen my-cv
cd my-cv
npm install
npm run dev

Open http://localhost:3000, edit your content, then press Cmd+P (or click Export PDF) to save your CV as a PDF.


How It Works

  • Content — edit content/cv.en.yaml with your personal info, experience, skills, and education
  • Theme — choose or customize a CSS theme in themes/
  • Export — use the browser's print dialog to save as PDF

That's it. The framework handles the layout, typography, and print formatting for you.


Project Structure

After running npm create cv-gen, your project looks like this:

my-cv/
├── content/
│   └── cv.en.yaml          # Your CV content
├── themes/
│   ├── default.css         # Theme colors and fonts
│   └── default.layout.yaml # Column layout and decorations (optional)
├── decorations/            # SVG decoration files
├── plugins/                # Custom plugins (optional)
├── public/
│   └── photo.jpg           # Your portrait photo
└── src/
    └── main.tsx            # Entry point (pre-configured, no need to edit)

Content (YAML)

All your CV data lives in content/cv.en.yaml. Here's a full example:

# Section order — rearrange to reorder sections on the page
blocks:
  - header
  - experience
  - technical_skills
  - soft_skills
  - education
  - languages

# Column layout
layout:
  columns: 2
  left:
    - experience
  right:
    - technical_skills
    - soft_skills
    - education
    - languages

# Section heading labels (customize for any language)
labels:
  workExperience: "WORK EXPERIENCE"
  technicalSkills: "TECHNICAL SKILLS"
  softSkills: "SOFT SKILLS"
  education: "EDUCATION"
  languages: "LANGUAGES"
  responsibilities: "Responsibilities"
  achievements: "Achievements/Tasks"

# Personal info
personal:
  name: "Jane Smith"
  title: "Software Engineer"
  photo: "photo.jpg"           # Place in public/ — omit if you don't want a photo
  summary: "Brief professional summary that appears below your name."
  contact:
    email: "[email protected]"
    phone: "+1 555 000 0000"
    location: "Berlin, Germany"
    linkedin: "linkedin.com/in/janesmith"

# Work experience
experience:
  - role: "Senior Engineer"
    company: "Acme Corp"
    dates: "01/2022 - Present"
    location: "Berlin, Germany (hybrid)"
    description: "Short description of the company."
    type: "achievements"       # "achievements" or "responsibilities"
    items:
      - "Led migration of monolith to microservices, reducing deploy time by 40%."
      - "Mentored a team of 4 junior engineers."

# Technical skills — displayed as category/value pairs
technical_skills:
  - category: "Languages"
    items: "Python, TypeScript, Go, SQL"
  - category: "Frameworks"
    items: "FastAPI, React, Django"
  - category: "Infrastructure"
    items: "Docker, Kubernetes, AWS, GitHub Actions"

# Soft skills — displayed as a comma-separated list
soft_skills:
  - "Collaborative"
  - "Pragmatic"
  - "Curious"

# Education
education:
  - title: "MSc Computer Science — University of Berlin"
    description: "Thesis: Distributed Systems at Scale"
    dates: "2016 - 2018"

# Languages — level is 1 (basic) to 5 (native)
languages:
  - name: "English"
    level: 5
  - name: "German"
    level: 4

Themes

Three themes are included out of the box:

| Theme | Description | |-------|-------------| | default | Clean two-column layout with a teal accent | | sidebar | Dark sidebar (30/70 split) with accent background | | minimal | Serif fonts, minimalist black and white |

The dev server shows a tab for each theme so you can compare them side by side.

Customizing a theme

Each theme is a plain CSS file that sets custom properties. Override any of these in your theme file:

:root {
  --cv-accent: #2b7a78;          /* Primary color */
  --cv-accent-light: #e0f2f1;    /* Light accent (backgrounds, borders) */
  --cv-text: #333333;            /* Body text */
  --cv-text-light: #666666;      /* Secondary text */
  --cv-bg: #ffffff;              /* Page background */
  --cv-font: 'Helvetica Neue', Arial, sans-serif;
  --cv-font-size-base: 9pt;
  --cv-font-size-name: 28pt;
  --cv-font-size-section: 13pt;
  --cv-left-column: 58%;         /* Left column width (right = remainder) */
}

To create a new theme, add a .css file to the themes/ directory — it's picked up automatically.

Theme layout

Themes can have a companion layout YAML file (themes/default.layout.yaml) to override the column layout and add decorations independently of the content YAML:

layout:
  columns: 2
  left:
    - experience
  right:
    - technical_skills
    - education
    - languages

Decorations

Drop any SVG into the decorations/ folder and reference it from a layout YAML file:

layout:
  columns: 2
  left: [experience]
  right: [technical_skills, education, languages]
  decorations:
    - svg: circles-cluster.svg
      position: top-right     # top-left | top-right | bottom-left | bottom-right
      width: 80mm
      height: 80mm
      opacity: 0.1
      color: "#2b7a78"        # Replaces currentColor in the SVG

Decorations are rendered as background images — they are fully print-safe and won't cause page overflow.


Plugins

Plugins let you extend or replace built-in blocks without touching the framework.

Place a plugin in plugins/my-plugin/index.tsx — it's auto-discovered at startup.

Inject content around items (hooks)

import { hooks } from "../../src/framework/plugin";
import type { Experience } from "../../src/framework/types";

// Extend the Experience type with your custom YAML fields
interface ExperienceWithBadges extends Experience {
  badges?: string[];
}

export default hooks<ExperienceWithBadges>("experience", {
  afterEach: ({ item }) =>
    item.badges?.length ? (
      <div className="badges">
        {item.badges.map((b) => (
          <span key={b} className="badge">{b}</span>
        ))}
      </div>
    ) : null,
});

Available hook points: before, after, beforeEach, afterEach.

Replace a block entirely (replace)

import { replace } from "../../src/framework/plugin";
import type { CVData } from "../../src/framework/types";

export default replace("languages", ({ data }: { data: CVData }) => (
  <div className="languages-custom">
    {data.languages.map((lang) => (
      <div key={lang.name}>
        <span>{lang.name}</span>
        <meter value={lang.level} min={0} max={5} />
      </div>
    ))}
  </div>
));

Two example plugins are included in the template: tech-badges (renders skill badges on experience items) and rating-bars (replaces the languages block with visual rating bars).


Multiple Languages

To add a French version of your CV, create content/cv.fr.yaml with translated content. The dev server will automatically show a tab for each language/theme combination.

content/
├── cv.en.yaml
└── cv.fr.yaml    ← auto-discovered

Exporting to PDF

  1. Click the Export PDF button in the top bar, or press Cmd+P / Ctrl+P
  2. In the print dialog, set Destination to "Save as PDF"
  3. Make sure "Print backgrounds" is enabled to preserve theme colors and decorations
  4. Set margins to None

A red dashed border appears in the dev preview if your content overflows the A4 page — use it as a guide to trim content before exporting.


License

MIT