feat: Navigationsleiste fertig und bereit zum einbinden

feature/navigationsleiste
Emirhan Sahin 2026-09-27 17:47:43 +00:00
parent 7059b41bfe
commit 33e844423b
3 changed files with 72 additions and 34 deletions

View File

@ -2,11 +2,7 @@
<header class="navbar" id="site-navbar"> <header class="navbar" id="site-navbar">
<div class="navbar__inner"> <div class="navbar__inner">
<a <div class="navbar__brand">
href="/src/design/tri-hub-ernaehrungsberatung.html"
class="navbar__brand"
aria-label="TriHub Startseite"
>
<img <img
src="/src/components/navigationsbar/images/logo.png" src="/src/components/navigationsbar/images/logo.png"
alt="" alt=""
@ -15,10 +11,10 @@
<span class="navbar__brand-text"> <span class="navbar__brand-text">
<span class="navbar__brand-title">TRI-HUB</span> <span class="navbar__brand-title">TRI-HUB</span>
<span class="navbar__brand-tagline" <span class="navbar__brand-tagline"
>Triathlon als sportliches Zuhause ab 50</span >Ernährungsberatung für Triathleten ab 50</span
> >
</span> </span>
</a> </div>
<button <button
class="navbar__toggle" class="navbar__toggle"
@ -50,6 +46,28 @@
<li> <li>
<a href="/kontakt.html" class="navbar__link">Kontakt</a> <a href="/kontakt.html" class="navbar__link">Kontakt</a>
</li> </li>
<li>
<a
href="https://tri-hub.de/"
class="navbar__link navbar__link--home"
>
<span class="navbar__home-icon" aria-hidden="true">
<svg
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 10L12 3L20 10V20a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V10Z M9 21V14H15V21"
stroke="currentColor"
stroke-linejoin="round"
stroke-linecap="round"
/>
</svg>
</span>
Startseite
</a>
</li>
</ul> </ul>
</nav> </nav>
</div> </div>

View File

@ -1,6 +1,7 @@
/** /**
* Lädt das Navbar-Fragment (navigation.html) per fetch() und ersetzt * Lädt das Navbar-Fragment (navigation.html) per fetch() und ersetzt
* damit den Platzhalter im Header der jeweiligen Seite. * damit den Platzhalter im Header der jeweiligen Seite.
* Aufruf z. B. in main.js: initNavbar('#navbar-placeholder')
*/ */
export function initNavbar(placeholderSelector = "#navbar-placeholder") { export function initNavbar(placeholderSelector = "#navbar-placeholder") {
const placeholder = document.querySelector(placeholderSelector); const placeholder = document.querySelector(placeholderSelector);
@ -38,15 +39,12 @@ export function setupBehavior() {
let openedViaKeyboard = false; let openedViaKeyboard = false;
// Merken, ob der Toggle-Button per Tastatur (statt Maus) ausgelöst wurde
toggle.addEventListener("keydown", (e) => { toggle.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") { if (e.key === "Enter" || e.key === " ") {
openedViaKeyboard = true; openedViaKeyboard = true;
} }
}); });
// Menü auf-/zuklappen; funktioniert nativ per Klick UND per Enter/Space,
// da .navbar__toggle ein echtes <button>-Element ist
toggle.addEventListener("click", () => { toggle.addEventListener("click", () => {
const isOpen = nav.classList.toggle("is-open"); const isOpen = nav.classList.toggle("is-open");
toggle.setAttribute("aria-expanded", String(isOpen)); toggle.setAttribute("aria-expanded", String(isOpen));
@ -56,14 +54,12 @@ export function setupBehavior() {
); );
if (isOpen && openedViaKeyboard) { if (isOpen && openedViaKeyboard) {
// Bei Tastatur-Öffnung direkt in den ersten Menüpunkt springen
const firstLink = menu.querySelector(".navbar__link"); const firstLink = menu.querySelector(".navbar__link");
if (firstLink) firstLink.focus(); if (firstLink) firstLink.focus();
} }
openedViaKeyboard = false; openedViaKeyboard = false;
}); });
// Escape schließt das Menü, Fokus geht zurück auf den Toggle-Button
document.addEventListener("keydown", (e) => { document.addEventListener("keydown", (e) => {
if (e.key === "Escape" && nav.classList.contains("is-open")) { if (e.key === "Escape" && nav.classList.contains("is-open")) {
nav.classList.remove("is-open"); nav.classList.remove("is-open");
@ -73,7 +69,6 @@ export function setupBehavior() {
} }
}); });
// Schließt das Menü automatisch, sobald der Fokus die Navbar per Tab verlässt
navbar.addEventListener("focusout", (e) => { navbar.addEventListener("focusout", (e) => {
const leavingNavbar = !navbar.contains(e.relatedTarget); const leavingNavbar = !navbar.contains(e.relatedTarget);
if (leavingNavbar && nav.classList.contains("is-open")) { if (leavingNavbar && nav.classList.contains("is-open")) {
@ -83,7 +78,6 @@ export function setupBehavior() {
} }
}); });
// Klick außerhalb der Navbar schließt das mobile Menü (Maus/Touch-Nutzer)
document.addEventListener("click", (e) => { document.addEventListener("click", (e) => {
if (!navbar.contains(e.target) && nav.classList.contains("is-open")) { if (!navbar.contains(e.target) && nav.classList.contains("is-open")) {
nav.classList.remove("is-open"); nav.classList.remove("is-open");
@ -107,5 +101,4 @@ export function markCurrentPage() {
}); });
} }
// Startet die Navbar automatisch, sobald die Seite geladen ist
document.addEventListener("DOMContentLoaded", () => initNavbar()); document.addEventListener("DOMContentLoaded", () => initNavbar());

View File

@ -11,8 +11,7 @@
--nav-muted: #94a3b8; --nav-muted: #94a3b8;
} }
/* Verhindert, dass Padding die Breite von Elementen zusätzlich vergrößert /* Verhindert, dass Padding die Breite von Elementen zusätzlich vergrößert */
(ohne das würde die Navbar breiter als der Bildschirm werden) */
.navbar, .navbar,
.navbar *, .navbar *,
.navbar *::before, .navbar *::before,
@ -26,13 +25,12 @@
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
z-index: 1000; /* bleibt über allen anderen Inhalten */ z-index: 1000;
border-bottom: 1px solid var(--nav-border); border-bottom: 1px solid var(--nav-border);
background: var(--nav-bg); background: var(--nav-bg);
font-family: "Inter", system-ui, sans-serif; font-family: "Inter", system-ui, sans-serif;
} }
/* Innerer Container: Logo links, Menü rechts, mit etwas Innenabstand */
.navbar__inner { .navbar__inner {
width: 100%; width: 100%;
min-height: 76px; min-height: 76px;
@ -43,13 +41,12 @@
gap: 16px; gap: 16px;
} }
/* ---------- Logo + Markenname (klickbarer Link, ganz links) ---------- */ /* ---------- Logo + Markenname (kein Link mehr, nur Anzeige) ---------- */
.navbar__brand { .navbar__brand {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 12px; gap: 12px;
text-decoration: none; flex-shrink: 0;
flex-shrink: 0; /* schrumpft nie, auch wenn das Menü eng wird */
} }
.navbar__logo-img { .navbar__logo-img {
@ -78,7 +75,7 @@
color: var(--nav-muted); color: var(--nav-muted);
} }
/* ---------- Menüpunkte (ganz rechts) ---------- */ /* ---------- Menüpunkte (rechts) ---------- */
.navbar__menu { .navbar__menu {
display: flex; display: flex;
align-items: center; align-items: center;
@ -97,7 +94,7 @@
text-decoration: none; text-decoration: none;
transition: color 0.2s ease; transition: color 0.2s ease;
padding: 0.35rem 0.1rem; padding: 0.35rem 0.1rem;
white-space: nowrap; /* verhindert Zeilenumbruch mitten im Linktext */ white-space: nowrap;
} }
.navbar__link:hover, .navbar__link:hover,
@ -105,9 +102,43 @@
color: var(--nav-white); color: var(--nav-white);
} }
/* ---------- Kriterium 2: barrierefreie Tastatursteuerung ---------- */ /* ---------- "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;
}
/* Skip-Link: unsichtbar, bis er per Tab fokussiert wird */ .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 { .skip-link {
position: absolute; position: absolute;
top: -40px; top: -40px;
@ -124,7 +155,6 @@
top: 0; top: 0;
} }
/* Sichtbarer, einheitlicher Fokus-Ring für alle interaktiven Elemente */
.navbar a:focus-visible, .navbar a:focus-visible,
.navbar button:focus-visible { .navbar button:focus-visible {
outline: 3px solid var(--nav-green); outline: 3px solid var(--nav-green);
@ -132,7 +162,7 @@
border-radius: 4px; border-radius: 4px;
} }
/* ---------- Mobiler Hamburger-Button (nur auf schmalen Bildschirmen sichtbar) ---------- */ /* ---------- Mobiler Hamburger-Button ---------- */
.navbar__toggle { .navbar__toggle {
display: none; display: none;
background: none; background: none;
@ -162,8 +192,6 @@
top: 7px; top: 7px;
} }
/* Ab dieser Breite wechselt die Navbar auf den Hamburger,
damit die Menüpunkte nie über den Bildschirmrand hinausragen */
@media (max-width: 1300px) { @media (max-width: 1300px) {
.navbar__toggle { .navbar__toggle {
display: block; display: block;
@ -176,11 +204,11 @@
right: 0; right: 0;
background: var(--nav-bg); background: var(--nav-bg);
border-bottom: 1px solid var(--nav-border); border-bottom: 1px solid var(--nav-border);
display: none; /* standardmäßig eingeklappt */ display: none;
} }
.navbar__nav.is-open { .navbar__nav.is-open {
display: block; /* wird per JS umgeschaltet */ display: block;
} }
.navbar__menu { .navbar__menu {
@ -192,7 +220,6 @@
} }
} }
/* Auf sehr schmalen Bildschirmen den Untertitel ausblenden, spart Platz */
@media (max-width: 600px) { @media (max-width: 600px) {
.navbar__inner { .navbar__inner {
padding: 0 16px; padding: 0 16px;
@ -203,7 +230,7 @@
} }
} }
/* Platz für die fixierte Navbar, damit sie keinen Inhalt verdeckt */ /* Platz für die fixierte Navbar */
body { body {
padding-top: 76px; padding-top: 76px;
} }