Ansprechpartner-Sektion: person-card/person-grid-Komponenten, Daten in ansprechpartner.json, Vereins-Platzhalter statt AdobeStock, tel:/mailto korrekt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 22:20:14 +02:00
parent 72c2efaf80
commit 335749b6d0
9 changed files with 191 additions and 1 deletions

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/**
* Visitenkarte für eine Person. Props:
* $person array{department, name, phone?, email?, image?}
* Ohne image greift der Vereins-Platzhalter (Silhouette im TSV-Trikot).
* Kontakt-Link: phone → tel:, sonst email → mailto:.
*/
$image = $person['image'] ?? null;
$src = $image ?: 'img/persons/platzhalter.jpg';
if (!empty($person['phone'])) {
$contactHref = 'tel:' . preg_replace('/[^+\d]/', '', $person['phone']);
$contactLabel = $person['phone'];
$contactIcon = 'telephone';
} elseif (!empty($person['email'])) {
$contactHref = 'mailto:' . $person['email'];
$contactLabel = $person['email'];
$contactIcon = 'envelope';
} else {
$contactHref = null;
}
?>
<article class="person-card">
<div class="person-card__media">
<img src="<?= e(asset($src)) ?>"
alt="<?= $image ? e($person['name']) : '' ?>"
width="480" height="600" loading="lazy" decoding="async">
</div>
<div class="person-card__body">
<h3 class="person-card__department"><?= e($person['department']) ?></h3>
<p class="person-card__name"><?= e($person['name']) ?></p>
<?php if ($contactHref !== null): ?>
<a class="person-card__contact" href="<?= e($contactHref) ?>">
<?= icon($contactIcon) ?><span><?= e($contactLabel) ?></span>
</a>
<?php endif; ?>
</div>
</article>

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
/**
* Ansprechpartner-Grid aus data/ansprechpartner.json.
* Optionaler Prop: $anchor (Sektions-ID, Default 'ansprechpartner').
*/
$data = json_load('ansprechpartner');
$anchor = $anchor ?? 'ansprechpartner';
?>
<section class="section persons" id="<?= e($anchor) ?>">
<div class="container">
<h2><?= e($data['title']) ?></h2>
<?php if (!empty($data['intro'])): ?>
<p class="persons__intro"><?= e($data['intro']) ?></p>
<?php endif; ?>
<ul class="persons__grid">
<?php foreach ($data['persons'] as $person): ?>
<li>
<?php component('person-card', ['person' => $person]); ?>
</li>
<?php endforeach; ?>
</ul>
</div>
</section>

36
data/ansprechpartner.json Normal file
View File

@@ -0,0 +1,36 @@
{
"title": "Ansprechpartner",
"intro": "Du hast Fragen zu einer Abteilung? Unsere Ansprechpartner helfen dir direkt weiter.",
"persons": [
{
"department": "Herrenfußball",
"name": "Benedikt Höfner",
"phone": "+49 160 97516404",
"image": "img/persons/benedikt-hoefner.jpg"
},
{
"department": "Damenfußball",
"name": "Sam Kern",
"phone": "+49 176 91371064",
"image": "img/persons/sam-kern.jpg"
},
{
"department": "Jugendfußball",
"name": "Fabian Früh",
"phone": "+49 160 97516797",
"image": "img/persons/fabian-frueh.jpg"
},
{
"department": "Jugendfußball",
"name": "Kerstin Tauber",
"phone": "+49 176 38957526",
"image": null
},
{
"department": "Turnen",
"name": "Maxi Roßberg",
"email": "turnabteilung.tsv08kulmbach@gmail.com",
"image": null
}
]
}

View File

@@ -277,6 +277,90 @@
object-fit: contain; object-fit: contain;
} }
/* === persons (Ansprechpartner-Visitenkarten) === */
.persons > .container {
text-align: center;
}
.persons__intro {
margin-inline: auto;
margin-top: 1rem;
max-width: 60ch;
color: var(--clr-text-muted);
}
.persons__grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
margin-top: 2.5rem;
text-align: left;
}
.person-card {
display: grid;
height: 100%;
background: var(--clr-glass-bg);
border: 1px solid var(--clr-glass-border);
border-radius: var(--radius-card);
overflow: hidden;
transition: transform var(--transition), box-shadow var(--transition);
}
.person-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-card);
}
.person-card__media img {
width: 100%;
height: auto;
aspect-ratio: 4 / 5;
object-fit: cover;
}
.person-card__body {
display: grid;
gap: 0.2rem;
padding: 1.1rem 1.2rem 1.2rem;
/* Verlauf knüpft an die dunklen Studiofotos an */
background: linear-gradient(180deg, rgb(20 20 20 / 0) 0%, rgb(20 20 20 / 0.35) 100%);
}
.person-card__department {
font-size: var(--fs-650);
letter-spacing: 0.04em;
}
.person-card__name {
color: var(--clr-text-muted);
}
.person-card__contact {
display: inline-flex;
align-items: center;
gap: 0.5rem;
margin-top: 0.5rem;
padding-block: 0.3rem;
color: var(--clr-text);
text-decoration: none;
font-size: var(--fs-500);
}
.person-card__contact .icon {
color: var(--clr-accent);
flex: none;
}
.person-card__contact span {
overflow-wrap: anywhere;
}
.person-card__contact:hover {
text-decoration: underline;
text-underline-offset: 0.25em;
}
/* === contact form === */ /* === contact form === */
.contact__inner { .contact__inner {
display: grid; display: grid;
@@ -462,7 +546,9 @@
.ad-banner__subline { .ad-banner__subline {
margin-top: 0.75rem; margin-top: 0.75rem;
max-width: 70ch; /* Volle Breite, damit der Dank in einer Zeile steht; auf Mobil bricht er natürlich um. */
max-width: none;
text-wrap: balance;
} }
.ad-banner__track { .ad-banner__track {

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M3.654 1.328a.678.678 0 0 0-1.015-.063L1.605 2.3c-.483.484-.661 1.169-.45 1.77a17.6 17.6 0 0 0 4.168 6.608 17.6 17.6 0 0 0 6.608 4.168c.601.211 1.286.033 1.77-.45l1.034-1.034a.678.678 0 0 0-.063-1.015l-2.307-1.794a.68.68 0 0 0-.58-.122l-2.19.547a1.75 1.75 0 0 1-1.657-.459L5.482 8.062a1.75 1.75 0 0 1-.46-1.657l.548-2.19a.68.68 0 0 0-.122-.58zM1.884.511a1.745 1.745 0 0 1 2.612.163L6.29 2.98c.329.423.445.974.315 1.494l-.547 2.19a.68.68 0 0 0 .178.643l2.457 2.457a.68.68 0 0 0 .644.178l2.189-.547a1.75 1.75 0 0 1 1.494.315l2.306 1.794c.829.645.905 1.87.163 2.611l-1.034 1.034c-.74.74-1.846 1.065-2.877.702a18.6 18.6 0 0 1-7.01-4.42 18.6 18.6 0 0 1-4.42-7.009c-.362-1.03-.037-2.137.703-2.877z"/></svg>

After

Width:  |  Height:  |  Size: 770 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB