Turnen: Tab-Navigation für Gruppen mit vielen Trainingszeiten
group-schedule um eine Tab-Leiste (group-tabs) erweitert: bei Disziplinen mit vielen Gruppen (z. B. Eltern-Kind-Turnen) werden die Gruppen als Tabs gruppiert statt endlos gestapelt. JS (group-tabs.js) als progressive enhancement — ohne JS bleiben alle Panels sichtbar. Begleitend Feinschliff an group-card/-list/schedule-row. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGzc6GhWhmLJt1jC2q1SRZ
This commit is contained in:
@@ -135,14 +135,30 @@ $soleNote = $single ? ($groups[0]['note'] ?? '') : '';
|
|||||||
<section class="section group-list-section" id="gruppen">
|
<section class="section group-list-section" id="gruppen">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h2 class="group-list__title"><?= e($groupsTitle) ?></h2>
|
<h2 class="group-list__title"><?= e($groupsTitle) ?></h2>
|
||||||
<div class="group-list <?= $rich ? 'group-list--stacked' : 'group-list--grid' ?>">
|
<?php if ($rich): ?>
|
||||||
<?php foreach ($groups as $g): ?>
|
<div class="group-tabs" data-group-tabs>
|
||||||
<article class="group-card">
|
<div class="group-tabs__bar" role="tablist" aria-label="<?= e($groupsTitle) ?>">
|
||||||
|
<?php foreach ($groups as $i => $g): ?>
|
||||||
|
<button class="group-tabs__tab" role="tab" type="button"
|
||||||
|
id="gtab-<?= $i ?>"
|
||||||
|
aria-controls="gpanel-<?= $i ?>"
|
||||||
|
aria-selected="<?= $i === 0 ? 'true' : 'false' ?>"
|
||||||
|
><?= e($g['name']) ?></button>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php foreach ($groups as $i => $g): ?>
|
||||||
|
<article class="group-card group-tabs__panel" role="tabpanel"
|
||||||
|
id="gpanel-<?= $i ?>"
|
||||||
|
aria-labelledby="gtab-<?= $i ?>"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
<div class="group-card__body">
|
<div class="group-card__body">
|
||||||
<h3 class="group-card__name"><?= e($g['name']) ?></h3>
|
<div class="group-card__header">
|
||||||
|
<h3 class="group-card__name"><?= e($g['name']) ?></h3>
|
||||||
<?php if (!empty($g['level'])): ?>
|
<?php if (!empty($g['level'])): ?>
|
||||||
<p class="group-card__level"><?= e($g['level']) ?></p>
|
<p class="group-card__level"><?= e($g['level']) ?></p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
<?php if (!empty($g['intro'])): ?>
|
<?php if (!empty($g['intro'])): ?>
|
||||||
<p class="group-card__intro"><?= e($g['intro']) ?></p>
|
<p class="group-card__intro"><?= e($g['intro']) ?></p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -175,6 +191,29 @@ $soleNote = $single ? ($groups[0]['note'] ?? '') : '';
|
|||||||
</article>
|
</article>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="group-list group-list--grid">
|
||||||
|
<?php foreach ($groups as $g): ?>
|
||||||
|
<article class="group-card">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<?php if (!empty($g['sessions'])): ?>
|
||||||
|
<div class="group-card__schedule">
|
||||||
|
<ul class="schedule-list">
|
||||||
|
<?php foreach ($g['sessions'] as $s): $renderSession($s); endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</article>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
<?php if ($footnote !== ''): ?>
|
<?php if ($footnote !== ''): ?>
|
||||||
<p class="group-list__footnote text-muted"><?= e($footnote) ?></p>
|
<p class="group-list__footnote text-muted"><?= e($footnote) ?></p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ $meta = [
|
|||||||
'title' => 'Eltern-Kind- & Kinderturnen in Kulmbach',
|
'title' => 'Eltern-Kind- & Kinderturnen in Kulmbach',
|
||||||
'description' => $d['hero']['text'],
|
'description' => $d['hero']['text'],
|
||||||
'og_image' => 'img/hero/hero-1600.jpg',
|
'og_image' => 'img/hero/hero-1600.jpg',
|
||||||
|
'scripts' => ['group-tabs.js'],
|
||||||
'schema' => page_schema([
|
'schema' => page_schema([
|
||||||
['name' => 'Startseite', 'slug' => ''],
|
['name' => 'Startseite', 'slug' => ''],
|
||||||
['name' => 'Turnen', 'slug' => 'turnen'],
|
['name' => 'Turnen', 'slug' => 'turnen'],
|
||||||
|
|||||||
@@ -2074,6 +2074,72 @@
|
|||||||
/* === group-list (Turn-Gruppen einer Disziplin) === */
|
/* === group-list (Turn-Gruppen einer Disziplin) === */
|
||||||
.group-list__title {
|
.group-list__title {
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === group-tabs (Tab-Navigation für rich Gruppen) === */
|
||||||
|
.group-tabs__bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
overflow-x: auto;
|
||||||
|
scrollbar-width: none;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-tabs__bar::-webkit-scrollbar { display: none; }
|
||||||
|
|
||||||
|
.group-tabs__tab {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0.7rem 1.4rem;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
box-shadow: inset 0 -2px 0 var(--clr-glass-border);
|
||||||
|
font-family: var(--ff-body);
|
||||||
|
font-size: var(--fs-500);
|
||||||
|
color: var(--clr-text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: color var(--transition), box-shadow var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-tabs__tab:hover {
|
||||||
|
color: var(--clr-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-tabs__tab[aria-selected="true"] {
|
||||||
|
color: var(--clr-text);
|
||||||
|
box-shadow: inset 0 -2px 0 var(--clr-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-tabs__tab:focus-visible {
|
||||||
|
outline: 2px solid var(--clr-accent);
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Panel-Karte übernimmt das bestehende group-card--stacked-Layout */
|
||||||
|
.group-tabs__panel {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1.6fr 1fr;
|
||||||
|
gap: 3rem;
|
||||||
|
align-items: start;
|
||||||
|
padding: 2.25rem 2.5rem;
|
||||||
|
background: var(--clr-glass-bg);
|
||||||
|
border: 1px solid var(--clr-glass-border);
|
||||||
|
border-radius: var(--radius-card);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* display:grid überschreibt den UA-Stil [hidden]{display:none} — explizit korrigieren */
|
||||||
|
.group-tabs__panel[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.group-tabs__panel {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 1.75rem;
|
||||||
|
padding: 1.6rem 1.75rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Schlichte Gruppen (z. B. Kurse) als Raster, beschreibende Gruppen gestapelt. */
|
/* Schlichte Gruppen (z. B. Kurse) als Raster, beschreibende Gruppen gestapelt. */
|
||||||
@@ -2086,7 +2152,7 @@
|
|||||||
.group-list--stacked {
|
.group-list--stacked {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1.25rem;
|
gap: 2.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-card {
|
.group-card {
|
||||||
@@ -2100,15 +2166,15 @@
|
|||||||
.group-list--stacked .group-card {
|
.group-list--stacked .group-card {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1.6fr 1fr;
|
grid-template-columns: 1.6fr 1fr;
|
||||||
gap: 2.5rem;
|
gap: 3rem;
|
||||||
align-items: start;
|
align-items: start;
|
||||||
padding: 2rem 2.25rem;
|
padding: 2.25rem 2.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.group-list--stacked .group-card {
|
.group-list--stacked .group-card {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 1.5rem;
|
gap: 1.75rem;
|
||||||
padding: 1.6rem 1.75rem 1.75rem;
|
padding: 1.6rem 1.75rem 1.75rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2118,47 +2184,69 @@
|
|||||||
max-width: 64ch;
|
max-width: 64ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Name links, Level-Chip rechts vertikal zentriert */
|
||||||
|
.group-card__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
.group-card__name {
|
.group-card__name {
|
||||||
font-size: clamp(1.9rem, 1.5rem + 1.2vw, 2.3rem);
|
font-size: clamp(2.2rem, 1.7rem + 1.5vw, 2.8rem);
|
||||||
line-height: var(--lh-heading);
|
line-height: var(--lh-heading);
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-card__level {
|
.group-card__level {
|
||||||
margin: -0.15rem 0 0;
|
flex-shrink: 0;
|
||||||
font-size: var(--fs-500);
|
padding: 0.15rem 0.5rem;
|
||||||
|
font-size: var(--fs-400);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.06em;
|
letter-spacing: 0.07em;
|
||||||
color: var(--clr-text-muted);
|
color: var(--clr-text-muted);
|
||||||
|
border: 1px solid var(--clr-glass-border-strong);
|
||||||
|
border-radius: 999px;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Intro = Lead-Paragraph, deutlich abgesetzt vom Level-Chip */
|
||||||
.group-card__intro {
|
.group-card__intro {
|
||||||
margin: 0.85rem 0 0;
|
margin: 1.25rem 0 0;
|
||||||
|
font-size: var(--fs-600);
|
||||||
line-height: 1.65;
|
line-height: 1.65;
|
||||||
|
color: var(--clr-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Detail-Abschnitte: Akzent-Randstrich links als visueller Anker */
|
||||||
.group-card__detail-heading {
|
.group-card__detail-heading {
|
||||||
margin: 1.6rem 0 0;
|
margin: 2.25rem 0 0;
|
||||||
/* Body-Schrift (Abel) als Eyebrow — Coolvetica wäre in dieser Größe unleserlich. */
|
padding-left: 0.7rem;
|
||||||
|
border-left: 2px solid var(--clr-accent);
|
||||||
font-family: var(--ff-body);
|
font-family: var(--ff-body);
|
||||||
font-size: var(--fs-500);
|
font-size: var(--fs-500);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.07em;
|
||||||
color: var(--clr-accent);
|
color: var(--clr-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-card__detail-text {
|
.group-card__detail-text {
|
||||||
margin: 0.4rem 0 0;
|
margin: 0.6rem 0 0;
|
||||||
line-height: 1.65;
|
padding-left: 0.7rem;
|
||||||
|
font-size: var(--fs-600);
|
||||||
|
line-height: 1.72;
|
||||||
|
color: var(--clr-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-card__recommendation {
|
.group-card__recommendation {
|
||||||
margin: 1.6rem 0 0;
|
margin: 2rem 0 0;
|
||||||
padding-top: 1.1rem;
|
padding: 1rem 1rem 0;
|
||||||
border-top: 1px solid var(--clr-glass-border);
|
border-top: 1px solid var(--clr-glass-border);
|
||||||
font-size: var(--fs-500);
|
font-size: var(--fs-500);
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
|
color: var(--clr-text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Trainingszeiten-Panel in der Karte. */
|
/* Trainingszeiten-Panel in der Karte. */
|
||||||
@@ -2166,20 +2254,23 @@
|
|||||||
margin-top: 1.25rem;
|
margin-top: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-list--stacked .group-card__schedule {
|
.group-list--stacked .group-card__schedule,
|
||||||
|
.group-tabs .group-card__schedule {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
padding: 1.5rem 1.6rem;
|
padding: 1.75rem;
|
||||||
background: var(--clr-bg);
|
background: var(--clr-bg);
|
||||||
border: 1px solid var(--clr-glass-border);
|
border: 1px solid var(--clr-glass-border);
|
||||||
border-radius: var(--radius-card);
|
border-radius: var(--radius-card);
|
||||||
|
border-top: 2px solid var(--clr-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-card__schedule-title {
|
.group-card__schedule-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
margin: 0 0 0.85rem;
|
margin: 0 0 1.1rem;
|
||||||
font-size: var(--fs-600);
|
font-size: var(--fs-650);
|
||||||
|
letter-spacing: 0.01em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-card__schedule-icon {
|
.group-card__schedule-icon {
|
||||||
@@ -2188,7 +2279,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.group-card__trainers {
|
.group-card__trainers {
|
||||||
margin: 0.85rem 0 0;
|
margin: 1rem 0 0;
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
border-top: 1px solid var(--clr-glass-border);
|
||||||
font-size: var(--fs-500);
|
font-size: var(--fs-500);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
@@ -2206,7 +2299,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.schedule-row {
|
.schedule-row {
|
||||||
padding: 0.7rem 0;
|
padding: 0.85rem 0;
|
||||||
border-bottom: 1px solid var(--clr-glass-border);
|
border-bottom: 1px solid var(--clr-glass-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2215,9 +2308,12 @@
|
|||||||
|
|
||||||
.schedule-row__label {
|
.schedule-row__label {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 0.2rem;
|
margin-bottom: 0.3rem;
|
||||||
font-size: var(--fs-600);
|
font-size: var(--fs-500);
|
||||||
color: var(--clr-text);
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.07em;
|
||||||
|
color: var(--clr-accent);
|
||||||
|
font-family: var(--ff-body);
|
||||||
}
|
}
|
||||||
|
|
||||||
.schedule-row__when {
|
.schedule-row__when {
|
||||||
@@ -2227,28 +2323,32 @@
|
|||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.schedule-row__day {
|
||||||
|
font-size: var(--fs-600);
|
||||||
|
color: var(--clr-text);
|
||||||
|
}
|
||||||
|
|
||||||
.schedule-row__time {
|
.schedule-row__time {
|
||||||
font-size: var(--fs-600);
|
font-size: var(--fs-600);
|
||||||
text-align: right;
|
color: var(--clr-text);
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.schedule-row__note {
|
.schedule-row__note {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 0.15rem;
|
margin-top: 0.2rem;
|
||||||
font-size: var(--fs-500);
|
font-size: var(--fs-500);
|
||||||
|
color: var(--clr-text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.schedule-row__trainers {
|
.schedule-row__trainers {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 0.25rem;
|
margin-top: 0.3rem;
|
||||||
font-size: var(--fs-500);
|
font-size: var(--fs-500);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
color: var(--clr-text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.schedule-row__trainers-label {
|
.schedule-row__trainers-label {
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.06em;
|
|
||||||
color: var(--clr-text-muted);
|
color: var(--clr-text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
31
public/assets/js/group-tabs.js
Normal file
31
public/assets/js/group-tabs.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function init(el) {
|
||||||
|
const buttons = [...el.querySelectorAll('[role="tab"]')];
|
||||||
|
const panels = [...el.querySelectorAll('[role="tabpanel"]')];
|
||||||
|
|
||||||
|
// Alle außer dem ersten verstecken
|
||||||
|
panels.forEach(function (p, i) { if (i > 0) p.hidden = true; });
|
||||||
|
|
||||||
|
buttons.forEach(function (btn, i) {
|
||||||
|
btn.addEventListener('click', function () { activate(i); });
|
||||||
|
btn.addEventListener('keydown', function (e) {
|
||||||
|
if (e.key === 'ArrowRight') { e.preventDefault(); activate((i + 1) % buttons.length); }
|
||||||
|
if (e.key === 'ArrowLeft') { e.preventDefault(); activate((i - 1 + buttons.length) % buttons.length); }
|
||||||
|
if (e.key === 'Home') { e.preventDefault(); activate(0); }
|
||||||
|
if (e.key === 'End') { e.preventDefault(); activate(buttons.length - 1); }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function activate(index) {
|
||||||
|
buttons.forEach(function (b, i) {
|
||||||
|
b.setAttribute('aria-selected', i === index ? 'true' : 'false');
|
||||||
|
});
|
||||||
|
panels.forEach(function (p, i) { p.hidden = i !== index; });
|
||||||
|
buttons[index].focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-group-tabs]').forEach(init);
|
||||||
|
}());
|
||||||
Reference in New Issue
Block a user