diff --git a/app/components/banner-slider.php b/app/components/banner-slider.php new file mode 100644 index 0000000..89ee303 --- /dev/null +++ b/app/components/banner-slider.php @@ -0,0 +1,68 @@ + +
+
+ +

+ +

Anzeigen unserer Sponsoren & Partner

+ + +

+ + + 1): ?> + + + +
+
diff --git a/app/pages/home.php b/app/pages/home.php index f624d2b..b9b55ca 100644 --- a/app/pages/home.php +++ b/app/pages/home.php @@ -13,10 +13,11 @@ $meta = [ 'title' => 'Fußball & Turnen in Kulmbach – TSV 08 Kulmbach', 'title_absolute' => true, 'description' => 'Dein Sportverein für Fußball und Turnen in Kulmbach: Jugend-, Herren- und Damenfußball, Turnen und Trampolin – seit 1908 im Katzbachtal. Jetzt Mitglied werden!', - 'scripts' => ['form.js'], + 'scripts' => ['form.js', 'banner.js'], ]; component('hero', ['hero' => $home['hero']]); +component('banner-slider'); component('instagram-feed'); foreach ($home['sections'] as $section) { diff --git a/data/banners.json b/data/banners.json new file mode 100644 index 0000000..19cf363 --- /dev/null +++ b/data/banners.json @@ -0,0 +1,28 @@ +{ + "heading": "Unsere Werbepartner", + "subline": "Diese Unternehmen aus Kulmbach und der Region unterstützen den TSV 08 – ein herzliches Dankeschön!", + "banners": [ + { + "company": "Musterfirma GmbH", + "title": "Ihre Werbung beim TSV 08 Kulmbach", + "text": "Erreichen Sie tausende sportbegeisterte Kulmbacherinnen und Kulmbacher – als Sponsor unseres Vereins direkt auf der Startseite.", + "logo": "img/banners/placeholder-1.svg", + "cta": { "label": "Jetzt Partner werden", "url": "https://example.com" } + }, + { + "company": "Beispiel Handwerk AG", + "title": "Regional. Verlässlich. Mittendrin.", + "text": "Wir unterstützen den Vereinssport im Katzbachtal – und freuen uns, Teil der TSV-08-Familie zu sein.", + "logo": "img/banners/placeholder-2.svg", + "background": "img/banners/bg-sample.svg", + "cta": { "label": "Mehr erfahren", "url": "https://example.com" } + }, + { + "company": "Sponsor Sport & Freizeit", + "title": "Ausrüstung für echte Teamplayer", + "text": "Vom Trikot bis zum Trainingsplatz – unser Partner für alles rund um den Sport in der Region Kulmbach.", + "logo": "img/banners/placeholder-3.svg", + "cta": { "label": "Zum Angebot", "url": "https://example.com" } + } + ] +} diff --git a/public/assets/img/banners/bg-sample.svg b/public/assets/img/banners/bg-sample.svg new file mode 100644 index 0000000..2336a4a --- /dev/null +++ b/public/assets/img/banners/bg-sample.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/assets/img/banners/placeholder-1.svg b/public/assets/img/banners/placeholder-1.svg new file mode 100644 index 0000000..06109e1 --- /dev/null +++ b/public/assets/img/banners/placeholder-1.svg @@ -0,0 +1,5 @@ + + + IHR LOGO + Sponsor 1 + diff --git a/public/assets/img/banners/placeholder-2.svg b/public/assets/img/banners/placeholder-2.svg new file mode 100644 index 0000000..31db74c --- /dev/null +++ b/public/assets/img/banners/placeholder-2.svg @@ -0,0 +1,5 @@ + + + IHR LOGO + Sponsor 2 + diff --git a/public/assets/img/banners/placeholder-3.svg b/public/assets/img/banners/placeholder-3.svg new file mode 100644 index 0000000..6015dff --- /dev/null +++ b/public/assets/img/banners/placeholder-3.svg @@ -0,0 +1,5 @@ + + + IHR LOGO + Sponsor 3 + diff --git a/public/assets/js/banner.js b/public/assets/js/banner.js new file mode 100644 index 0000000..6581133 --- /dev/null +++ b/public/assets/js/banner.js @@ -0,0 +1,115 @@ +/* Werbe-/Sponsoren-Banner-Slider: Auto-Rotation über die Fortschrittsanimation + des aktiven Indikators (animationend → nächste Folie). + + Barrierefreiheit: + - explizite Pause/Play-Taste (WCAG 2.2.2 „Pause, Stop, Hide"), + - zusätzlich Pause bei Hover/Fokus (Komfort), + - aria-live: im Autoplay "off" (kein Vorlese-Spam), bei Pause/manuell "polite", + - bei prefers-reduced-motion läuft keine Animation → keine Auto-Rotation; + Indikatoren bleiben per Klick bedienbar, die Pause-Taste entfällt. */ +(function () { + 'use strict'; + + var root = document.querySelector('[data-banner]'); + if (!root) { + return; + } + + var track = root.querySelector('#ad-banner-track'); + var slides = Array.prototype.slice.call(root.querySelectorAll('[data-banner-slide]')); + var dots = Array.prototype.slice.call(root.querySelectorAll('[data-banner-dot]')); + var controls = root.querySelector('[data-banner-controls]'); + var toggle = root.querySelector('[data-banner-toggle]'); + var toggleLabel = root.querySelector('[data-banner-toggle-label]'); + if (slides.length <= 1) { + return; + } + + var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; + var index = 0; + var hoverPaused = false; + var userPaused = false; + + function syncPaused() { + // Bei reduzierter Bewegung gilt jeder Wechsel als manuell → ansagen. + var paused = hoverPaused || userPaused || reduce; + root.classList.toggle('is-paused', paused); + if (track) { + track.setAttribute('aria-live', paused ? 'polite' : 'off'); + } + } + + function activate(i) { + index = (i % slides.length + slides.length) % slides.length; + slides.forEach(function (slide, n) { + slide.hidden = n !== index; + }); + dots.forEach(function (dot, n) { + var on = n === index; + dot.classList.toggle('is-active', on); + if (on) { + dot.setAttribute('aria-current', 'true'); + } else { + dot.removeAttribute('aria-current'); + } + }); + } + + // Sobald die Fortschrittsanimation des aktiven Indikators durchgelaufen ist, + // zur nächsten Folie wechseln. (Beim Wechsel wird die alte Animation + // abgebrochen → 'animationcancel', nicht 'animationend' → kein Doppelsprung.) + root.addEventListener('animationend', function (event) { + if (event.animationName === 'ad-progress') { + activate(index + 1); + } + }); + + // Pause bei Hover/Fokus. + root.addEventListener('mouseenter', function () { + hoverPaused = true; + syncPaused(); + }); + root.addEventListener('mouseleave', function () { + hoverPaused = false; + syncPaused(); + }); + root.addEventListener('focusin', function () { + hoverPaused = true; + syncPaused(); + }); + root.addEventListener('focusout', function (event) { + if (!root.contains(event.relatedTarget)) { + hoverPaused = false; + syncPaused(); + } + }); + + dots.forEach(function (dot, n) { + dot.addEventListener('click', function () { + activate(n); + }); + }); + + // Steuerung einblenden (ohne JS bleibt sie verborgen, da funktionslos). + if (controls) { + controls.hidden = false; + } + + // Pause/Play nur anbieten, wenn überhaupt automatisch rotiert wird. + if (toggle && !reduce) { + toggle.hidden = false; + toggle.addEventListener('click', function () { + userPaused = !userPaused; + toggle.setAttribute('aria-pressed', String(userPaused)); + if (toggleLabel) { + toggleLabel.textContent = userPaused + ? 'Automatischen Wechsel fortsetzen' + : 'Automatischen Wechsel pausieren'; + } + syncPaused(); + }); + } + + activate(0); + syncPaused(); +})();