Compare commits

...

2 Commits

Author SHA1 Message Date
fs
133c3f926e „Trainerteam" statt „Übungsleitung", Eva Wünsch mit Sternchen
Eva Wünsch aus der Turnabteilung am 31.07.2026: „Der Sternchentext müsste
eigentlich sein, dass die Person mit dem Sternchen Übungsleiter ist. Von daher
müsste vor den Namen 'Trainerteam' stehen. Einige sind auch nicht ÜL."

Ich hatte davon nur die Fußnote erledigt. Das Label blieb „Übungsleitung" und
damit stand die falsche Aussage weiter auf der Seite: neun namentlich genannte
Personen liefen unter „Übungsleitung", vier davon sind es. Das ist keine
Formulierungsfrage, die Seite behauptete etwas über echte Menschen.

Label jetzt „Trainerteam:" an beiden Stellen (Trainingszeit und Gruppe), der
Stern bleibt die Auszeichnung und die Fußnote erklärt ihn. So stand es auch auf
der alten Seite: „TRAINERTEAM (* = ÜBUNGSLEITER)". Ein Kommentar an der Stelle
sagt, warum es nicht zurückbenannt werden darf.

Dabei fiel ein zweiter Datenfehler auf: die alte Seite führt „Annelie Frisque*,
Eva Wünsch*", bei uns stand Eva Wünsch ohne Stern. Sie ist Übungsleiterin.

Geprüft: „Übungsleitung" kommt auf keiner der sechs Turn-Seiten mehr vor, die
Grashüpfer-Zeile lautet jetzt Zeichen für Zeichen wie in Evas Screenshot. Die
Fußnote erscheint nur in Sektionen mit mehreren Gruppen — nachgezählt, dass
beide Disziplinen mit Sternchen-Namen mehrere Gruppen haben, die
Einzelgruppen-Disziplinen tragen keine Sterne.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:57:31 +02:00
fs
146471bae8 Wettkampfturnen: Karten offen untereinander statt hinter Tabs
Bei zwei Gruppen verstecken Tabs mehr Inhalt als sie ordnen, und die offene
Kartenreihe hält den Bento-Eindruck der Seite (Entscheidung Felix). Das
Kinderturnen bleibt bei Tabs, dort sind es drei textreiche Gruppen — die
Flamingos-Karte allein ist am Handy 2142px hoch.

.group-list--stacked lag vollständig im CSS, wurde aber von KEINEM PHP
ausgegeben: totes CSS aus der Zeit vor den Tabs, das heute morgen sogar noch
meine minmax(0, …)-Korrektur mitbekommen hat, ohne dass es jemand sehen konnte.
Jetzt ist es lebendig. Dritter toter Rest heute: "layout": "blocks" in
data/turnen.json las ebenfalls niemand, ist raus.

Der Karteninhalt steht dafür jetzt einmal statt zweimal. Er wird in zwei
Fassungen gebraucht (Tab-Panel mit role/aria und gestapelt ohne) und ist
deshalb eine Closure geworden — eine zweite Kopie wäre die Stelle, an der beide
in einem halben Jahr auseinanderlaufen.

Die Überschrift ist visually-hidden, nicht entfernt: sonst hätte die Sektion
keinen zugänglichen Namen und die Überschriften-Gliederung eine Lücke. Dasselbe
Muster wie $title_hidden in team-cards. Damit braucht die Seite auch kein
group-tabs.js mehr, das ich zwei Commits vorher erst dazugeschrieben hatte.

Zwei Nachbesserungen, beide gemessen:
- Die versteckte Überschrift hinterließ ein Loch. Zwischen Dashboard und erster
  Karte standen 160px (80 unten von der einen Sektion, 80 oben von der anderen),
  in denen nur nackte Hintergrund-Textur zu sehen war — vorher füllte die
  Überschrift diesen Raum. .group-list-section--tight nimmt das obere Padding
  weg, jetzt 80px.
- Die gestapelten Karten hatten feste 40px Innenabstand; auf --pad-card-lg
  umgestellt, dieselbe Korrektur wie heute morgen beim Tab-Panel. Am Desktop
  unverändert 40px, am Handy 28px.

Nicht behoben, weil kein Fehler: die Hintergrund-Textur wirkt in leeren Flächen
weich. Sie ist 1920x2545 groß und wird bei 1478px Fensterbreite auf Faktor 0,77
VERKLEINERT, ist also nicht unscharf gerechnet. Sichtbar wurde sie durch den
Leerraum plus background-attachment: fixed, das sie beim Scrollen stehen lässt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:56:21 +02:00
4 changed files with 111 additions and 56 deletions

View File

@@ -5,7 +5,7 @@ declare(strict_types=1);
/** /**
* Turn-Disziplin im einheitlichen Bento-Look (wie die Mannschaftsseiten). Props: * Turn-Disziplin im einheitlichen Bento-Look (wie die Mannschaftsseiten). Props:
* $location array{venue, street?, zip?, city?} (optional) — Trainingsort + „Route planen" * $location array{venue, street?, zip?, city?} (optional) — Trainingsort + „Route planen"
* $lead string (optional) — verantwortliche Übungsleitung (Akzent-Kachel) * $lead string (optional) — Ansprechperson der Disziplin (Akzent-Kachel)
* $notice string (optional) — hervorgehobener Hinweis (z. B. Wartelisten-Status) * $notice string (optional) — hervorgehobener Hinweis (z. B. Wartelisten-Status)
* $noticeTitle string (optional) — Überschrift dazu * $noticeTitle string (optional) — Überschrift dazu
* $noticeTone 'info' (Default) | 'stop' — Tonlage, siehe components/notice.php * $noticeTone 'info' (Default) | 'stop' — Tonlage, siehe components/notice.php
@@ -24,6 +24,13 @@ declare(strict_types=1);
* das Label kennt das Geschlecht der Person nicht (Haus-Stil Paarformen). * das Label kennt das Geschlecht der Person nicht (Haus-Stil Paarformen).
* $footnote string (optional) — Erläuterung (z. B. zur Sternchen-Kennzeichnung) * $footnote string (optional) — Erläuterung (z. B. zur Sternchen-Kennzeichnung)
* $groupsTitle string (optional, Default 'Unsere Gruppen') * $groupsTitle string (optional, Default 'Unsere Gruppen')
* $groupsTitleHidden bool (optional) — H2 bleibt im DOM (Dokumentgliederung für
* Screenreader, Sektion behält ihren Namen), ist aber visuell versteckt.
* Dasselbe Muster wie $title_hidden in team-cards. NICHT die Überschrift
* weglassen: dann hätte die Sektion keinen zugänglichen Namen mehr.
* $layout 'tabs' (Default) | 'stacked' — greift nur bei beschreibenden Gruppen.
* 'stacked' zeigt alle Karten offen untereinander (wenige Gruppen),
* 'tabs' ordnet viele textreiche Gruppen (Kinderturnen).
* $anchor string (optional, Default 'training') * $anchor string (optional, Default 'training')
* *
* Eine Gruppe → Trainingszeiten landen als Bento-Kachel im Dashboard. * Eine Gruppe → Trainingszeiten landen als Bento-Kachel im Dashboard.
@@ -32,6 +39,8 @@ declare(strict_types=1);
*/ */
$anchor = $anchor ?? 'training'; $anchor = $anchor ?? 'training';
$groupsTitle = $groupsTitle ?? 'Unsere Gruppen'; $groupsTitle = $groupsTitle ?? 'Unsere Gruppen';
$groupsTitleHidden = $groupsTitleHidden ?? false;
$layout = ($layout ?? 'tabs') === 'stacked' ? 'stacked' : 'tabs';
$lead = $lead ?? ''; $lead = $lead ?? '';
$notice = $notice ?? ''; $notice = $notice ?? '';
$noticeTitle = $noticeTitle ?? ''; $noticeTitle = $noticeTitle ?? '';
@@ -65,7 +74,14 @@ $mapsQuery = $hasVenue ? rawurlencode(trim(
', ' ', '
)) : ''; )) : '';
/** Eine Trainingszeit (Tag/Zeit + optional Label, Notiz, Übungsleitung) ausgeben. */ /**
* Eine Trainingszeit (Tag/Zeit + optional Label, Notiz, Trainerteam) ausgeben.
*
* Das Label heißt „Trainerteam", nicht „Übungsleitung": in der Liste stehen auch
* Personen, die KEINE Übungsleitung sind (Eva Wünsch aus der Abteilung, 31.07.2026).
* Wer eine ist, trägt ein Sternchen, und das erklärt die Fußnote der Sektion. Die alte
* Seite schrieb dafür „TRAINERTEAM (* = ÜBUNGSLEITER)". Nicht zurückbenennen.
*/
$renderSession = static function (array $s): void { $renderSession = static function (array $s): void {
?> ?>
<li class="schedule-row"> <li class="schedule-row">
@@ -80,12 +96,62 @@ $renderSession = static function (array $s): void {
<span class="schedule-row__note text-muted"><?= e($s['note']) ?></span> <span class="schedule-row__note text-muted"><?= e($s['note']) ?></span>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($s['trainers'])): ?> <?php if (!empty($s['trainers'])): ?>
<span class="schedule-row__trainers"><span class="schedule-row__trainers-label">Übungsleitung:</span> <?= e(implode(', ', $s['trainers'])) ?></span> <span class="schedule-row__trainers"><span class="schedule-row__trainers-label">Trainerteam:</span> <?= e(implode(', ', $s['trainers'])) ?></span>
<?php endif; ?> <?php endif; ?>
</li> </li>
<?php <?php
}; };
/**
* Inhalt einer beschreibenden Gruppen-Karte (ohne das <article> drumherum). Als Closure,
* weil es diese Karte in zwei Fassungen gibt: als Tab-Panel (mit role/aria) und gestapelt
* (ohne). Vorher stand das Markup nur in der Tab-Fassung; eine zweite Kopie daneben wäre
* die Stelle, an der beide auseinanderlaufen.
*/
$renderCardBody = static function (array $g) use ($renderSession): void {
?>
<div class="group-card__body">
<div class="group-card__header">
<h3 class="group-card__name"><?= e($g['name']) ?></h3>
<?php if (!empty($g['level'])): ?>
<p class="group-card__level"><?= e($g['level']) ?></p>
<?php endif; ?>
</div>
<?php if (!empty($g['intro'])): ?>
<p class="group-card__intro"><?= e($g['intro']) ?></p>
<?php endif; ?>
<?php foreach ($g['details'] ?? [] as $det): ?>
<?php if (!empty($det['heading'])): ?>
<h4 class="group-card__detail-heading"><?= e($det['heading']) ?></h4>
<?php endif; ?>
<?php if (!empty($det['text'])): ?>
<p class="group-card__detail-text"><?= e($det['text']) ?></p>
<?php endif; ?>
<?php endforeach; ?>
<?php if (!empty($g['recommendation'])): ?>
<p class="group-card__recommendation text-muted"><?= e($g['recommendation']) ?></p>
<?php endif; ?>
</div>
<?php if (!empty($g['sessions'])): ?>
<div class="group-card__schedule">
<p class="group-card__schedule-title"><?= icon('clock', 'group-card__schedule-icon') ?>Trainingszeiten</p>
<ul class="schedule-list">
<?php foreach ($g['sessions'] as $s): $renderSession($s); endforeach; ?>
</ul>
<?php if (!empty($g['contact'])): ?>
<p class="group-card__trainers"><span class="schedule-row__trainers-label">Kontakt:</span> <?= e($g['contact']) ?></p>
<?php endif; ?>
<?php if (!empty($g['trainers'])): ?>
<p class="group-card__trainers"><span class="schedule-row__trainers-label">Trainerteam:</span> <?= e(implode(', ', $g['trainers'])) ?></p>
<?php endif; ?>
<?php if (!empty($g['note'])): ?>
<p class="group-card__note text-muted"><?= e($g['note']) ?></p>
<?php endif; ?>
</div>
<?php endif; ?>
<?php
};
$soleSessions = $single ? ($groups[0]['sessions'] ?? []) : []; $soleSessions = $single ? ($groups[0]['sessions'] ?? []) : [];
$soleNote = $single ? ($groups[0]['note'] ?? '') : ''; $soleNote = $single ? ($groups[0]['note'] ?? '') : '';
?> ?>
@@ -150,10 +216,22 @@ $soleNote = $single ? ($groups[0]['note'] ?? '') : '';
</section> </section>
<?php if (!$single): ?> <?php if (!$single): ?>
<section class="section group-list-section" id="gruppen"> <section class="section group-list-section<?= $groupsTitleHidden ? ' group-list-section--tight' : '' ?>" id="gruppen">
<div class="container"> <div class="container">
<h2 class="group-list__title"><?= e($groupsTitle) ?></h2> <h2 class="group-list__title<?= $groupsTitleHidden ? ' visually-hidden' : '' ?>"><?= e($groupsTitle) ?></h2>
<?php if ($rich): ?> <?php if ($rich && $layout === 'stacked'): ?>
<?php /* Karten direkt untereinander, alle sichtbar. Für Disziplinen mit wenigen Gruppen:
Tabs verstecken dort mehr Inhalt als sie ordnen, und die offene Kartenreihe hält
den Bento-Eindruck der Seite. Bei vielen textreichen Gruppen (Kinderturnen) sind
Tabs weiter richtig, sonst wird die Seite endlos. */ ?>
<div class="group-list group-list--stacked">
<?php foreach ($groups as $g): ?>
<article class="group-card">
<?php $renderCardBody($g); ?>
</article>
<?php endforeach; ?>
</div>
<?php elseif ($rich): ?>
<div class="group-tabs" data-group-tabs> <div class="group-tabs" data-group-tabs>
<div class="group-tabs__bar" role="tablist" aria-label="<?= e($groupsTitle) ?>"> <div class="group-tabs__bar" role="tablist" aria-label="<?= e($groupsTitle) ?>">
<?php foreach ($groups as $i => $g): ?> <?php foreach ($groups as $i => $g): ?>
@@ -170,45 +248,7 @@ $soleNote = $single ? ($groups[0]['note'] ?? '') : '';
aria-labelledby="gtab-<?= $i ?>" aria-labelledby="gtab-<?= $i ?>"
tabindex="0" tabindex="0"
> >
<div class="group-card__body"> <?php $renderCardBody($g); ?>
<div class="group-card__header">
<h3 class="group-card__name"><?= e($g['name']) ?></h3>
<?php if (!empty($g['level'])): ?>
<p class="group-card__level"><?= e($g['level']) ?></p>
<?php endif; ?>
</div>
<?php if (!empty($g['intro'])): ?>
<p class="group-card__intro"><?= e($g['intro']) ?></p>
<?php endif; ?>
<?php foreach ($g['details'] ?? [] as $det): ?>
<?php if (!empty($det['heading'])): ?>
<h4 class="group-card__detail-heading"><?= e($det['heading']) ?></h4>
<?php endif; ?>
<?php if (!empty($det['text'])): ?>
<p class="group-card__detail-text"><?= e($det['text']) ?></p>
<?php endif; ?>
<?php endforeach; ?>
<?php if (!empty($g['recommendation'])): ?>
<p class="group-card__recommendation text-muted"><?= e($g['recommendation']) ?></p>
<?php endif; ?>
</div>
<?php if (!empty($g['sessions'])): ?>
<div class="group-card__schedule">
<p class="group-card__schedule-title"><?= icon('clock', 'group-card__schedule-icon') ?>Trainingszeiten</p>
<ul class="schedule-list">
<?php foreach ($g['sessions'] as $s): $renderSession($s); endforeach; ?>
</ul>
<?php if (!empty($g['contact'])): ?>
<p class="group-card__trainers"><span class="schedule-row__trainers-label">Kontakt:</span> <?= e($g['contact']) ?></p>
<?php endif; ?>
<?php if (!empty($g['trainers'])): ?>
<p class="group-card__trainers"><span class="schedule-row__trainers-label">Übungsleitung:</span> <?= e(implode(', ', $g['trainers'])) ?></p>
<?php endif; ?>
<?php if (!empty($g['note'])): ?>
<p class="group-card__note text-muted"><?= e($g['note']) ?></p>
<?php endif; ?>
</div>
<?php endif; ?>
</article> </article>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>

View File

@@ -6,10 +6,10 @@ declare(strict_types=1);
* Turnen → Wettkampfturnen. Inhalt: data/turnen.json (disciplines.wettkampfturnen). * Turnen → Wettkampfturnen. Inhalt: data/turnen.json (disciplines.wettkampfturnen).
* Struktur: Hero → Gruppen/Trainingszeiten. * Struktur: Hero → Gruppen/Trainingszeiten.
* *
* Zwei Gruppen: das Wettkampftraining selbst und die Flamingos, die am 31.07.2026 vom * Zwei Gruppen: die Flamingos, die am 31.07.2026 vom Kinderturnen hierher gezogen sind,
* Kinderturnen hierher gezogen sind. Dadurch läuft die Seite nicht mehr im * und das Leistungsturnen. Dadurch läuft die Seite nicht mehr im Einzelgruppen-Layout
* Einzelgruppen-Layout (Trainingszeiten als Kachel im Dashboard), sondern zeigt die * (Trainingszeiten als Kachel im Dashboard), sondern zeigt die Gruppen darunter als
* Gruppen darunter als Tabs — deshalb group-tabs.js und die Sternchen-Fußnote. * offene Kartenreihe (layout => 'stacked') — deshalb die Sternchen-Fußnote.
*/ */
$slug = 'turnen/wettkampfturnen'; $slug = 'turnen/wettkampfturnen';
$data = json_load('turnen'); $data = json_load('turnen');
@@ -19,10 +19,6 @@ $meta = [
'title' => 'Wettkampfturnen in Kulmbach', 'title' => 'Wettkampfturnen in Kulmbach',
'description' => 'Leistungsorientiertes Gerätturnen mit Training und Wettkämpfen: Wettkampfturnen beim TSV 08 Kulmbach im Mäximum Gym. Trainingszeiten und Kontakt im Überblick.', 'description' => 'Leistungsorientiertes Gerätturnen mit Training und Wettkämpfen: Wettkampfturnen beim TSV 08 Kulmbach im Mäximum Gym. Trainingszeiten und Kontakt im Überblick.',
'og_image' => 'img/pages/vereinsgelaende-1600.jpg', 'og_image' => 'img/pages/vereinsgelaende-1600.jpg',
// Seit die Flamingos hier stehen, sind es zwei Gruppen, davon eine mit
// Beschreibung — group-schedule rendert dann eine Tab-Leiste, und die braucht
// dieses Skript. Ohne es wären die Tab-Knöpfe tote Schaltflächen.
'scripts' => ['group-tabs.js'],
'schema' => page_schema([ 'schema' => page_schema([
['name' => 'Startseite', 'slug' => ''], ['name' => 'Startseite', 'slug' => ''],
['name' => 'Turnen', 'slug' => 'turnen'], ['name' => 'Turnen', 'slug' => 'turnen'],
@@ -35,6 +31,15 @@ component('group-schedule', [
'location' => $d['location'] ?? [], 'location' => $d['location'] ?? [],
'lead' => $d['lead'] ?? '', 'lead' => $d['lead'] ?? '',
'groups' => $d['groups'] ?? [], 'groups' => $d['groups'] ?? [],
// Zwei Gruppen, beide offen untereinander statt hinter Tabs: bei so wenigen
// Gruppen verstecken Tabs mehr Inhalt als sie ordnen, und die offene Kartenreihe
// hält den Bento-Eindruck der Seite. Damit braucht die Seite auch kein
// group-tabs.js mehr. Das Kinderturnen bleibt bei Tabs, dort sind es drei
// textreiche Gruppen.
'layout' => 'stacked',
// Überschrift „Unsere Gruppen" nur für Screenreader: die Karten erklären sich
// selbst, und ohne die Zeile bleibt der Bento-Fluss ruhiger.
'groupsTitleHidden' => true,
// Drei Flamingos-Trainerinnen tragen ein Sternchen. Ohne die Fußnote stünden // Drei Flamingos-Trainerinnen tragen ein Sternchen. Ohne die Fußnote stünden
// hier drei unerklärte Sterne auf der Seite. // hier drei unerklärte Sterne auf der Seite.
'footnote' => $data['footnote'] ?? '', 'footnote' => $data['footnote'] ?? '',

View File

@@ -101,7 +101,6 @@
"image": { "base": "img/pages/vereinsgelaende", "widths": [640, 1000, 1600], "alt": "Luftaufnahme unserer Hans-Rausch-Sportanlage im Katzbachtal" }, "image": { "base": "img/pages/vereinsgelaende", "widths": [640, 1000, 1600], "alt": "Luftaufnahme unserer Hans-Rausch-Sportanlage im Katzbachtal" },
"ctas": [] "ctas": []
}, },
"layout": "blocks",
"location": { "venue": "Mäximum Gym", "street": "Thurnauer Str. 51", "zip": "95326", "city": "Kulmbach" }, "location": { "venue": "Mäximum Gym", "street": "Thurnauer Str. 51", "zip": "95326", "city": "Kulmbach" },
"lead": "Annelie Frisque", "lead": "Annelie Frisque",
"notice_tone": "stop", "notice_tone": "stop",
@@ -120,7 +119,7 @@
"recommendation": "Bei besonderem Potenzial für Wettkampf- oder Leistungsgruppen sprechen unsere Trainer eine Empfehlung aus.", "recommendation": "Bei besonderem Potenzial für Wettkampf- oder Leistungsgruppen sprechen unsere Trainer eine Empfehlung aus.",
"sessions": [ "sessions": [
{ "label": "Kängurus", "day": "Dienstag", "time": "16:3017:15 Uhr", "trainers": ["Carina Schleicher*"] }, { "label": "Kängurus", "day": "Dienstag", "time": "16:3017:15 Uhr", "trainers": ["Carina Schleicher*"] },
{ "label": "Grashüpfer", "day": "Donnerstag", "time": "15:3016:15 Uhr", "trainers": ["Annelie Frisque*", "Eva Wünsch"] } { "label": "Grashüpfer", "day": "Donnerstag", "time": "15:3016:15 Uhr", "trainers": ["Annelie Frisque*", "Eva Wünsch*"] }
] ]
}, },
{ {

View File

@@ -2869,6 +2869,14 @@
} }
} }
/* Versteckte Überschrift: dann fehlt der Zeile, die den Raum zwischen Dashboard und
erster Karte gefüllt hat, ihr Inhalt. Übrig blieben 160px (80 unten von der
Dashboard-Sektion + 80 oben von dieser), in denen nur nackte Hintergrund-Textur stand
— gemessen und als Loch aufgefallen (31.07.2026). Die 80px des Dashboards reichen. */
.group-list-section--tight {
padding-top: 0;
}
/* Schlichte Gruppen (z. B. Kurse) als Raster, beschreibende Gruppen gestapelt. */ /* Schlichte Gruppen (z. B. Kurse) als Raster, beschreibende Gruppen gestapelt. */
.group-list--grid { .group-list--grid {
display: grid; display: grid;
@@ -2901,7 +2909,10 @@
grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
gap: var(--space-4xl); gap: var(--space-4xl);
align-items: start; align-items: start;
padding: var(--space-3xl) var(--space-3xl); /* --pad-card-lg statt fester 40px, dieselbe Korrektur wie beim Tab-Panel:
auf schmalen Handys stapeln sich Container- und Kartenabstand sonst so, dass
dem Inhalt kaum Breite bleibt. Obergrenze ist der bisherige Wert. */
padding: var(--pad-card-lg) var(--pad-card-lg);
} }
@media (max-width: 768px) { @media (max-width: 768px) {