<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ResumeCraft · Smart & Simple Resume Builder</title>
<style>
/* ===== SIMPLE, NATURAL, PROFESSIONAL PALETTE ===== */
:root {
--blue-deep: #1a365d;
--blue-mid: #2b5a8a;
--blue-soft: #d4e4f7;
--blue-pale: #eef5fc;
--orange-warm: #e67e22;
--orange-soft: #fdebd0;
--orange-light: #fff5eb;
--text-dark: #1a2b3c;
--text-mid: #3b5a7a;
--text-soft: #6b8299;
--white: #ffffff;
--shadow-sm: 0 2px 8px rgba(26, 54, 93, 0.06);
--shadow-md: 0 8px 24px -6px rgba(26, 54, 93, 0.14);
--shadow-lg: 0 24px 48px -16px rgba(26, 54, 93, 0.22);
--radius: 18px;
--radius-sm: 12px;
--font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: linear-gradient(135deg, #fdebd0 0%, #eef5fc 45%, #d4e4f7 100%);
font-family: var(--font);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 1.8rem;
line-height: 1.55;
color: var(--text-dark);
-webkit-font-smoothing: antialiased;
}
/* ===== MAIN CARD ===== */
.tool-card {
max-width: 1300px;
width: 100%;
background: rgba(255, 255, 255, 0.88);
backdrop-filter: blur(18px);
-webkit-backdrop-filter: blur(18px);
border-radius: var(--radius);
padding: 2.2rem 2.4rem;
box-shadow: var(--shadow-lg), 0 0 0 1px rgba(255, 255, 255, 0.6);
border: 1px solid rgba(255, 255, 255, 0.5);
}
/* ===== HEADER ===== */
.header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 2rem;
padding-bottom: 1.2rem;
border-bottom: 2px solid var(--blue-soft);
}
.header-left {
display: flex;
align-items: center;
gap: 14px;
}
.logo-mark {
width: 50px;
height: 50px;
background: linear-gradient(145deg, var(--blue-mid), var(--blue-deep));
color: #fff;
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.6rem;
font-weight: 700;
box-shadow: 0 8px 18px -6px rgba(43, 90, 138, 0.45);
transition: transform 0.25s ease;
}
.logo-mark:hover {
transform: rotate(-4deg) scale(1.03);
}
.header-left h1 {
font-size: 1.7rem;
font-weight: 800;
letter-spacing: -0.025em;
color: var(--blue-deep);
line-height: 1.15;
}
.header-left .tagline {
font-size: 0.78rem;
font-weight: 500;
color: var(--text-soft);
letter-spacing: 0.3px;
display: flex;
align-items: center;
gap: 6px;
}
.dot-green {
width: 7px;
height: 7px;
background: #27ae60;
border-radius: 50%;
display: inline-block;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { opacity: 0.6; transform: scale(1); }
50% { opacity: 1; transform: scale(1.15); }
100% { opacity: 0.6; transform: scale(1); }
}
.header-badge {
background: var(--orange-soft);
color: #a85c1a;
font-size: 0.72rem;
font-weight: 700;
padding: 0.4rem 1.1rem;
border-radius: 60px;
letter-spacing: 0.3px;
text-transform: uppercase;
border: 1px solid rgba(255, 255, 255, 0.8);
display: flex;
align-items: center;
gap: 7px;
box-shadow: var(--shadow-sm);
}
.header-badge .badge-dot {
width: 7px;
height: 7px;
background: var(--orange-warm);
border-radius: 50%;
display: inline-block;
}
/* ===== TWO COLUMN LAYOUT ===== */
.main-grid {
display: grid;
grid-template-columns: 1fr 1.75fr;
gap: 2rem;
align-items: start;
}
/* ===== LEFT: FORM PANEL ===== */
.form-panel {
background: var(--white);
border-radius: var(--radius-sm);
padding: 1.8rem 1.6rem;
border: 1px solid var(--blue-soft);
box-shadow: var(--shadow-sm);
}
.form-panel h2 {
font-size: 1.1rem;
font-weight: 700;
color: var(--blue-deep);
margin-bottom: 1.4rem;
display: flex;
align-items: center;
gap: 8px;
padding-bottom: 0.6rem;
border-bottom: 2px dashed var(--blue-soft);
}
.form-panel h2 span {
background: var(--orange-warm);
color: #fff;
width: 26px;
height: 26px;
border-radius: 8px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.8rem;
font-weight: 700;
}
.field {
margin-bottom: 1.2rem;
}
.field label {
display: block;
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-mid);
margin-bottom: 0.4rem;
}
.field input,
.field textarea {
width: 100%;
padding: 0.8rem 1rem;
font-family: var(--font);
font-size: 0.92rem;
background: var(--blue-pale);
border: 1.5px solid var(--blue-soft);
border-radius: 10px;
transition: all 0.2s ease;
color: var(--text-dark);
resize: vertical;
}
.field input:focus,
.field textarea:focus {
outline: none;
border-color: var(--blue-mid);
background: #fff;
box-shadow: 0 0 0 4px rgba(43, 90, 138, 0.1);
}
.field textarea {
min-height: 75px;
line-height: 1.5;
}
/* ===== ACTION BUTTONS ===== */
.action-group {
display: flex;
flex-direction: column;
gap: 0.7rem;
margin-top: 1.6rem;
}
.btn {
width: 100%;
padding: 0.9rem 1.3rem;
border: none;
border-radius: 12px;
font-weight: 600;
font-size: 0.9rem;
font-family: var(--font);
display: flex;
align-items: center;
justify-content: center;
gap: 9px;
cursor: pointer;
transition: all 0.2s ease;
letter-spacing: 0.2px;
}
.btn-primary {
background: var(--blue-mid);
color: #fff;
box-shadow: 0 8px 18px -6px rgba(43, 90, 138, 0.45);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.btn-primary:hover {
background: #1e4a73;
transform: translateY(-2px);
box-shadow: 0 12px 22px -8px rgba(43, 90, 138, 0.55);
}
.btn-primary:active {
transform: translateY(1px);
}
.btn-download {
background: var(--orange-warm);
color: #fff;
box-shadow: 0 8px 18px -6px rgba(230, 126, 34, 0.45);
border: 1px solid rgba(255, 255, 255, 0.15);
}
.btn-download:hover {
background: #c96a2e;
transform: translateY(-2px);
box-shadow: 0 12px 22px -8px rgba(230, 126, 34, 0.55);
}
.btn-download:active {
transform: translateY(1px);
}
.btn .icon {
font-size: 1.1rem;
line-height: 1;
}
.hint {
font-size: 0.7rem;
color: var(--text-soft);
text-align: center;
margin-top: 0.2rem;
}
/* ===== RIGHT: RESUME PREVIEW ===== */
.preview-panel {
background: var(--white);
border-radius: var(--radius-sm);
padding: 2rem 2.2rem;
border: 1px solid var(--blue-soft);
box-shadow: var(--shadow-md);
position: relative;
overflow: hidden;
min-height: 540px;
}
/* subtle decorative watermark */
.preview-panel::after {
content: "📄";
position: absolute;
right: -10px;
bottom: -20px;
font-size: 10rem;
opacity: 0.025;
transform: rotate(-12deg);
pointer-events: none;
}
.resume-content {
position: relative;
z-index: 2;
}
/* ===== RESUME HEADER ===== */
.resume-header {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: flex-start;
border-bottom: 3px solid var(--orange-soft);
padding-bottom: 1.2rem;
margin-bottom: 1.5rem;
}
.resume-name-block {
flex: 2;
min-width: 180px;
}
.resume-name {
font-size: 2.1rem;
font-weight: 800;
color: var(--blue-deep);
line-height: 1.2;
word-break: break-word;
letter-spacing: -0.025em;
}
.resume-title {
font-size: 1rem;
font-weight: 600;
color: var(--blue-mid);
background: var(--blue-soft);
display: inline-block;
padding: 0.25rem 1rem;
border-radius: 40px;
margin-top: 0.4rem;
letter-spacing: 0.2px;
}
.resume-contact {
flex: 1;
min-width: 170px;
text-align: right;
font-size: 0.85rem;
color: var(--text-mid);
line-height: 1.8;
background: var(--blue-pale);
padding: 0.7rem 1rem;
border-radius: 12px;
border: 1px solid var(--blue-soft);
}
.resume-contact span {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 6px;
}
/* ===== RESUME SECTIONS ===== */
.resume-section {
margin-top: 1.5rem;
}
.section-title {
font-size: 0.78rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--blue-deep);
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 0.9rem;
background: linear-gradient(to right, var(--orange-light), transparent);
padding: 0.3rem 0 0.3rem 0.9rem;
border-left: 5px solid var(--orange-warm);
border-radius: 0 30px 30px 0;
}
.resume-summary {
font-size: 0.94rem;
color: var(--text-dark);
line-height: 1.7;
background: var(--blue-pale);
padding: 1rem 1.2rem;
border-radius: 12px;
white-space: pre-wrap;
word-break: break-word;
border: 1px solid var(--blue-soft);
}
.skills-container {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
margin-top: 0.3rem;
}
.skill-badge {
background: var(--blue-pale);
color: var(--text-mid);
padding: 0.35rem 1rem;
border-radius: 30px;
font-size: 0.8rem;
font-weight: 600;
border: 1px solid var(--blue-soft);
transition: all 0.15s ease;
}
.skill-badge:hover {
background: var(--blue-soft);
transform: translateY(-1px);
}
.empty-message {
color: var(--text-soft);
font-style: italic;
font-size: 0.9rem;
background: var(--blue-pale);
padding: 0.7rem 1rem;
border-radius: 10px;
border: 1px dashed var(--blue-soft);
}
/* ===== ANIMATION ===== */
.preview-updated {
animation: fadeIn 0.35s ease-out;
}
@keyframes fadeIn {
0% { opacity: 0.7; transform: translateY(5px); }
100% { opacity: 1; transform: translateY(0); }
}
/* ===== RESPONSIVE ===== */
@media (max-width: 900px) {
.main-grid {
grid-template-columns: 1fr;
gap: 1.8rem;
}
.tool-card {
padding: 1.6rem;
}
.resume-header {
flex-direction: column;
gap: 0.8rem;
}
.resume-contact {
text-align: left;
width: 100%;
}
.resume-contact span {
justify-content: flex-start;
}
}
@media (max-width: 480px) {
body { padding: 0.8rem; }
.tool-card { padding: 1.2rem; }
.header-left h1 { font-size: 1.4rem; }
.logo-mark { width: 40px; height: 40px; font-size: 1.3rem; }
.resume-name { font-size: 1.7rem; }
.preview-panel { padding: 1.2rem; }
.form-panel { padding: 1.2rem; }
}
</style>
</head>
<body>
<div class="tool-card">
<!-- ===== HEADER ===== -->
<header class="header">
<div class="header-left">
<div class="logo-mark">R</div>
<div>
<h1>ResumeCraft</h1>
<div class="tagline">
<span class="dot-green"></span> smart · simple · professional
</div>
</div>
</div>
<div class="header-badge">
<span class="badge-dot"></span> free forever
</div>
</header>
<!-- ===== MAIN GRID ===== -->
<div class="main-grid">
<!-- LEFT: FORM -->
<section class="form-panel">
<h2><span>1</span> Your details</h2>
<div class="field">
<label for="fullName">Full name</label>
<input type="text" id="fullName" value="Jordan Lee" placeholder="Jordan Lee">
</div>
<div class="field">
<label for="jobTitle">Job title</label>
<input type="text" id="jobTitle" value="Senior Software Engineer" placeholder="Senior Software Engineer">
</div>
<div class="field">
<label for="email">Email</label>
<input type="email" id="email" value="jordan.lee@example.com" placeholder="jordan@example.com">
</div>
<div class="field">
<label for="phone">Phone</label>
<input type="tel" id="phone" value="+1 (555) 123-4567" placeholder="+1 (555) 123-4567">
</div>
<div class="field">
<label for="summary">Professional summary</label>
<textarea id="summary" rows="4">Results-driven software engineer with 8+ years of experience designing and scaling distributed systems. Proven track record of leading cross-functional teams and delivering high-impact products.</textarea>
</div>
<div class="field">
<label for="skills">Skills (comma separated)</label>
<textarea id="skills" rows="2">JavaScript, TypeScript, React, Node.js, Python, AWS, Docker, Kubernetes</textarea>
</div>
<div class="action-group">
<button class="btn btn-primary" id="generateBtn">
<span class="icon">⚡</span> Generate resume
</button>
<button class="btn btn-download" id="downloadBtn">
<span class="icon">⬇</span> Download as PDF
</button>
<div class="hint">click "Download" to save your resume as PDF</div>
</div>
</section>
<!-- RIGHT: PREVIEW -->
<section class="preview-panel" id="previewPanel">
<div class="resume-content" id="resumeContent">
<!-- HEADER -->
<div class="resume-header">
<div class="resume-name-block">
<div class="resume-name" id="previewName">Jordan Lee</div>
<div class="resume-title" id="previewTitle">Senior Software Engineer</div>
</div>
<div class="resume-contact" id="previewContact">
<span>✉ jordan.lee@example.com</span>
<span>📞 +1 (555) 123-4567</span>
</div>
</div>
<!-- SUMMARY -->
<div class="resume-section">
<div class="section-title">📋 Professional Summary</div>
<div class="resume-summary" id="previewSummary">Results-driven software engineer with 8+ years of experience designing and scaling distributed systems. Proven track record of leading cross-functional teams and delivering high-impact products.</div>
</div>
<!-- SKILLS -->
<div class="resume-section">
<div class="section-title">⚙️ Core Skills</div>
<div class="skills-container" id="previewSkills">
<!-- populated by JS -->
</div>
</div>
</div>
</section>
</div>
</div>
<script>
(function() {
"use strict";
// ----- DOM REFS: INPUTS -----
const fullNameInput = document.getElementById('fullName');
const jobTitleInput = document.getElementById('jobTitle');
const emailInput = document.getElementById('email');
const phoneInput = document.getElementById('phone');
const summaryInput = document.getElementById('summary');
const skillsInput = document.getElementById('skills');
const generateBtn = document.getElementById('generateBtn');
const downloadBtn = document.getElementById('downloadBtn');
// ----- DOM REFS: PREVIEW -----
const previewName = document.getElementById('previewName');
const previewTitle = document.getElementById('previewTitle');
const previewContact = document.getElementById('previewContact');
const previewSummary = document.getElementById('previewSummary');
const previewSkills = document.getElementById('previewSkills');
const resumeContent = document.getElementById('resumeContent');
// ----- UTILITY: ESCAPE HTML -----
function escapeHtml(text) {
if (text == null) return '';
return String(text)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// ----- CORE RENDER FUNCTION -----
function renderResume() {
const name = fullNameInput.value.trim() || 'Your Name';
const title = jobTitleInput.value.trim() || 'Professional Title';
const email = emailInput.value.trim() || 'email@example.com';
const phone = phoneInput.value.trim() || '+1 (555) 000-0000';
const summary = summaryInput.value.trim() || 'A short professional summary will appear here.';
const skillsRaw = skillsInput.value.trim();
// Update header
previewName.textContent = name;
previewTitle.textContent = title;
// Contact
previewContact.innerHTML = `
<span>✉ ${escapeHtml(email)}</span>
<span>📞 ${escapeHtml(phone)}</span>
`;
// Summary
previewSummary.textContent = summary;
// Skills
let skillsArray = [];
if (skillsRaw) {
skillsArray = skillsRaw
.split(',')
.map(s => s.trim())
.filter(s => s.length > 0);
}
// Deduplicate (case-insensitive)
const seen = new Set();
const uniqueSkills = [];
for (let skill of skillsArray) {
const lower = skill.toLowerCase();
if (!seen.has(lower)) {
seen.add(lower);
uniqueSkills.push(skill);
}
}
const maxSkills = 16;
const displaySkills = uniqueSkills.slice(0, maxSkills);
const hasMore = uniqueSkills.length > maxSkills;
if (displaySkills.length === 0) {
previewSkills.innerHTML = `<div class="empty-message">Add skills above to see them here</div>`;
} else {
let html = displaySkills.map(skill =>
`<span class="skill-badge">${escapeHtml(skill)}</span>`
).join('');
if (hasMore) {
html += `<span class="skill-badge" style="background:#e2ecf7;">+${uniqueSkills.length - maxSkills} more</span>`;
}
previewSkills.innerHTML = html;
}
// Subtle animation
resumeContent.classList.remove('preview-updated');
void resumeContent.offsetWidth;
resumeContent.classList.add('preview-updated');
}
// ----- INITIAL RENDER -----
renderResume();
// ----- EVENT LISTENERS -----
generateBtn.addEventListener('click', (e) => {
e.preventDefault();
renderResume();
});
// Live update on blur
[fullNameInput, jobTitleInput, emailInput, phoneInput, summaryInput, skillsInput].forEach(input => {
input.addEventListener('blur', renderResume);
});
// Enter key (non-textarea)
[fullNameInput, jobTitleInput, emailInput, phoneInput].forEach(input => {
input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
renderResume();
input.blur();
}
});
});
// Ctrl+Enter in textareas
[summaryInput, skillsInput].forEach(ta => {
ta.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
renderResume();
}
});
});
// ----- DOWNLOAD AS PDF -----
downloadBtn.addEventListener('click', function() {
// Ensure preview is current
renderResume();
const previewElement = document.getElementById('previewPanel');
function loadHtml2Pdf(callback) {
if (window.html2pdf) {
callback();
return;
}
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js';
script.onload = callback;
document.head.appendChild(script);
}
loadHtml2Pdf(function() {
const opt = {
margin: [0.5, 0.5, 0.5, 0.5],
filename: 'resume.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2, useCORS: true, letterRendering: true },
jsPDF: { unit: 'in', format: 'a4', orientation: 'portrait' }
};
// Clone the preview for clean PDF generation
const clone = previewElement.cloneNode(true);
clone.style.background = '#ffffff';
clone.style.borderRadius = '0';
clone.style.boxShadow = 'none';
clone.style.border = 'none';
clone.style.padding = '0.7in 0.6in';
clone.style.position = 'relative';
// Remove animation class from clone
const contentClone = clone.querySelector('.resume-content');
if (contentClone) {
contentClone.classList.remove('preview-updated');
}
html2pdf().set(opt).from(clone).save();
});
});
})();
</script>
<!-- Preload html2pdf for instant download -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js" async></script>
</body>
</html>
Comments
Post a Comment