Logo-Downloadseite /logo mit Erzeuger-Skript

Damit die Abteilungen das Wappen selbst holen können, statt jedes Mal zu fragen:
drei Varianten (farbig, einfarbig schwarz, einfarbig weiß) in SVG, PDF, PNG und JPG,
dazu ein ZIP über alles und die Vereinsfarben. Verlinkt nur in der Fußzeile.

Bisher lag das Logo genau einmal im Projekt, als eine einzige SVG. Es gab kein PNG,
kein JPG, kein PDF und keine einfarbige Fassung.

bin/logo-build.php erzeugt alle Dateien aus einer Master-SVG unter storage/logo-quellen/.
Gerastert wird mit headless Chrome, weil GD kein SVG lesen kann und weder Inkscape noch
rsvg-convert noch ImageMagick verfügbar sind; das PDF ist dabei echter Vektor. Das Skript
läuft einmalig lokal, nichts davon im Request-Pfad. bin/preflight.php meldet als Fehler,
wenn eine Download-Datei fehlt: die Komponente ließe den Knopf sonst still weg.

Die beiden einfarbigen Fassungen sind exakte Negative voneinander, nur Linien, alle
Flächen frei. EPS gibt es bewusst nicht, dafür fehlt jedes Werkzeug; SVG und PDF decken
die Druckereien ab. Frühere Vereinslogos sind vorbereitet, aber im Projekt liegt bisher
kein einziges historisches Wappen vor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J9mubC8gtpjtJntbhSJBRS
This commit is contained in:
2026-09-17 10:10:08 +02:00
parent e6ac00b248
commit 75fce4f680
35 changed files with 1141 additions and 2 deletions

View File

@@ -91,7 +91,7 @@ $extraLinks = array_merge($extraLinks, $nav['ctas'] ?? []);
<div class="site-footer__bottom">
<div class="container site-footer__bottom-inner">
<p>&copy; <?= e(date('Y')) ?> <?= e($club['legal_name']) ?></p>
<ul class="site-footer__legal" aria-label="Rechtliches">
<ul class="site-footer__legal" aria-label="Service und Rechtliches">
<?php foreach ($nav['legal'] as $item): ?>
<li><a href="<?= e(url($item['slug'])) ?>"><?= e($item['label']) ?></a></li>
<?php endforeach; ?>

View File

@@ -0,0 +1,136 @@
<?php
declare(strict_types=1);
/**
* Logo-Varianten mit ihren Download-Knöpfen. Props:
* $varianten array [{key, title, text, grund?: 'hell'|'dunkel'}, …]
* `key` ist zugleich das Dateinamen-Suffix unter assets/downloads/logo/
* und der Schlüssel in bin/logo-build.php. `grund` steuert nur, worauf die
* Vorschau liegt: 'hell' zeigt ein Karomuster (der Grund ist frei),
* 'dunkel' eine dunkle Fläche (sonst sähe man die weiße Variante nicht).
* $title string — Überschrift des Abschnitts
* $title_hidden bool (optional) — Überschrift nur für Screenreader. Hält die
* Heading-Kette heil (h1 → h2 → Kartentitel), ohne sie zu zeigen: über
* drei Wappen-Karten muss nicht „Das aktuelle Wappen" stehen.
* $text string (optional) — Einleitung darunter
* $anchor string (optional, Default 'varianten')
* $tinted bool (optional) — abgesetzte Sektionsfläche
* $paket array{label, href, meta} (optional) — ein Knopf unter dem Raster, für das
* ZIP über alle Varianten. Steht bewusst HIER und nicht in einer eigenen
* Sektion mit Überschrift (die war gebaut und ist am 17.09.2026 wieder raus):
* „alles davon" gehört direkt unter das, worauf es sich bezieht.
*
* Die Dateiliste steht bewusst NICHT in data/logo.json: welche Formate es gibt,
* entscheidet bin/logo-build.php, und Größe und Maße liest die Seite direkt von der
* Platte. Eine zweite Liste in der JSON könnte nur noch falsch werden. Fehlt eine
* Datei, entfällt ihr Knopf still (und bin/preflight.php meldet es vor dem Deploy).
*/
$varianten = array_values(array_filter($varianten ?? [], static fn (array $v): bool => !empty($v['key'])));
if ($varianten === []) {
return;
}
$anchor = $anchor ?? 'varianten';
$text = trim((string) ($text ?? ''));
$titleHidden = !empty($title_hidden);
$paket = $paket ?? [];
$tinted = !empty($tinted);
$dir = PUBLIC_PATH . '/assets/downloads/logo';
/** Dateigröße menschenlesbar: unter 1 MB in KB, darüber mit einer Nachkommastelle in MB. */
$size = static function (string $file): string {
$bytes = (int) filesize($file);
return $bytes < 1024 * 1024
? number_format($bytes / 1024, 0, ',', '.') . ' KB'
: number_format($bytes / 1024 / 1024, 1, ',', '.') . ' MB';
};
// Reihenfolge der Knöpfe = Reihenfolge der Empfehlung: erst Vektor (das Richtige für
// die Druckerei), dann PNG (freigestellt), zuletzt JPG (mit gefülltem Rechteck drumherum).
$formate = static function (string $base) use ($dir): array {
$liste = [];
foreach ([
['datei' => "{$base}.svg", 'label' => 'SVG', 'hint' => 'Vektor'],
['datei' => "{$base}.pdf", 'label' => 'PDF', 'hint' => 'Vektor'],
['datei' => "{$base}-500.png", 'label' => 'PNG', 'hint' => '500 px'],
['datei' => "{$base}-1000.png", 'label' => 'PNG', 'hint' => '1000 px'],
['datei' => "{$base}-2000.png", 'label' => 'PNG', 'hint' => '2000 px'],
['datei' => "{$base}-1000.jpg", 'label' => 'JPG', 'hint' => '1000 px'],
['datei' => "{$base}-2000.jpg", 'label' => 'JPG', 'hint' => '2000 px'],
] as $format) {
if (is_file($dir . '/' . $format['datei'])) {
$liste[] = $format;
}
}
return $liste;
};
?>
<section class="section<?= $tinted ? ' section--tinted' : '' ?> logo-downloads" id="<?= e($anchor) ?>">
<div class="container">
<header class="logo-downloads__head">
<h2<?= $titleHidden ? ' class="visually-hidden"' : '' ?>><?= e($title) ?></h2>
<?php if ($text !== ''): ?>
<p class="logo-downloads__intro"><?= e($text) ?></p>
<?php endif; ?>
</header>
<ul class="logo-downloads__grid" role="list">
<?php foreach ($varianten as $variante): ?>
<?php
$base = 'tsv08-kulmbach-logo-' . $variante['key'];
$formats = $formate($base);
if ($formats === []) {
continue;
}
$preview = is_file($dir . '/' . $base . '.svg')
? 'downloads/logo/' . $base . '.svg'
: 'downloads/logo/' . $base . '-1000.png';
$dunkel = ($variante['grund'] ?? 'hell') === 'dunkel';
?>
<li class="logo-card">
<div class="logo-card__preview logo-card__preview--<?= $dunkel ? 'dunkel' : 'karo' ?>">
<?php component('img', [
'image' => [
'src' => $preview,
'width' => 300,
'height' => 388,
'alt' => 'Unser Vereinswappen, Variante ' . ($variante['title'] ?? $variante['key']),
],
'class' => 'logo-card__img',
]); ?>
</div>
<div class="logo-card__body">
<h3 class="logo-card__title"><?= e($variante['title'] ?? $variante['key']) ?></h3>
<?php if (!empty($variante['text'])): ?>
<p class="logo-card__text"><?= e($variante['text']) ?></p>
<?php endif; ?>
<ul class="logo-card__files" role="list">
<?php foreach ($formats as $format): ?>
<li>
<a class="btn btn--outline btn--sm logo-card__btn"
href="<?= e(asset('downloads/logo/' . $format['datei'])) ?>" download>
<?= icon('download') ?>
<span class="logo-card__format"><?= e($format['label']) ?></span>
<span class="logo-card__meta"><?= e($format['hint']) ?> · <?= e($size($dir . '/' . $format['datei'])) ?></span>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
</li>
<?php endforeach; ?>
</ul>
<?php if (!empty($paket['href'])): ?>
<p class="logo-downloads__paket">
<a class="btn btn--lg" href="<?= e($paket['href']) ?>" download>
<?= icon('download') ?><?= e($paket['label']) ?>
</a>
<?php if (!empty($paket['meta'])): ?>
<span class="logo-downloads__paket-meta"><?= e($paket['meta']) ?></span>
<?php endif; ?>
</p>
<?php endif; ?>
</div>
</section>