diff --git a/index.html b/index.html index dc976fa..722aa9e 100644 --- a/index.html +++ b/index.html @@ -2,6 +2,7 @@ + 1:1-Ernährungsberatung -
- Ernährungsberatung -
+

Ernährung, die zu dir passt.

diff --git a/public/images/logo.png b/public/images/logo.png new file mode 100644 index 0000000..3063d26 Binary files /dev/null and b/public/images/logo.png differ diff --git a/src/components/navigationsbar/README.md b/src/components/navigationsbar/README.md new file mode 100644 index 0000000..e349d1c --- /dev/null +++ b/src/components/navigationsbar/README.md @@ -0,0 +1,14 @@ +# Navigation einbinden + +Füge an der gewünschten Stelle im HTML einen Platzhalter ein und binde CSS +und JavaScript separat ein: + +```html + + + +``` + +Das Script lädt `navigation.html` und initialisiert die Navigation automatisch, +`initNavbar()` muss nicht zusätzlich aufgerufen werden. Die Stylesheet-Datei wird +nicht vom Script geladen und muss deshalb separat eingebunden werden. diff --git a/src/components/navigationsbar/images/logo.png b/src/components/navigationsbar/images/logo.png new file mode 100644 index 0000000..3063d26 Binary files /dev/null and b/src/components/navigationsbar/images/logo.png differ diff --git a/src/components/navigationsbar/navigation.html b/src/components/navigationsbar/navigation.html new file mode 100644 index 0000000..255751a --- /dev/null +++ b/src/components/navigationsbar/navigation.html @@ -0,0 +1,84 @@ + + + diff --git a/src/components/navigationsbar/navigation.js b/src/components/navigationsbar/navigation.js new file mode 100644 index 0000000..a93a67c --- /dev/null +++ b/src/components/navigationsbar/navigation.js @@ -0,0 +1,99 @@ +import navigationHtml from "./navigation.html?raw"; + +/** + * Bindet das mit Vite gebündelte Navbar-Fragment (navigation.html) ein und + * ersetzt damit den Platzhalter im Header der jeweiligen Seite. + * Aufruf z. B. in main.js: initNavbar('#navbar-placeholder') + */ +export function initNavbar(placeholderSelector = "#navbar-placeholder") { + const placeholder = document.querySelector(placeholderSelector); + if (!placeholder) { + console.warn("Navbar-Platzhalter nicht gefunden:", placeholderSelector); + return; + } + + placeholder.outerHTML = navigationHtml; // Platzhalter durch echtes Navbar-Markup ersetzen + setupBehavior(); + markCurrentPage(); +} + +/** + * Richtet alle interaktiven Funktionen der Navbar ein: + * - Mobiles Menü öffnen/schließen per Klick + * - Automatischer Fokus auf ersten Link, wenn per Tastatur geöffnet + * - Escape schließt das Menü und gibt den Fokus zurück + * - Menü schließt sich, wenn der Fokus die Navbar per Tab verlässt + * - Klick außerhalb der Navbar schließt das Menü (Maus/Touch) + */ +export function setupBehavior() { + const navbar = document.querySelector(".navbar"); + const toggle = document.querySelector(".navbar__toggle"); + const nav = document.querySelector(".navbar__nav"); + const menu = document.querySelector(".navbar__menu"); + if (!navbar || !toggle || !nav || !menu) return; + + let openedViaKeyboard = false; + + toggle.addEventListener("keydown", (e) => { + if (e.key === "Enter" || e.key === " ") { + openedViaKeyboard = true; + } + }); + + toggle.addEventListener("click", () => { + const isOpen = nav.classList.toggle("is-open"); + toggle.setAttribute("aria-expanded", String(isOpen)); + toggle.setAttribute( + "aria-label", + isOpen ? "Menü schließen" : "Menü öffnen", + ); + + if (isOpen && openedViaKeyboard) { + const firstLink = menu.querySelector(".navbar__link"); + if (firstLink) firstLink.focus(); + } + openedViaKeyboard = false; + }); + + document.addEventListener("keydown", (e) => { + if (e.key === "Escape" && nav.classList.contains("is-open")) { + nav.classList.remove("is-open"); + toggle.setAttribute("aria-expanded", "false"); + toggle.setAttribute("aria-label", "Menü öffnen"); + toggle.focus(); + } + }); + + navbar.addEventListener("focusout", (e) => { + const leavingNavbar = !navbar.contains(e.relatedTarget); + if (leavingNavbar && nav.classList.contains("is-open")) { + nav.classList.remove("is-open"); + toggle.setAttribute("aria-expanded", "false"); + toggle.setAttribute("aria-label", "Menü öffnen"); + } + }); + + document.addEventListener("click", (e) => { + if (!navbar.contains(e.target) && nav.classList.contains("is-open")) { + nav.classList.remove("is-open"); + toggle.setAttribute("aria-expanded", "false"); + toggle.setAttribute("aria-label", "Menü öffnen"); + } + }); +} + +/** + * Markiert den Menüpunkt der aktuell geöffneten Seite mit aria-current="page", + * damit er optisch hervorgehoben wird (siehe .navbar__link[aria-current="page"] in der CSS). + */ +export function markCurrentPage() { + const links = document.querySelectorAll(".navbar__link"); + const current = window.location.pathname.replace(/\/index\.html$/, "/"); + links.forEach((link) => { + if (link.getAttribute("href") === current) { + link.setAttribute("aria-current", "page"); + } + }); +} + +document.addEventListener("DOMContentLoaded", () => initNavbar()); diff --git a/src/main.js b/src/main.js index cb01bb8..a8ccb3d 100644 --- a/src/main.js +++ b/src/main.js @@ -1,3 +1,4 @@ -import "./styles.css"; +import "./styles/styles.css"; +import "./components/navigationsbar/navigation.js"; document.querySelector("#year").textContent = new Date().getFullYear(); diff --git a/src/styles/navigation.css b/src/styles/navigation.css new file mode 100644 index 0000000..14cde15 --- /dev/null +++ b/src/styles/navigation.css @@ -0,0 +1,236 @@ +/* Google Fonts: Inter für Fließtext, Playfair Display für Akzente (falls später gebraucht) */ +@import url("https://fonts.googleapis.com/css2?family=Playfair+Display:wght@600;700&family=Inter:wght@400;500;600;700&display=swap"); + +/* Zentrale Design-Werte der Navbar, passend zum TriHub-Farbschema */ +:root { + --nav-bg: #0d0d1a; + --nav-border: rgba(248, 250, 252, 0.08); + --nav-green: #10b981; + --nav-white: #ffffff; + --nav-light: #f8fafc; + --nav-muted: #94a3b8; +} + +/* Verhindert, dass Padding die Breite von Elementen zusätzlich vergrößert */ +.navbar, +.navbar *, +.navbar *::before, +.navbar *::after { + box-sizing: border-box; +} + +/* ---------- Kriterium 1: durchgehend fixiert am oberen Bildschirmrand ---------- */ +.navbar { + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 1000; + border-bottom: 1px solid var(--nav-border); + background: var(--nav-bg); + font-family: "Inter", system-ui, sans-serif; +} + +.navbar__inner { + width: 100%; + min-height: 76px; + padding: 0 20px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; +} + +/* ---------- Logo + Markenname (kein Link mehr, nur Anzeige) ---------- */ +.navbar__brand { + display: inline-flex; + align-items: center; + gap: 12px; + flex-shrink: 0; +} + +.navbar__logo-img { + height: 40px; + width: auto; + display: block; +} + +.navbar__brand-text { + display: flex; + flex-direction: column; + line-height: 1.25; +} + +.navbar__brand-title { + font-size: 0.7rem; + font-weight: 600; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--nav-muted); +} + +.navbar__brand-tagline { + font-size: 0.85rem; + font-weight: 500; + color: var(--nav-muted); +} + +/* ---------- Menüpunkte (rechts) ---------- */ +.navbar__menu { + display: flex; + align-items: center; + gap: 14px; + list-style: none; + margin: 0; + padding: 0; + color: var(--nav-muted); + font-size: 0.85rem; + font-weight: 500; + flex-shrink: 0; +} + +.navbar__link { + color: inherit; + text-decoration: none; + transition: color 0.2s ease; + padding: 0.35rem 0.1rem; + white-space: nowrap; +} + +.navbar__link:hover, +.navbar__link[aria-current="page"] { + color: var(--nav-white); +} + +/* ---------- "Startseite"-Button mit Haus-Icon, ganz rechts ---------- */ +.navbar__link--home { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 18px 6px 10px; + border: 1px solid rgba(148, 163, 184, 0.3); + border-radius: 999px; + color: var(--nav-light); + transition: + border-color 0.2s ease, + color 0.2s ease; +} + +.navbar__link--home:hover { + border-color: rgba(16, 185, 129, 0.55); + color: var(--nav-white); +} + +.navbar__home-icon { + display: inline-flex; + align-items: center; + justify-content: center; + color: #22d3ee; +} + +.navbar__home-icon svg { + width: 18px; + height: 18px; +} + +/* Linienstärke des Haus-Icons zentral hier steuern statt im SVG-Markup */ +.navbar__home-icon svg path { + stroke-width: 1.5; +} + +/* ---------- Kriterium 2: barrierefreie Tastatursteuerung ---------- */ +.skip-link { + position: absolute; + top: -40px; + left: 0; + background: var(--nav-green); + color: #06130f; + font-weight: 700; + padding: 0.75rem 1rem; + z-index: 1100; + transition: top 0.2s ease; +} + +.skip-link:focus { + top: 0; +} + +.navbar a:focus-visible, +.navbar button:focus-visible { + outline: 3px solid var(--nav-green); + outline-offset: 2px; + border-radius: 4px; +} + +/* ---------- Mobiler Hamburger-Button ---------- */ +.navbar__toggle { + display: none; + background: none; + border: none; + cursor: pointer; + padding: 0.5rem; +} + +.navbar__toggle-icon, +.navbar__toggle-icon::before, +.navbar__toggle-icon::after { + display: block; + width: 22px; + height: 2px; + background: var(--nav-white); + position: relative; +} +.navbar__toggle-icon::before, +.navbar__toggle-icon::after { + content: ""; + position: absolute; +} +.navbar__toggle-icon::before { + top: -7px; +} +.navbar__toggle-icon::after { + top: 7px; +} + +@media (max-width: 1300px) { + .navbar__toggle { + display: block; + } + + .navbar__nav { + position: absolute; + top: 76px; + left: 0; + right: 0; + background: var(--nav-bg); + border-bottom: 1px solid var(--nav-border); + display: none; + } + + .navbar__nav.is-open { + display: block; + } + + .navbar__menu { + flex-direction: column; + align-items: flex-start; + padding: 1rem 20px; + gap: 1rem; + font-size: 0.95rem; + } +} + +@media (max-width: 600px) { + .navbar__inner { + padding: 0 16px; + } + + .navbar__brand-tagline { + display: none; + } +} + +/* Platz für die fixierte Navbar */ +body { + padding-top: 76px; +}