Banner-Slider: Crossfade-Übergang + per-Slide-Akzentfarbe

Folienwechsel als sauberes Crossfade (best practice): Slides im selben
Grid-Feld gestapelt → konstante Höhe ohne Sprung, ein-/ausgehende Folie
blendet über (Opacity + leichtes translateY). JS steuert die Sichtbarkeit
über .is-active statt hidden; ohne JS bleibt der hidden-Fallback (erste Folie).
Reduced-Motion über den globalen Kill-Switch instant.

Neue Slide-Prop "accent" (CSS-Farbe) überschreibt das Vereins-Rot der Buttons
pro Slide; Breadcrumb-Slide nutzt #6cbbbc. Button-Text wird aus der Akzentfarbe
dunkel abgeleitet (color-mix), da helle Akzente mit Weiß zu wenig Kontrast haben.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDQZ6FuchNUh2bxSW9PeNv
This commit is contained in:
2026-06-19 08:40:35 +02:00
parent f294f1a9de
commit ede0258cd8
4 changed files with 143 additions and 24 deletions

View File

@@ -9,7 +9,8 @@ declare(strict_types=1);
* "tag" (Badge-Text, Default "Anzeige"), "background" (Bild), "cta.internal"
* (interner Link ohne target=_blank/rel=sponsored, für eigene Seiten/Anker),
* "cta_secondary" (zweiter Button als Outline), "logo_light" (helle Logo-Platte
* für dunkle Firmenlogos).
* für dunkle Firmenlogos), "accent" (CSS-Farbe; überschreibt das Vereins-Rot
* der Buttons für diese Slide, z. B. Sponsor-CI).
* Auto-Rotation + Fortschritts-Indikatoren übernimmt assets/js/banner.js
* (pausiert bei Hover/Fokus, respektiert prefers-reduced-motion).
* Ohne JS: nur der erste Banner ist sichtbar (Graceful Degradation).
@@ -35,9 +36,32 @@ $total = count($banners);
<?php endif; ?>
<ul class="ad-banner__track" id="ad-banner-track" aria-live="off">
<?php foreach ($banners as $i => $banner): ?>
<?php $bg = $banner['background'] ?? null; ?>
<?php $variant = $banner['variant'] ?? null; ?>
<li class="ad-banner__slide<?= $bg ? ' ad-banner__slide--bg' : '' ?><?= $variant ? ' ad-banner__slide--' . e($variant) : '' ?>"<?= $bg ? ' style="--banner-bg: url(' . e(asset($bg)) . ')"' : '' ?> data-banner-slide role="group" aria-roledescription="Folie" aria-label="<?= ($i + 1) ?> von <?= $total ?>"<?= $i === 0 ? '' : ' hidden' ?>>
<?php
$bg = $banner['background'] ?? null;
$variant = $banner['variant'] ?? null;
$accent = $banner['accent'] ?? null;
$classes = 'ad-banner__slide';
if ($bg) {
$classes .= ' ad-banner__slide--bg';
}
if ($accent) {
$classes .= ' ad-banner__slide--accent';
}
if ($variant) {
$classes .= ' ad-banner__slide--' . preg_replace('/[^a-z0-9-]+/', '', strtolower($variant));
}
$styleParts = [];
if ($bg) {
$styleParts[] = '--banner-bg: url(' . asset($bg) . ')';
}
if ($accent) {
$styleParts[] = '--banner-accent: ' . $accent;
}
$style = $styleParts !== [] ? ' style="' . e(implode('; ', $styleParts)) . '"' : '';
?>
<li class="<?= e($classes) ?>"<?= $style ?> data-banner-slide role="group" aria-roledescription="Folie" aria-label="<?= ($i + 1) ?> von <?= $total ?>"<?= $i === 0 ? '' : ' hidden' ?>>
<div class="ad-banner__content">
<span class="ad-banner__tag"><?= e($banner['tag'] ?? 'Anzeige') ?></span>
<p class="ad-banner__title"><?= e($banner['title']) ?></p>