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:
@@ -9,7 +9,8 @@ declare(strict_types=1);
|
|||||||
* "tag" (Badge-Text, Default "Anzeige"), "background" (Bild), "cta.internal"
|
* "tag" (Badge-Text, Default "Anzeige"), "background" (Bild), "cta.internal"
|
||||||
* (interner Link ohne target=_blank/rel=sponsored, für eigene Seiten/Anker),
|
* (interner Link ohne target=_blank/rel=sponsored, für eigene Seiten/Anker),
|
||||||
* "cta_secondary" (zweiter Button als Outline), "logo_light" (helle Logo-Platte
|
* "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
|
* Auto-Rotation + Fortschritts-Indikatoren übernimmt assets/js/banner.js
|
||||||
* (pausiert bei Hover/Fokus, respektiert prefers-reduced-motion).
|
* (pausiert bei Hover/Fokus, respektiert prefers-reduced-motion).
|
||||||
* Ohne JS: nur der erste Banner ist sichtbar (Graceful Degradation).
|
* Ohne JS: nur der erste Banner ist sichtbar (Graceful Degradation).
|
||||||
@@ -35,9 +36,32 @@ $total = count($banners);
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<ul class="ad-banner__track" id="ad-banner-track" aria-live="off">
|
<ul class="ad-banner__track" id="ad-banner-track" aria-live="off">
|
||||||
<?php foreach ($banners as $i => $banner): ?>
|
<?php foreach ($banners as $i => $banner): ?>
|
||||||
<?php $bg = $banner['background'] ?? null; ?>
|
<?php
|
||||||
<?php $variant = $banner['variant'] ?? null; ?>
|
$bg = $banner['background'] ?? 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' ?>>
|
$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">
|
<div class="ad-banner__content">
|
||||||
<span class="ad-banner__tag"><?= e($banner['tag'] ?? 'Anzeige') ?></span>
|
<span class="ad-banner__tag"><?= e($banner['tag'] ?? 'Anzeige') ?></span>
|
||||||
<p class="ad-banner__title"><?= e($banner['title']) ?></p>
|
<p class="ad-banner__title"><?= e($banner['title']) ?></p>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"text": "Beratung, Software & Implementierung aus einer Hand. Wir entwickeln Intranets, Kundenportale, Apps und Individualsoftware – inklusive Einführung, sodass sie im Alltag tatsächlich ankommt.",
|
"text": "Beratung, Software & Implementierung aus einer Hand. Wir entwickeln Intranets, Kundenportale, Apps und Individualsoftware – inklusive Einführung, sodass sie im Alltag tatsächlich ankommt.",
|
||||||
"logo": "img/banners/breadcrumb-solutions.svg",
|
"logo": "img/banners/breadcrumb-solutions.svg",
|
||||||
"logo_light": true,
|
"logo_light": true,
|
||||||
|
"accent": "#6cbbbc",
|
||||||
"background": "img/banners/breadcrumb-hero-1600.jpg",
|
"background": "img/banners/breadcrumb-hero-1600.jpg",
|
||||||
"cta": { "label": "Lösungen entdecken", "url": "https://www.breadcrumb-solutions.de/bc/de/loesungen/" },
|
"cta": { "label": "Lösungen entdecken", "url": "https://www.breadcrumb-solutions.de/bc/de/loesungen/" },
|
||||||
"cta_secondary": { "label": "Über uns", "url": "https://www.breadcrumb-solutions.de/bc/de/unternehmen/ueber-uns/" }
|
"cta_secondary": { "label": "Über uns", "url": "https://www.breadcrumb-solutions.de/bc/de/unternehmen/ueber-uns/" }
|
||||||
|
|||||||
@@ -907,6 +907,32 @@
|
|||||||
box-shadow: var(--shadow-card);
|
box-shadow: var(--shadow-card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Crossfade-Übergang (nur mit JS): Slides im selben Grid-Feld stapeln, sodass
|
||||||
|
die Höhe konstant bleibt (kein Sprung) und ein- wie ausgehende Folie sauber
|
||||||
|
überblenden. Die ausgehende driftet leicht nach unten, die eingehende steigt
|
||||||
|
auf. Ohne JS greift weiter .ad-banner__slide[hidden] (nur erste Folie).
|
||||||
|
Reduced-Motion: globaler Kill-Switch in reset.css macht den Wechsel instant. */
|
||||||
|
.ad-banner.is-enhanced .ad-banner__track {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ad-banner.is-enhanced .ad-banner__slide {
|
||||||
|
grid-area: 1 / 1;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(8px);
|
||||||
|
pointer-events: none;
|
||||||
|
transition: opacity 0.55s ease, transform 0.55s ease, visibility 0s linear 0.55s;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ad-banner.is-enhanced .ad-banner__slide.is-active {
|
||||||
|
opacity: 1;
|
||||||
|
transform: none;
|
||||||
|
pointer-events: auto;
|
||||||
|
visibility: visible;
|
||||||
|
transition: opacity 0.55s ease, transform 0.55s ease, visibility 0s;
|
||||||
|
}
|
||||||
|
|
||||||
/* Optionaler Hintergrund pro Banner (data: "background").
|
/* Optionaler Hintergrund pro Banner (data: "background").
|
||||||
Dunkler Verlauf über dem Bild hält Text & CTA links lesbar. */
|
Dunkler Verlauf über dem Bild hält Text & CTA links lesbar. */
|
||||||
.ad-banner__slide--bg {
|
.ad-banner__slide--bg {
|
||||||
@@ -918,6 +944,30 @@
|
|||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Pro-Slide-Akzentfarbe (data: "accent") — z. B. Sponsor-CI statt Vereins-Rot.
|
||||||
|
--banner-accent kommt als Inline-Variable von der Komponente. */
|
||||||
|
.ad-banner__slide--accent {
|
||||||
|
--clr-accent: var(--banner-accent);
|
||||||
|
--clr-accent-dark: color-mix(in srgb, var(--banner-accent) 82%, #000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Solider Button: dunkler, aus der Akzentfarbe abgeleiteter Text — helle
|
||||||
|
Akzentfarben (z. B. Teal) hätten mit Weiß zu wenig Kontrast (WCAG 1.4.3). */
|
||||||
|
.ad-banner__slide--accent .ad-banner__cta.btn:not(.btn--outline) {
|
||||||
|
color: color-mix(in srgb, var(--banner-accent) 22%, #000);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ad-banner__slide--accent .ad-banner__cta.btn--outline {
|
||||||
|
color: var(--banner-accent);
|
||||||
|
border-color: var(--banner-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ad-banner__slide--accent .ad-banner__cta.btn--outline:hover {
|
||||||
|
background: color-mix(in srgb, var(--banner-accent) 16%, transparent);
|
||||||
|
border-color: var(--banner-accent);
|
||||||
|
color: var(--banner-accent);
|
||||||
|
}
|
||||||
|
|
||||||
.ad-banner__slide--bg .ad-banner__text {
|
.ad-banner__slide--bg .ad-banner__text {
|
||||||
color: var(--clr-text);
|
color: var(--clr-text);
|
||||||
}
|
}
|
||||||
@@ -2343,7 +2393,7 @@
|
|||||||
|
|
||||||
/* === membership (Mitglied werden) === */
|
/* === membership (Mitglied werden) === */
|
||||||
.membership-signup__inner {
|
.membership-signup__inner {
|
||||||
max-width: 820px;
|
max-width: 720px;
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2359,43 +2409,78 @@
|
|||||||
color: var(--clr-text-muted);
|
color: var(--clr-text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Beitragstabelle und Formular mittig, schlanke Spaltenbreite für Lesbarkeit */
|
/* Eine einzige Karte: Beitragsinfo + Formular zusammen, durch Hairline getrennt. */
|
||||||
.membership-signup .fee-table {
|
.membership-card {
|
||||||
max-width: 560px;
|
padding: clamp(1.5rem, 1rem + 2.5vw, 2.5rem);
|
||||||
margin-inline: auto;
|
background: var(--clr-glass-bg);
|
||||||
|
border: 1px solid var(--clr-glass-border);
|
||||||
|
border-radius: var(--radius-card);
|
||||||
}
|
}
|
||||||
|
|
||||||
.membership-signup .form {
|
/* Verschachtelte Bausteine verlieren ihre eigene Karten-Optik (eine Karte, ein Rahmen). */
|
||||||
margin-inline: auto;
|
.membership-card .fee-table,
|
||||||
|
.membership-card .form {
|
||||||
|
max-width: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.membership-signup .fee-table + .form {
|
.membership-card .fee-table {
|
||||||
margin-top: 2rem;
|
padding-bottom: 1.75rem;
|
||||||
|
margin-bottom: 1.75rem;
|
||||||
|
border-bottom: 1px solid var(--clr-glass-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Turnen-Hinweise: ruhige Liste, Icon links, dezente Hairline als Trenner. */
|
||||||
.membership-notes {
|
.membership-notes {
|
||||||
display: grid;
|
|
||||||
gap: 1.5rem;
|
|
||||||
margin: 0 0 2.5rem;
|
margin: 0 0 2.5rem;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
.membership-notes__item {
|
||||||
.membership-notes {
|
display: flex;
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
gap: 1rem;
|
||||||
}
|
padding: 1.25rem 0;
|
||||||
|
border-bottom: 1px solid var(--clr-glass-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.membership-notes__item:first-child {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.membership-notes__item:last-child {
|
||||||
|
padding-bottom: 0;
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.membership-notes__icon {
|
||||||
|
flex: none;
|
||||||
|
margin-top: 0.15rem;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 1;
|
||||||
|
color: var(--clr-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.membership-notes__title {
|
.membership-notes__title {
|
||||||
font-size: var(--fs-650);
|
font-size: var(--fs-650);
|
||||||
line-height: var(--lh-heading);
|
line-height: var(--lh-heading);
|
||||||
margin-bottom: 0.4rem;
|
margin-bottom: 0.25rem;
|
||||||
padding-top: 0.75rem;
|
|
||||||
border-top: 2px solid var(--clr-accent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.membership-notes__body p {
|
||||||
|
color: var(--clr-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Download-Block: alles sauber mittig, Texte in lesbarer Breite. */
|
||||||
.membership-download {
|
.membership-download {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2411,7 +2496,8 @@
|
|||||||
|
|
||||||
.membership-download__hint,
|
.membership-download__hint,
|
||||||
.membership-download__contact {
|
.membership-download__contact {
|
||||||
margin-top: 0.75rem;
|
margin: 0;
|
||||||
|
max-width: 48ch;
|
||||||
font-size: var(--fs-400);
|
font-size: var(--fs-400);
|
||||||
color: var(--clr-text-muted);
|
color: var(--clr-text-muted);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,8 @@
|
|||||||
function activate(i) {
|
function activate(i) {
|
||||||
index = (i % slides.length + slides.length) % slides.length;
|
index = (i % slides.length + slides.length) % slides.length;
|
||||||
slides.forEach(function (slide, n) {
|
slides.forEach(function (slide, n) {
|
||||||
slide.hidden = n !== index;
|
// Sichtbarkeit per Klasse → CSS blendet über (siehe .is-enhanced).
|
||||||
|
slide.classList.toggle('is-active', n === index);
|
||||||
});
|
});
|
||||||
dots.forEach(function (dot, n) {
|
dots.forEach(function (dot, n) {
|
||||||
var on = n === index;
|
var on = n === index;
|
||||||
@@ -110,6 +111,13 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Crossfade aktivieren: Slides stapeln (CSS) und Sichtbarkeit per Klasse
|
||||||
|
// steuern. Vorher gesetzte hidden-Attribute (No-JS-Fallback) entfernen.
|
||||||
|
root.classList.add('is-enhanced');
|
||||||
|
slides.forEach(function (slide) {
|
||||||
|
slide.hidden = false;
|
||||||
|
});
|
||||||
|
|
||||||
activate(0);
|
activate(0);
|
||||||
syncPaused();
|
syncPaused();
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user