Synchronisation avec le site en ligne (architecture components.js)
- 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
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
/* ============================================================
|
||||
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"));
|
||||
}
|
||||
});
|
||||
+161
-1196
File diff suppressed because it is too large
Load Diff
+158
-1369
File diff suppressed because it is too large
Load Diff
+169
-837
File diff suppressed because it is too large
Load Diff
+325
-959
File diff suppressed because it is too large
Load Diff
+1202
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user