A resume builder that edits inline against a live A4 preview. Fifteen templates, fifty-five palettes, thirty fonts, a Europass-grade data model, and a multi-page PDF export β all client-side, with your data kept in your own browser.
Three tabs on the left, the page you are actually exporting on the right.
| Tab | What it holds |
|---|---|
| Content | Every resume section as a collapsible form. Edits land in the preview as you type. |
| Design | Template gallery, palette grid, custom colors, font, and the dark-resume switch. |
| Export | Language variants, JSON import/export, the annotated starter file, and a reset. |
On phones and tablets the two panes cannot sit side by side, so a bottom bar switches between Content, Design, Export, and Preview, each taking the full viewport. The preview scales to fit and has its own zoom control.
There is no save button. Every change is written to localStorage shortly after you stop typing, and the whole document can be exported as JSON at any time.
- 15 templates β Modern, Classic, Minimal, Split, Executive, Creative, Compact, Elegant, Timeline, Portfolio, Editorial, Bold, Banner, Geometric, Neo
- Live inline editing β no modal, no save step; the preview is the source of truth
- Rich data model β experience, education, skills, languages, projects, certifications, awards, publications, training, volunteering, interests, references, and your own custom sections
- Structured dates β a month picker per entry, formatted per locale, with an "I am still here" switch
- Achievement bullets β a bullet list per role, project, or qualification, alongside the prose description
- Multi-page PDF β content taller than one page flows onto further pages instead of being cropped; page breaks are marked in the preview
- Clickable links in the PDF β contact and project URLs stay live in the export
- 55 palettes + custom colors β grouped into classic and bold, or dial in your own six values
- 30 Google Fonts β loaded on demand
- Dark mode β for the app and, separately, for the resume itself
- Multilingual β UI in English and Italian, with an independent resume version per language
- Photo upload β crop and position in-app; stored inline so the PDF export never breaks on CORS
- Accessible β native disclosure widgets, keyboard navigation, ARIA attributes, focus management
The document is a JSON object keyed by locale. Only name, jobTitle, and contact are required β every other field and section is optional, and unknown keys are ignored on import.
{
"en-US": {
"name": "Jane Doe",
"jobTitle": "Full-Stack Developer",
"contact": [{ "type": "email", "value": "jane.doe@example.com" }],
"experience": [
{
"position": "Senior Developer",
"company": "Acme Corp.",
"location": "Berlin, Germany",
"employmentType": "Full-time",
"startDate": "2022-01",
"isCurrent": true,
"description": "One or two sentences framing the role.",
"highlights": ["A quantified achievement."],
"technologies": ["React", "TypeScript"]
}
]
}
}Dates are stored as YYYY-MM or YYYY and reformatted for the resume's locale; anything else is rendered verbatim, so imported free text like "Summer 2019" survives. Set isCurrent instead of an endDate for ongoing entries.
Export β Starter JSON downloads an annotated file with every supported field filled in. Files written by earlier versions still load: the legacy duration string is split into startDate/endDate, and the old fixed contact object is converted to the ContactLink[] form.
.
βββ .husky/ # Git hooks (pre-commit)
βββ resume/ # Main application
β βββ public/
β βββ src/
β β βββ assets/
β β β βββ icons/
β β β βββ template/data.json # Bundled example resume
β β βββ components/
β β β βββ atoms/ # Button, CollapsibleSection, MonthInput, StringListInput, β¦
β β β βββ molecules/ # EntryList, EntryFields, TemplateGallery, ResumeExtraSections, β¦
β β β βββ organisms/ # Navbar, Workspace, ContentPanel, DesignPanel, ExportPanel, PreviewStage
β β β βββ templates/ # 15 resume templates
β β βββ context/ # Theme context (dark mode)
β β βββ data/ # Palettes, fonts, templates, contact types, section registry
β β βββ i18n/ # i18next localization (en-US, it-IT)
β β βββ pages/ # ResumeBuilderPage
β β βββ styles/ # SCSS base (tokens, mixins, variables)
β β βββ utils/ # Dates, PDF generation, validation, helpers
β β βββ types.ts # Shared TypeScript types
β βββ eslint.config.js
β βββ tsconfig.json
β βββ vite.config.ts
β βββ package.json
βββ README.md
Repeatable sections are declared once in src/data/resumeSections.ts β an id, a title key, a blank entry, and a list of typed fields. That single entry gives you the editor form, the add/remove/reorder controls, and the collapsed header. Rendering comes from ResumeExtraSections, which every template appends, so a new section needs no changes in any of the fifteen template components.
-
Clone the repository:
git clone https://github.com/yourusername/Resume.git
-
Navigate to the
resumefolder:cd Resume/resume -
Install the dependencies:
pnpm install
-
Run the app:
pnpm dev
-
Navigate to the
resumefolder:cd resume -
Run the build command:
pnpm build
-
Preview the production build:
pnpm preview
.github/workflows/ci.yml runs on every push to main, every pull request, and on demand. It checks formatting, lints, runs the date self-check, then type-checks and builds β the same four commands you can run locally.
CI does not deploy. Publishing stays manual: pnpm deploygh builds and pushes dist/ to the gh-pages branch, which GitHub Pages serves at the custom domain from resume/public/CNAME.
.github/dependabot.yml opens grouped weekly dependency PRs (and monthly ones for the actions themselves), which CI then gates. Two majors are pinned back on purpose:
- ESLint 10 β
eslint-plugin-react@7.37.5crashes on it (contextOrFilename.getFilename is not a function). - TypeScript 7 β
typescript-eslintdoes not support the TS 7 compiler yet;tscpasses butpnpm lintrefuses to run.
pnpm audit reports one high-severity finding it cannot fix, so pnpm-workspace.yaml ignores it under auditConfig.ignoreGhsas with the reasoning inline:
GHSA-mh99-v99m-4gvg β a DoS in brace-expansion. The vulnerable 1.x copy arrives only through minimatch@3, which both eslint@9 and eslint-plugin-react@7.37.5 hard-depend on. No patched 1.x exists (1.1.16 is the head of maintenance-v1), and brace-expansion@5 changed its CommonJS export from a function to an object, so forcing it via an override makes minimatch@3 throw. It is dev-only: pnpm audit --prod is clean and the package never reaches dist/. The ignore comes out when eslint-plugin-react supports ESLint 10, which pulls minimatch@10 and with it the patched brace-expansion@5.
| Script | Description |
|---|---|
pnpm dev |
Start development server |
pnpm build |
Type-check and build for production |
pnpm preview |
Preview production build |
pnpm lint |
Run ESLint |
pnpm lint:fix |
Run ESLint with auto-fix |
pnpm format |
Format code with Prettier |
pnpm format:check |
Verify formatting without writing |
pnpm selfcheck |
Run the date parsing/formatting assertions |
pnpm deploygh |
Build and publish to the gh-pages branch |