- Refonte : navbar/footer extraits dans components.js (injection JS) - Contact : email [email protected], suppression du telephone sur les CV - Pied de page : retrait de « Design & code - fait main » - Ajout de style.css (jusqu'ici non versionne) - Mise a jour : cv.html, cv-roman-felden.html, index.html, projets.html
106 lines
4.9 KiB
JavaScript
106 lines
4.9 KiB
JavaScript
/* ============================================================
|
|
Roman Felden — Composants partagés
|
|
Navbar, footer, toggle thème, animations au scroll
|
|
============================================================ */
|
|
|
|
const navbarHTML = `
|
|
<nav class="navbar">
|
|
<div class="navbar-container">
|
|
<a href="index.html" class="navbar-brand" aria-label="Accueil — Roman Felden">
|
|
<span class="brand-name">Roman Felden<span class="brand-dot">.</span></span>
|
|
<span class="brand-tag">Sécu & Systèmes</span>
|
|
</a>
|
|
<div class="navbar-menu">
|
|
<a href="index.html" id="nav-index"><span class="nav-index">01</span>Accueil</a>
|
|
<a href="cv.html" id="nav-cv"><span class="nav-index">02</span>CV</a>
|
|
<a href="projets.html" id="nav-projets"><span class="nav-index">03</span>Projets</a>
|
|
<button id="theme-toggle" class="theme-toggle" aria-label="Basculer le thème clair / sombre" title="Basculer le thème">
|
|
<svg id="theme-icon-sun" style="display:none" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v1.5m0 15V21m9-9h-1.5M4.5 12H3m15.36 6.36l-1.06-1.06M6.7 6.7L5.64 5.64m12.72 0l-1.06 1.06M6.7 17.3l-1.06 1.06M16.5 12a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0z"/>
|
|
</svg>
|
|
<svg id="theme-icon-moon" style="display:none" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
`;
|
|
|
|
const footerHTML = `
|
|
<footer class="footer">
|
|
<div class="container">
|
|
<div class="footer-grid">
|
|
<span class="footer-name">Roman Felden<span class="brand-dot">.</span></span>
|
|
<div class="footer-links">
|
|
<a class="u-link" href="https://lanro.eu" target="_blank" rel="noopener">lanro.eu</a>
|
|
<a class="u-link" href="https://www.linkedin.com/in/roman-felden" target="_blank" rel="noopener">LinkedIn</a>
|
|
<a class="u-link" href="mailto:[email protected]">Email</a>
|
|
</div>
|
|
</div>
|
|
<div class="footer-bottom">
|
|
<span>© 2026 — Grenoble, France</span>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
`;
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
const body = document.body;
|
|
body.insertAdjacentHTML("afterbegin", navbarHTML);
|
|
body.insertAdjacentHTML("beforeend", footerHTML);
|
|
|
|
/* ---------- Lien actif ---------- */
|
|
const page = window.location.pathname.split("/").pop();
|
|
const activeMap = {
|
|
"": "nav-index",
|
|
"index.html": "nav-index",
|
|
"cv.html": "nav-cv",
|
|
"cv-roman-felden.html": "nav-cv",
|
|
"projets.html": "nav-projets",
|
|
};
|
|
const activeId = activeMap[page];
|
|
if (activeId) document.getElementById(activeId)?.classList.add("active");
|
|
|
|
/* ---------- Thème ---------- */
|
|
const toggleBtn = document.getElementById("theme-toggle");
|
|
const sunIcon = document.getElementById("theme-icon-sun");
|
|
const moonIcon = document.getElementById("theme-icon-moon");
|
|
|
|
const applyTheme = (theme) => {
|
|
document.documentElement.setAttribute("data-theme", theme);
|
|
sunIcon.style.display = theme === "dark" ? "block" : "none";
|
|
moonIcon.style.display = theme === "dark" ? "none" : "block";
|
|
};
|
|
|
|
const saved = localStorage.getItem("theme");
|
|
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
applyTheme(saved || (prefersDark ? "dark" : "light"));
|
|
|
|
toggleBtn.addEventListener("click", () => {
|
|
const current = document.documentElement.getAttribute("data-theme");
|
|
const next = current === "dark" ? "light" : "dark";
|
|
localStorage.setItem("theme", next);
|
|
applyTheme(next);
|
|
});
|
|
|
|
/* ---------- Apparition au scroll ---------- */
|
|
const revealEls = document.querySelectorAll(".reveal");
|
|
if ("IntersectionObserver" in window && revealEls.length) {
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add("is-visible");
|
|
observer.unobserve(entry.target);
|
|
}
|
|
});
|
|
},
|
|
{ threshold: 0.08, rootMargin: "0px 0px -40px 0px" }
|
|
);
|
|
revealEls.forEach((el) => observer.observe(el));
|
|
} else {
|
|
revealEls.forEach((el) => el.classList.add("is-visible"));
|
|
}
|
|
});
|