Mitmachen: Ehrenamtsseite mit Stellen-Filter

Neue Seite /mitmachen, die offene Ehrenamtsstellen aus data/mitmachen.json als
position-card-Komponente listet, mit clientseitigem Bereichsfilter
(position-filter.js, progressive enhancement) und JobPosting-Schema
(job_posting_schema(), employmentType VOLUNTEER). Route ergänzt.
Enthält zudem kleine person-card__contact-Stilanpassungen (gleiche CSS-Sektion).

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:
2026-06-20 20:47:13 +02:00
parent 6a612e52c3
commit 2c0594811c
13 changed files with 653 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
(function () {
'use strict';
// Filtert die Stellen-Karten (/mitmachen) nach Bereich. Ohne JS sind alle Karten
// sichtbar (Default-Filter "all"), der Empty-State bleibt verborgen — die Tabs
// sind reine Progressive Enhancement.
function init(bar) {
var section = bar.closest('section');
if (!section) { return; }
var buttons = [].slice.call(bar.querySelectorAll('[data-filter]'));
var cards = [].slice.call(section.querySelectorAll('.position-card'));
var empty = section.querySelector('[data-position-empty]');
if (!buttons.length || !cards.length) { return; }
function apply(filter) {
var visible = 0;
cards.forEach(function (card) {
var match = filter === 'all' || card.getAttribute('data-group') === filter;
card.hidden = !match;
if (match) { visible++; }
});
if (empty) { empty.hidden = visible !== 0; }
}
buttons.forEach(function (btn, i) {
btn.addEventListener('click', function () {
buttons.forEach(function (b) { b.setAttribute('aria-pressed', 'false'); });
btn.setAttribute('aria-pressed', 'true');
apply(btn.getAttribute('data-filter'));
});
btn.addEventListener('keydown', function (e) {
var next = null;
if (e.key === 'ArrowRight') { next = (i + 1) % buttons.length; }
if (e.key === 'ArrowLeft') { next = (i - 1 + buttons.length) % buttons.length; }
if (e.key === 'Home') { next = 0; }
if (e.key === 'End') { next = buttons.length - 1; }
if (next !== null) { e.preventDefault(); buttons[next].focus(); }
});
});
}
document.querySelectorAll('[data-position-filter]').forEach(init);
}());