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">
|
||||
<div class="container">
|
||||
<h2 class="group-list__title"><?= e($groupsTitle) ?></h2>
|
||||
<div class="group-list <?= $rich ? 'group-list--stacked' : 'group-list--grid' ?>">
|
||||
<?php foreach ($groups as $g): ?>
|
||||
<article class="group-card">
|
||||
<?php if ($rich): ?>
|
||||
<div class="group-tabs" data-group-tabs>
|
||||
<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__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; ?>
|
||||
@@ -175,6 +191,29 @@ $soleNote = $single ? ($groups[0]['note'] ?? '') : '';
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
</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 !== ''): ?>
|
||||
<p class="group-list__footnote text-muted"><?= e($footnote) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -15,6 +15,7 @@ $meta = [
|
||||
'title' => 'Eltern-Kind- & Kinderturnen in Kulmbach',
|
||||
'description' => $d['hero']['text'],
|
||||
'og_image' => 'img/hero/hero-1600.jpg',
|
||||
'scripts' => ['group-tabs.js'],
|
||||
'schema' => page_schema([
|
||||
['name' => 'Startseite', 'slug' => ''],
|
||||
['name' => 'Turnen', 'slug' => 'turnen'],
|
||||
|
||||
@@ -2074,6 +2074,72 @@
|
||||
/* === group-list (Turn-Gruppen einer Disziplin) === */
|
||||
.group-list__title {
|
||||
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. */
|
||||
@@ -2086,7 +2152,7 @@
|
||||
.group-list--stacked {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
gap: 2.75rem;
|
||||
}
|
||||
|
||||
.group-card {
|
||||
@@ -2100,15 +2166,15 @@
|
||||
.group-list--stacked .group-card {
|
||||
display: grid;
|
||||
grid-template-columns: 1.6fr 1fr;
|
||||
gap: 2.5rem;
|
||||
gap: 3rem;
|
||||
align-items: start;
|
||||
padding: 2rem 2.25rem;
|
||||
padding: 2.25rem 2.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.group-list--stacked .group-card {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
gap: 1.75rem;
|
||||
padding: 1.6rem 1.75rem 1.75rem;
|
||||
}
|
||||
}
|
||||
@@ -2118,47 +2184,69 @@
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
.group-card__level {
|
||||
margin: -0.15rem 0 0;
|
||||
font-size: var(--fs-500);
|
||||
flex-shrink: 0;
|
||||
padding: 0.15rem 0.5rem;
|
||||
font-size: var(--fs-400);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
letter-spacing: 0.07em;
|
||||
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 {
|
||||
margin: 0.85rem 0 0;
|
||||
margin: 1.25rem 0 0;
|
||||
font-size: var(--fs-600);
|
||||
line-height: 1.65;
|
||||
color: var(--clr-text);
|
||||
}
|
||||
|
||||
/* Detail-Abschnitte: Akzent-Randstrich links als visueller Anker */
|
||||
.group-card__detail-heading {
|
||||
margin: 1.6rem 0 0;
|
||||
/* Body-Schrift (Abel) als Eyebrow — Coolvetica wäre in dieser Größe unleserlich. */
|
||||
margin: 2.25rem 0 0;
|
||||
padding-left: 0.7rem;
|
||||
border-left: 2px solid var(--clr-accent);
|
||||
font-family: var(--ff-body);
|
||||
font-size: var(--fs-500);
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--clr-accent);
|
||||
letter-spacing: 0.07em;
|
||||
color: var(--clr-text);
|
||||
}
|
||||
|
||||
.group-card__detail-text {
|
||||
margin: 0.4rem 0 0;
|
||||
line-height: 1.65;
|
||||
margin: 0.6rem 0 0;
|
||||
padding-left: 0.7rem;
|
||||
font-size: var(--fs-600);
|
||||
line-height: 1.72;
|
||||
color: var(--clr-text);
|
||||
}
|
||||
|
||||
.group-card__recommendation {
|
||||
margin: 1.6rem 0 0;
|
||||
padding-top: 1.1rem;
|
||||
margin: 2rem 0 0;
|
||||
padding: 1rem 1rem 0;
|
||||
border-top: 1px solid var(--clr-glass-border);
|
||||
font-size: var(--fs-500);
|
||||
font-style: italic;
|
||||
line-height: 1.6;
|
||||
color: var(--clr-text-muted);
|
||||
}
|
||||
|
||||
/* Trainingszeiten-Panel in der Karte. */
|
||||
@@ -2166,20 +2254,23 @@
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
|
||||
.group-list--stacked .group-card__schedule {
|
||||
.group-list--stacked .group-card__schedule,
|
||||
.group-tabs .group-card__schedule {
|
||||
margin-top: 0;
|
||||
padding: 1.5rem 1.6rem;
|
||||
padding: 1.75rem;
|
||||
background: var(--clr-bg);
|
||||
border: 1px solid var(--clr-glass-border);
|
||||
border-radius: var(--radius-card);
|
||||
border-top: 2px solid var(--clr-accent);
|
||||
}
|
||||
|
||||
.group-card__schedule-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin: 0 0 0.85rem;
|
||||
font-size: var(--fs-600);
|
||||
margin: 0 0 1.1rem;
|
||||
font-size: var(--fs-650);
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.group-card__schedule-icon {
|
||||
@@ -2188,7 +2279,9 @@
|
||||
}
|
||||
|
||||
.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);
|
||||
line-height: 1.5;
|
||||
}
|
||||
@@ -2206,7 +2299,7 @@
|
||||
}
|
||||
|
||||
.schedule-row {
|
||||
padding: 0.7rem 0;
|
||||
padding: 0.85rem 0;
|
||||
border-bottom: 1px solid var(--clr-glass-border);
|
||||
}
|
||||
|
||||
@@ -2215,9 +2308,12 @@
|
||||
|
||||
.schedule-row__label {
|
||||
display: block;
|
||||
margin-bottom: 0.2rem;
|
||||
font-size: var(--fs-600);
|
||||
color: var(--clr-text);
|
||||
margin-bottom: 0.3rem;
|
||||
font-size: var(--fs-500);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.07em;
|
||||
color: var(--clr-accent);
|
||||
font-family: var(--ff-body);
|
||||
}
|
||||
|
||||
.schedule-row__when {
|
||||
@@ -2227,28 +2323,32 @@
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.schedule-row__day {
|
||||
font-size: var(--fs-600);
|
||||
color: var(--clr-text);
|
||||
}
|
||||
|
||||
.schedule-row__time {
|
||||
font-size: var(--fs-600);
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
color: var(--clr-text);
|
||||
}
|
||||
|
||||
.schedule-row__note {
|
||||
display: block;
|
||||
margin-top: 0.15rem;
|
||||
margin-top: 0.2rem;
|
||||
font-size: var(--fs-500);
|
||||
color: var(--clr-text-muted);
|
||||
}
|
||||
|
||||
.schedule-row__trainers {
|
||||
display: block;
|
||||
margin-top: 0.25rem;
|
||||
margin-top: 0.3rem;
|
||||
font-size: var(--fs-500);
|
||||
line-height: 1.5;
|
||||
color: var(--clr-text-muted);
|
||||
}
|
||||
|
||||
.schedule-row__trainers-label {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
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