Vereinshistorie-Seite: kompakter Hero, interaktive Museums-Timeline (Scroll-Reveal, Jahres-Sprungnav), Originalbilder unverändert
routes.php enthält auch die fussball-Route (User-WIP, Seite existiert) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,12 +4,14 @@ declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Vollflächiger Hero mit Hintergrundbild/-video, Titel, Intro und CTAs.
|
||||
* Props: $hero (array aus home.json: title, text, image, video?, ctas, align?)
|
||||
* Props: $hero (array aus home.json: title, text, image, video?, ctas, align?, compact?)
|
||||
* align-Varianten: 'center' (mittig zentriert), 'bottom-left' (Default).
|
||||
* compact: true → niedrigere Variante für Unterseiten-Header.
|
||||
*/
|
||||
$align = in_array($hero['align'] ?? '', ['center', 'bottom-left'], true) ? $hero['align'] : 'bottom-left';
|
||||
$compact = !empty($hero['compact']);
|
||||
?>
|
||||
<section class="hero<?= $align === 'center' ? ' hero--center' : '' ?>">
|
||||
<section class="hero<?= $align === 'center' ? ' hero--center' : '' ?><?= $compact ? ' hero--compact' : '' ?>">
|
||||
<div class="hero__media" aria-hidden="true">
|
||||
<?php component('img', ['image' => $hero['image'], 'sizes' => '100vw', 'eager' => true, 'class' => 'hero__img']); ?>
|
||||
<?php if (!empty($hero['video'])): ?>
|
||||
@@ -19,11 +21,15 @@ $align = in_array($hero['align'] ?? '', ['center', 'bottom-left'], true) ? $hero
|
||||
</div>
|
||||
<div class="container hero__content">
|
||||
<h1 class="hero__title"><?= e($hero['title']) ?><?php if (!empty($hero['tagline'])): ?> <span class="hero__tagline"><?= e($hero['tagline']) ?></span><?php endif; ?></h1>
|
||||
<?php if (!empty($hero['text'])): ?>
|
||||
<p class="hero__text"><?= e($hero['text']) ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($hero['ctas'])): ?>
|
||||
<div class="hero__actions">
|
||||
<?php foreach ($hero['ctas'] as $cta): ?>
|
||||
<?php component('cta', ['cta' => $cta]); ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
57
app/components/timeline.php
Normal file
57
app/components/timeline.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Interaktive Vereins-Timeline (Museums-Look). Props:
|
||||
* $events array — Stationen aus data/historie.json (year, text, image{src,width,height,alt})
|
||||
* Bilder werden bewusst in ORIGINALGRÖSSE eingebunden (keine Varianten) —
|
||||
* historisches Material bleibt unangetastet.
|
||||
* timeline.js ergänzt Scroll-Reveal + aktive Jahres-Navigation; ohne JS ist
|
||||
* alles sichtbar (Initial-Versteckt-Zustand nur unter .js-reveal).
|
||||
*/
|
||||
|
||||
// Anker je Jahr — Duplikate (2x 1972) bekommen einen Suffix, die Jahres-Nav
|
||||
// führt auf das jeweils erste Vorkommen.
|
||||
$seen = [];
|
||||
$prepared = [];
|
||||
foreach ($events as $event) {
|
||||
$slug = 'jahr-' . strtolower(preg_replace('/[^a-zA-Z0-9]+/', '-', $event['year']));
|
||||
$first = !isset($seen[$slug]);
|
||||
$seen[$slug] = ($seen[$slug] ?? 0) + 1;
|
||||
$prepared[] = $event + [
|
||||
'id' => $first ? $slug : $slug . '-' . $seen[$slug],
|
||||
'nav' => $slug,
|
||||
];
|
||||
}
|
||||
?>
|
||||
<section class="section timeline-section" data-timeline>
|
||||
<div class="container">
|
||||
<nav class="timeline-nav" aria-label="Zeitsprünge">
|
||||
<ol class="timeline-nav__list">
|
||||
<?php foreach ($prepared as $event): ?>
|
||||
<?php if ($event['id'] === $event['nav']): ?>
|
||||
<li><a href="#<?= e($event['nav']) ?>"><?= e($event['year']) ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</ol>
|
||||
</nav>
|
||||
<ol class="timeline">
|
||||
<?php foreach ($prepared as $i => $event): ?>
|
||||
<li class="timeline__event" id="<?= e($event['id']) ?>" data-nav="<?= e($event['nav']) ?>">
|
||||
<span class="timeline__marker" aria-hidden="true"></span>
|
||||
<article class="timeline__card">
|
||||
<h2 class="timeline__year"><?= e($event['year']) ?></h2>
|
||||
<figure class="timeline__figure">
|
||||
<img src="<?= e(asset($event['image']['src'])) ?>"
|
||||
alt="<?= e($event['image']['alt']) ?>"
|
||||
width="<?= e($event['image']['width']) ?>" height="<?= e($event['image']['height']) ?>"
|
||||
loading="<?= $i === 0 ? 'eager' : 'lazy' ?>" decoding="async">
|
||||
<figcaption class="timeline__text"><?= e($event['text']) ?></figcaption>
|
||||
</figure>
|
||||
</article>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
23
app/pages/historie.php
Normal file
23
app/pages/historie.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Vereinshistorie — schmaler Hero + interaktive Timeline.
|
||||
* Inhalte: data/historie.json (Single Source of Truth).
|
||||
*/
|
||||
$historie = json_load('historie');
|
||||
|
||||
$meta = [
|
||||
'title' => 'Vereinshistorie – Der Katzbachtaler Weg',
|
||||
'description' => 'Erlebe die bewegte Geschichte des TSV 08 Kulmbach – von den ersten Spielen an der Bayreuther Straße 1947 bis zur Hans-Rausch-Sportanlage im Katzbachtal.',
|
||||
'scripts' => ['timeline.js'],
|
||||
];
|
||||
|
||||
component('hero', ['hero' => $historie['hero']]);
|
||||
?>
|
||||
<div class="container section--slim">
|
||||
<p class="timeline-intro"><?= e($historie['intro']) ?></p>
|
||||
</div>
|
||||
<?php
|
||||
component('timeline', ['events' => $historie['events']]);
|
||||
@@ -10,6 +10,8 @@ declare(strict_types=1);
|
||||
* Canonical und Sitemap folgen automatisch.
|
||||
*/
|
||||
return [
|
||||
'' => ['file' => 'home.php', 'sitemap' => true],
|
||||
'404' => ['file' => '404.php', 'sitemap' => false],
|
||||
'' => ['file' => 'home.php', 'sitemap' => true],
|
||||
'fussball' => ['file' => 'fussball.php', 'sitemap' => true],
|
||||
'historie' => ['file' => 'historie.php', 'sitemap' => true],
|
||||
'404' => ['file' => '404.php', 'sitemap' => false],
|
||||
];
|
||||
|
||||
46
data/historie.json
Normal file
46
data/historie.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"hero": {
|
||||
"align": "center",
|
||||
"compact": true,
|
||||
"title": "Vereinshistorie",
|
||||
"tagline": "Der Katzbachtaler Weg seit 1908",
|
||||
"image": {
|
||||
"base": "img/pages/vereinsgelaende",
|
||||
"widths": [1000, 1600],
|
||||
"alt": "Luftaufnahme der Hans-Rausch-Sportanlage im Katzbachtal"
|
||||
}
|
||||
},
|
||||
"intro": "Erlebe die bewegte Geschichte des TSV 08 Kulmbach – von den ersten Spielen an der Bayreuther Straße bis zur Hans-Rausch-Sportanlage im Katzbachtal.",
|
||||
"events": [
|
||||
{
|
||||
"year": "1947",
|
||||
"text": "Zu Beginn unserer Zeit im Fußball spielten wir nicht wie heute im Katzbachtal, sondern an der Bayreuther Straße, wo heute beispielsweise der Schuhmücke zu finden ist.",
|
||||
"image": { "src": "img/historie/1947.png", "width": 519, "height": 352, "alt": "Historische Aufnahme des Sportgeländes an der Bayreuther Straße" }
|
||||
},
|
||||
{
|
||||
"year": "1948",
|
||||
"text": "Die Bilder zeigen die ersten Fußballspiele an der Bayreuther Straße im Jahre 1948.",
|
||||
"image": { "src": "img/historie/1948.jpg", "width": 519, "height": 334, "alt": "Fußballspiel an der Bayreuther Straße im Jahr 1948" }
|
||||
},
|
||||
{
|
||||
"year": "1969",
|
||||
"text": "Im Jahre 1969 wurde die später nach Hans Rausch benannte Sportanlage eingeweiht. Im Bild zu sehen sind Hans Rausch (v. r.) bei Arbeiten an der Sportstätte.",
|
||||
"image": { "src": "img/historie/1969.png", "width": 1070, "height": 730, "alt": "Hans Rausch bei Bauarbeiten an der Sportanlage 1969" }
|
||||
},
|
||||
{
|
||||
"year": "1972",
|
||||
"text": "Im Jahr 1972 wurde das Sportheim im Katzbachtal eingeweiht.",
|
||||
"image": { "src": "img/historie/1972-sportheim.jpg", "width": 519, "height": 272, "alt": "Einweihung des Sportheims im Katzbachtal 1972" }
|
||||
},
|
||||
{
|
||||
"year": "1972",
|
||||
"text": "Zu diesem Anlass spielten wir damals vor fast 10.000 Zuschauern gegen den FC Bayern München.",
|
||||
"image": { "src": "img/historie/1972-fcbayern.png", "width": 519, "height": 322, "alt": "Spiel gegen den FC Bayern München vor fast 10.000 Zuschauern, 1972" }
|
||||
},
|
||||
{
|
||||
"year": "Heute",
|
||||
"text": "Mitten im schönen Katzbachtal liegt nun seit vielen Jahren die Hans-Rausch-Sportanlage des TSV 1908 Kulmbach. Unsere Sportstätte besteht aus einem Großfeldrasenplatz, einem Kleinfeldrasenplatz und einem Hartplatz, der auch im Winter das Training zulässt. Die komplette Sportanlage ist mit Flutlicht ausgestattet.",
|
||||
"image": { "src": "img/historie/heute.jpg", "width": 519, "height": 360, "alt": "Die Hans-Rausch-Sportanlage im Katzbachtal heute" }
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -289,6 +289,184 @@
|
||||
color: var(--clr-text-muted);
|
||||
}
|
||||
|
||||
/* === timeline (Vereinshistorie, Museums-Look) === */
|
||||
.timeline-intro {
|
||||
max-width: 60ch;
|
||||
margin-inline: auto;
|
||||
padding-block: 2.5rem 0;
|
||||
text-align: center;
|
||||
color: var(--clr-text-muted);
|
||||
}
|
||||
|
||||
/* Sprung-Navigation: Jahres-Pills, klebt beim Scrollen oben */
|
||||
.timeline-nav {
|
||||
position: sticky;
|
||||
top: 1rem;
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.timeline-nav__list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.4rem;
|
||||
background: rgb(10 10 10 / 0.82);
|
||||
border: 1px solid var(--clr-glass-border);
|
||||
border-radius: 999px;
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
.timeline-nav a {
|
||||
display: inline-block;
|
||||
padding: 0.35em 1em;
|
||||
font-family: var(--ff-heading);
|
||||
font-size: var(--fs-500);
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
color: var(--clr-text-muted);
|
||||
border-radius: 999px;
|
||||
transition: color var(--transition), background var(--transition);
|
||||
}
|
||||
|
||||
.timeline-nav a:hover {
|
||||
color: var(--clr-text);
|
||||
}
|
||||
|
||||
.timeline-nav a.is-active {
|
||||
color: var(--clr-text);
|
||||
background: var(--clr-accent);
|
||||
}
|
||||
|
||||
/* Mittellinie + Stationen */
|
||||
.timeline {
|
||||
position: relative;
|
||||
display: grid;
|
||||
gap: 4.5rem;
|
||||
padding-block: 1rem 2rem;
|
||||
}
|
||||
|
||||
.timeline::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
width: 2px;
|
||||
transform: translateX(-50%);
|
||||
background: linear-gradient(180deg,
|
||||
transparent,
|
||||
rgb(var(--clr-accent-rgb) / 0.55) 6%,
|
||||
rgb(var(--clr-accent-rgb) / 0.55) 94%,
|
||||
transparent);
|
||||
}
|
||||
|
||||
.timeline__event {
|
||||
position: relative;
|
||||
width: calc(50% - 3.5rem);
|
||||
}
|
||||
|
||||
.timeline__event:nth-child(even) {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.timeline__marker {
|
||||
position: absolute;
|
||||
top: 0.6rem;
|
||||
left: calc(100% + 3.5rem);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
transform: translateX(-50%);
|
||||
background: var(--clr-accent);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 0 4px rgb(var(--clr-accent-rgb) / 0.25), 0 0 18px rgb(var(--clr-accent-rgb) / 0.5);
|
||||
}
|
||||
|
||||
.timeline__event:nth-child(even) .timeline__marker {
|
||||
left: -3.5rem;
|
||||
}
|
||||
|
||||
.timeline__year {
|
||||
font-size: var(--fs-900);
|
||||
color: var(--clr-accent);
|
||||
text-shadow: 0 2px 18px rgb(0 0 0 / 0.5);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.timeline__event:nth-child(odd) .timeline__year {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Museums-Rahmen: Foto wie ein aufgezogener Print, leicht gedreht */
|
||||
.timeline__figure {
|
||||
margin: 0;
|
||||
padding: 0.9rem 0.9rem 1.1rem;
|
||||
background: #f3efe4;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 12px 35px rgb(0 0 0 / 0.5);
|
||||
transform: rotate(-1.2deg);
|
||||
transition: transform var(--transition);
|
||||
}
|
||||
|
||||
.timeline__event:nth-child(even) .timeline__figure {
|
||||
transform: rotate(1.2deg);
|
||||
}
|
||||
|
||||
.timeline__event:hover .timeline__figure {
|
||||
transform: rotate(0deg) scale(1.02);
|
||||
}
|
||||
|
||||
.timeline__figure img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.timeline__text {
|
||||
padding-top: 0.9rem;
|
||||
font-size: var(--fs-500);
|
||||
line-height: 1.45;
|
||||
color: #3a372f;
|
||||
}
|
||||
|
||||
/* Scroll-Reveal nur mit JS (js-reveal), sonst alles sichtbar */
|
||||
.js-reveal .timeline__event {
|
||||
opacity: 0;
|
||||
translate: 0 28px;
|
||||
transition: opacity 600ms ease, translate 600ms ease;
|
||||
}
|
||||
|
||||
.js-reveal .timeline__event.is-visible {
|
||||
opacity: 1;
|
||||
translate: 0 0;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.timeline::before {
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.timeline__event,
|
||||
.timeline__event:nth-child(even) {
|
||||
width: auto;
|
||||
margin-left: 0;
|
||||
padding-left: 2.5rem;
|
||||
}
|
||||
|
||||
.timeline__marker,
|
||||
.timeline__event:nth-child(even) .timeline__marker {
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.timeline__event:nth-child(odd) .timeline__year {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
/* === partners === */
|
||||
.partners > .container {
|
||||
text-align: center;
|
||||
|
||||
BIN
public/assets/img/historie/1947.png
Normal file
BIN
public/assets/img/historie/1947.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 317 KiB |
BIN
public/assets/img/historie/1948.jpg
Normal file
BIN
public/assets/img/historie/1948.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
public/assets/img/historie/1969.png
Normal file
BIN
public/assets/img/historie/1969.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
BIN
public/assets/img/historie/1972-fcbayern.png
Normal file
BIN
public/assets/img/historie/1972-fcbayern.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 334 KiB |
BIN
public/assets/img/historie/1972-sportheim.jpg
Normal file
BIN
public/assets/img/historie/1972-sportheim.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
public/assets/img/historie/heute.jpg
Normal file
BIN
public/assets/img/historie/heute.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
52
public/assets/js/timeline.js
Normal file
52
public/assets/js/timeline.js
Normal file
@@ -0,0 +1,52 @@
|
||||
/* Timeline: Scroll-Reveal der Stationen + aktive Jahres-Navigation.
|
||||
Progressive Enhancement — ohne JS ist alles sichtbar (js-reveal-Scope). */
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var section = document.querySelector('[data-timeline]');
|
||||
if (!section) {
|
||||
return;
|
||||
}
|
||||
|
||||
var events = section.querySelectorAll('.timeline__event');
|
||||
var navLinks = section.querySelectorAll('.timeline-nav a');
|
||||
|
||||
if (!('IntersectionObserver' in window)) {
|
||||
return;
|
||||
}
|
||||
|
||||
section.classList.add('js-reveal');
|
||||
|
||||
var reveal = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('is-visible');
|
||||
reveal.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.15, rootMargin: '0px 0px -8% 0px' });
|
||||
|
||||
// Aktives Jahr in der Sprung-Navigation markieren.
|
||||
var spy = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (!entry.isIntersecting) {
|
||||
return;
|
||||
}
|
||||
var target = '#' + entry.target.getAttribute('data-nav');
|
||||
navLinks.forEach(function (link) {
|
||||
var active = link.getAttribute('href') === target;
|
||||
link.classList.toggle('is-active', active);
|
||||
if (active) {
|
||||
link.setAttribute('aria-current', 'true');
|
||||
} else {
|
||||
link.removeAttribute('aria-current');
|
||||
}
|
||||
});
|
||||
});
|
||||
}, { rootMargin: '-35% 0px -55% 0px' });
|
||||
|
||||
events.forEach(function (event) {
|
||||
reveal.observe(event);
|
||||
spy.observe(event);
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user