feat: add reading progress bar, active nav anchor, PDF download button, dynamic OG meta

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
LIABEUF, Stephane (External) 2026-05-18 02:38:10 +02:00
parent ace0a1e299
commit d95e6aa955

View file

@ -2931,10 +2931,83 @@
.contact-icon { .contact-icon {
font-size: 14px; font-size: 14px;
} }
/* ════ PROGRESS BAR ════ */
#read-progress {
position: fixed;
top: 0; left: 0;
width: 0%;
height: 3px;
z-index: 10010;
transition: width .1s linear;
background: var(--progress-color, #fff);
}
[data-theme="cyberpunk"] #read-progress { background: #00f5ff; box-shadow: 0 0 8px #00f5ff; }
[data-theme="bd"] #read-progress { background: #FFE033; }
[data-theme="itpro"] #read-progress { background: #1a56db; }
[data-theme="cgi"] #read-progress {
background: linear-gradient(to right, #E31937, #A82465, #5236AB);
}
/* ════ NAV ACTIVE ════ */
nav a.nav-active {
opacity: 1 !important;
}
[data-theme="cyberpunk"] nav a.nav-active { color: #00f5ff !important; }
[data-theme="cyberpunk"] nav a.nav-active::before { opacity: 1 !important; }
[data-theme="bd"] nav a.nav-active { color: #1a1a1a !important; background: #FFE033; }
[data-theme="itpro"] nav a.nav-active { color: #1a56db !important; border-bottom-color: #1a56db !important; }
[data-theme="cgi"] nav a.nav-active { color: #5236AB !important; }
/* ════ DOWNLOAD BTN ════ */
.dl-btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 9px 18px;
font-size: 11px;
font-weight: 700;
letter-spacing: .1em;
text-transform: uppercase;
cursor: pointer;
border: none;
transition: all .25s;
}
[data-theme="cyberpunk"] .dl-btn {
font-family: 'Share Tech Mono', monospace;
color: #00f5ff;
background: rgba(0,245,255,.08);
border: 1px solid rgba(0,245,255,.4);
box-shadow: 0 0 14px rgba(0,245,255,.15);
}
[data-theme="cyberpunk"] .dl-btn:hover { background: rgba(0,245,255,.18); box-shadow: 0 0 24px rgba(0,245,255,.3); }
[data-theme="bd"] .dl-btn {
font-family: 'Comic Neue', cursive;
color: #1a1a1a;
background: #FFE033;
border: 3px solid #1a1a1a;
box-shadow: 3px 3px 0 #1a1a1a;
}
[data-theme="bd"] .dl-btn:hover { transform: translate(-2px,-2px); box-shadow: 5px 5px 0 #1a1a1a; }
[data-theme="itpro"] .dl-btn {
font-family: 'Inter', sans-serif;
color: #fff;
background: #1a56db;
border-radius: 30px;
}
[data-theme="itpro"] .dl-btn:hover { background: #1345b5; }
[data-theme="cgi"] .dl-btn {
font-family: Arial, sans-serif;
color: #fff;
background: #5236AB;
border-left: 4px solid #E31937;
}
[data-theme="cgi"] .dl-btn:hover { background: #200A58; }
</style> </style>
</head> </head>
<body> <body>
<div id="read-progress"></div>
<!-- Loading --> <!-- Loading -->
<div id="loading"> <div id="loading">
@ -3087,10 +3160,11 @@
const navHTML = ` const navHTML = `
<nav> <nav>
<span class="nav-logo">${esc(id.nom.split(' ')[0][0])}${esc(id.prenom[0])} // 2025</span> <span class="nav-logo">${esc(id.nom.split(' ')[0][0])}${esc(id.prenom[0])} // 2025</span>
<a href="#experience">Parcours</a> <a href="#experience" data-section="experience">Parcours</a>
<a href="#competences">Skills</a> <a href="#competences" data-section="competences">Skills</a>
<a href="#secteurs">Secteurs</a> <a href="#secteurs" data-section="secteurs">Secteurs</a>
<a href="#formation">Formation</a> <a href="#formation" data-section="formation">Formation</a>
<button class="dl-btn" onclick="downloadCV()" title="Télécharger le CV en PDF">⬇ PDF</button>
</nav>`; </nav>`;
// ── EXPÉRIENCES ── // ── EXPÉRIENCES ──
@ -3292,6 +3366,26 @@
}, 500); }, 500);
} }
// ── Progress bar ──
const progressBar = document.getElementById('read-progress');
window.addEventListener('scroll', () => {
const scrolled = window.scrollY;
const total = document.documentElement.scrollHeight - window.innerHeight;
progressBar.style.width = (total > 0 ? (scrolled / total) * 100 : 0) + '%';
}, { passive: true });
// ── Nav active (Intersection Observer) ──
const navObs = new IntersectionObserver(entries => {
entries.forEach(entry => {
const link = document.querySelector(`nav a[data-section="${entry.target.id}"]`);
if (link) link.classList.toggle('nav-active', entry.isIntersecting);
});
}, { threshold: 0.3 });
['experience', 'competences', 'secteurs', 'formation'].forEach(id => {
const el = document.getElementById(id);
if (el) navObs.observe(el);
});
// Cacher le loading // Cacher le loading
const ld = document.getElementById('loading'); const ld = document.getElementById('loading');
ld.style.opacity = '0'; ld.style.opacity = '0';
@ -3308,6 +3402,27 @@
</span> </span>
</div>`; </div>`;
}); });
// ══════════════════════════════════════════════
// TÉLÉCHARGEMENT PDF
// ══════════════════════════════════════════════
function downloadCV() {
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js';
script.onload = () => {
const nom = document.querySelector('.hero-name')?.textContent?.trim().replace(/\s+/g, '_') || 'CV';
const opt = {
margin: [10, 10, 10, 10],
filename: `CV_${nom}.pdf`,
image: { type: 'jpeg', quality: 0.95 },
html2canvas: { scale: 2, useCORS: true, logging: false },
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
pagebreak: { mode: ['avoid-all', 'css'] }
};
html2pdf().set(opt).from(document.getElementById('app')).save();
};
document.head.appendChild(script);
}
</script> </script>
</body> </body>