From c5f7b258ff08c2da47f03bf195cca37e2eec02e0 Mon Sep 17 00:00:00 2001 From: fs Date: Fri, 19 Jun 2026 08:13:10 +0200 Subject: [PATCH] =?UTF-8?q?Navigation:=20Dropdown-Untermen=C3=BCs=20f?= =?UTF-8?q?=C3=BCr=20Fu=C3=9Fball=20und=20Verein?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit navigation.json gruppiert Fußball (Übersicht, Mannschaften, Jugend) und Verein (Übersicht, Historie) als children. nav.php rendert Disclosure-Buttons mit aria-expanded/-controls; ohne JS per CSS :hover/:focus-within bedienbar. nav.js ergänzt Toggle, Escape (Untermenü zuerst, dann Overlay) und Klick-außerhalb-schließt. Kontakt-CTA aus der Nav entfernt. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01KDQZ6FuchNUh2bxSW9PeNv --- app/components/nav.php | 35 ++++++++++++++++++++++------ data/navigation.json | 25 +++++++++++++++----- public/assets/js/nav.js | 51 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 95 insertions(+), 16 deletions(-) diff --git a/app/components/nav.php b/app/components/nav.php index 179ad3f..e4d5e4e 100644 --- a/app/components/nav.php +++ b/app/components/nav.php @@ -4,7 +4,10 @@ declare(strict_types=1); /** * Hauptnavigation aus data/navigation.json. Props: $current (Slug). - * Burger-Toggle übernimmt assets/js/nav.js (aria-expanded/aria-controls). + * Burger-Toggle + Dropdowns übernimmt assets/js/nav.js. + * Dropdowns funktionieren auch ohne JS (CSS :hover / :focus-within); JS ergänzt + * nur aria-expanded, Escape und Klick-außerhalb-schließt. + * CTAs rendert der Header (site-header__actions) — nicht hier. */ $nav = json_load('navigation'); @@ -20,14 +23,32 @@ $href = static function (string $slug): string { diff --git a/data/navigation.json b/data/navigation.json index 175acfa..f3351a3 100644 --- a/data/navigation.json +++ b/data/navigation.json @@ -1,15 +1,28 @@ { "main": [ { "label": "Startseite", "slug": "" }, - { "label": "Aktuelles", "slug": "#aktuelles" }, - { "label": "Jugendfußball", "slug": "jugendfussball" }, - { "label": "Fußball", "slug": "fussball" }, + { + "label": "Fußball", + "slug": "fussball", + "children": [ + { "label": "Übersicht", "slug": "fussball" }, + { "label": "1. Mannschaft", "slug": "fussball/erste-mannschaft" }, + { "label": "2. Mannschaft", "slug": "fussball/zweite-mannschaft" }, + { "label": "Damenmannschaft", "slug": "fussball/damen" }, + { "label": "Jugendfußball", "slug": "jugendfussball" } + ] + }, { "label": "Turnen", "slug": "turnen" }, - { "label": "Der Verein", "slug": "der-verein" }, - { "label": "Matchcenter", "slug": "matchcenter" } + { + "label": "Der Verein", + "slug": "verein", + "children": [ + { "label": "Übersicht", "slug": "verein" }, + { "label": "Historie", "slug": "historie" } + ] + } ], "ctas": [ - { "label": "Kontakt", "slug": "kontakt", "style": "outline" }, { "label": "Mitglied werden", "slug": "mitglied-werden", "style": "primary" } ], "legal": [ diff --git a/public/assets/js/nav.js b/public/assets/js/nav.js index f0929f2..7acaa19 100644 --- a/public/assets/js/nav.js +++ b/public/assets/js/nav.js @@ -1,4 +1,7 @@ -/* Burger-Navigation: Toggle mit aria-expanded, Escape schließt, Klick auf Link schließt. */ +/* Navigation: Burger-Overlay (aria-expanded, Escape, Klick auf Link schließt) + + Dropdown-Untermenüs als Disclosure (Klick togglet aria-expanded). + Die Untermenüs sind auch ohne JS per CSS (:hover/:focus-within) bedienbar; + JS macht den Zustand für Screenreader sichtbar und ergänzt Escape/Außerklick. */ (function () { 'use strict'; @@ -8,10 +11,23 @@ return; } + var subToggles = Array.prototype.slice.call(list.querySelectorAll('.site-nav__sublink')); + + function closeSubmenus(except) { + subToggles.forEach(function (btn) { + if (btn !== except) { + btn.setAttribute('aria-expanded', 'false'); + } + }); + } + function setOpen(open) { toggle.setAttribute('aria-expanded', String(open)); document.body.classList.toggle('nav-open', open); toggle.querySelector('.visually-hidden').textContent = open ? 'Menü schließen' : 'Menü öffnen'; + if (!open) { + closeSubmenus(null); + } } toggle.addEventListener('click', function () { @@ -24,14 +40,43 @@ } }); + // Dropdown-Buttons: togglen ihr eigenes Untermenü, schließen die anderen. + subToggles.forEach(function (btn) { + btn.addEventListener('click', function () { + var open = btn.getAttribute('aria-expanded') === 'true'; + closeSubmenus(btn); + btn.setAttribute('aria-expanded', String(!open)); + }); + }); + + // Klick außerhalb eines Dropdowns schließt offene Untermenüs. + document.addEventListener('click', function (event) { + if (!event.target.closest('.site-nav__item--has-submenu')) { + closeSubmenus(null); + } + }); + document.addEventListener('keydown', function (event) { - if (event.key === 'Escape' && toggle.getAttribute('aria-expanded') === 'true') { + if (event.key !== 'Escape') { + return; + } + // Zuerst ein offenes Untermenü schließen (Fokus zurück auf seinen Button) … + var openSub = subToggles.filter(function (btn) { + return btn.getAttribute('aria-expanded') === 'true'; + })[0]; + if (openSub) { + openSub.setAttribute('aria-expanded', 'false'); + openSub.focus(); + return; + } + // … sonst das Burger-Overlay. + if (toggle.getAttribute('aria-expanded') === 'true') { setOpen(false); toggle.focus(); } }); - // Beim Vergrößern auf Desktop offenes Overlay zurücksetzen. + // Beim Vergrößern auf Desktop offenes Overlay/Untermenüs zurücksetzen. window.addEventListener('resize', function () { if (window.innerWidth > 1249 && toggle.getAttribute('aria-expanded') === 'true') { setOpen(false);