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:
@@ -91,7 +91,7 @@ $extraLinks = array_merge($extraLinks, $nav['ctas'] ?? []);
|
||||
<div class="site-footer__bottom">
|
||||
<div class="container site-footer__bottom-inner">
|
||||
<p>© <?= 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; ?>
|
||||
|
||||
136
app/components/logo-downloads.php
Normal file
136
app/components/logo-downloads.php
Normal 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>
|
||||
96
app/pages/logo.php
Normal file
96
app/pages/logo.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Logo zum Herunterladen. Inhalt: data/logo.json.
|
||||
* Struktur: Hero → Varianten mit Downloads (samt ZIP-Knopf) → Farben → frühere Logos.
|
||||
*
|
||||
* Bewusst KARG (Entscheidung Felix, 17.09.2026): Einleitungstext, Nutzungshinweis,
|
||||
* Abschnittsüberschrift und FAQ waren gebaut und sind wieder raus. Eine Downloadseite
|
||||
* ist ein Regal, kein Text: Wappen, Knöpfe, Farben. Wer etwas fragen will, findet den
|
||||
* Weg über die Fußzeile. Die Überschrift steht noch im DOM, aber visually-hidden,
|
||||
* damit die Heading-Kette nicht von h1 auf h3 springt.
|
||||
*
|
||||
* Die DATEIEN erzeugt bin/logo-build.php nach public/assets/downloads/logo/; diese Seite
|
||||
* kennt nur die Variantenschlüssel und liest Größen beim Rendern von der Platte.
|
||||
*/
|
||||
$page = json_load('logo');
|
||||
|
||||
$zip = 'downloads/logo/tsv08-kulmbach-logo-paket.zip';
|
||||
$zipFile = PUBLIC_PATH . '/assets/' . $zip;
|
||||
$zipSize = is_file($zipFile) ? number_format(filesize($zipFile) / 1024 / 1024, 1, ',', '.') . ' MB' : '';
|
||||
|
||||
// Farbfläche über eine Klasse, nie über ein style-Attribut: die CSP verwirft Inline-Styles
|
||||
// in Produktion stillschweigend. Die Klasse holt sich den Wert aus dem Token in tokens.css,
|
||||
// die JSON nennt nur den Namen. Unbekannte Namen fallen still weg.
|
||||
$farbKlassen = ['accent' => 'accent', 'black' => 'black', 'bg' => 'bg', 'white' => 'white'];
|
||||
|
||||
$meta = [
|
||||
'title' => 'Logo herunterladen',
|
||||
'description' => 'Unser Vereinswappen zum Herunterladen: als Vektor für die Druckerei, als PNG und JPG für Dokumente und Aushänge, dazu unsere Vereinsfarben.',
|
||||
'og_image' => 'downloads/logo/tsv08-kulmbach-logo-farbig-1000.jpg',
|
||||
'schema' => page_schema([
|
||||
['name' => 'Startseite', 'slug' => ''],
|
||||
['name' => 'Der Verein', 'slug' => 'verein'],
|
||||
['name' => 'Logo herunterladen', 'slug' => 'logo'],
|
||||
]),
|
||||
];
|
||||
|
||||
component('hero', ['hero' => $page['hero']]);
|
||||
?>
|
||||
<?php
|
||||
component('logo-downloads', [
|
||||
'varianten' => $page['varianten'] ?? [],
|
||||
'title' => $page['varianten_title'] ?? 'Das aktuelle Wappen',
|
||||
'title_hidden' => true,
|
||||
'anchor' => 'varianten',
|
||||
'paket' => $zipSize === '' ? [] : [
|
||||
'label' => $page['paket']['label'] ?? 'Alle Dateien als ZIP',
|
||||
'href' => asset($zip),
|
||||
'meta' => 'ZIP, ' . $zipSize,
|
||||
],
|
||||
]);
|
||||
?>
|
||||
<?php if (!empty($page['farben']['items'])): ?>
|
||||
<section class="section logo-farben" id="farben">
|
||||
<div class="container">
|
||||
<header class="logo-farben__head">
|
||||
<h2><?= e($page['farben']['title']) ?></h2>
|
||||
<?php if (!empty($page['farben']['text'])): ?>
|
||||
<p class="logo-farben__intro"><?= e($page['farben']['text']) ?></p>
|
||||
<?php endif; ?>
|
||||
</header>
|
||||
<ul class="logo-farben__grid" role="list">
|
||||
<?php foreach ($page['farben']['items'] as $farbe): ?>
|
||||
<?php $klasse = $farbKlassen[$farbe['token'] ?? ''] ?? null; ?>
|
||||
<li class="logo-farbe">
|
||||
<?php if ($klasse !== null): ?>
|
||||
<span class="logo-farbe__swatch logo-farbe__swatch--<?= e($klasse) ?>"></span>
|
||||
<?php endif; ?>
|
||||
<div class="logo-farbe__body">
|
||||
<h3 class="logo-farbe__name"><?= e($farbe['name']) ?></h3>
|
||||
<dl class="logo-farbe__werte">
|
||||
<div><dt>HEX</dt><dd><?= e($farbe['hex']) ?></dd></div>
|
||||
<div><dt>RGB</dt><dd><?= e($farbe['rgb']) ?></dd></div>
|
||||
<div><dt>CMYK</dt><dd><?= e($farbe['cmyk']) ?></dd></div>
|
||||
</dl>
|
||||
<?php if (!empty($farbe['hinweis'])): ?>
|
||||
<p class="logo-farbe__hinweis"><?= e($farbe['hinweis']) ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
// Rendert nichts, solange retro[] leer ist: alte Vereinslogos liegen bisher nirgends vor.
|
||||
component('logo-downloads', [
|
||||
'varianten' => $page['retro'] ?? [],
|
||||
'title' => $page['retro_title'] ?? 'Frühere Vereinslogos',
|
||||
'text' => $page['retro_text'] ?? '',
|
||||
'anchor' => 'frueher',
|
||||
'tinted' => true,
|
||||
]);
|
||||
@@ -34,6 +34,7 @@ return [
|
||||
'veranstaltungen' => ['file' => 'veranstaltungen.php', 'sitemap' => true],
|
||||
'kontakt' => ['file' => 'kontakt.php', 'sitemap' => true],
|
||||
'partner-werden' => ['file' => 'partner-werden.php', 'sitemap' => true],
|
||||
'logo' => ['file' => 'logo.php', 'sitemap' => true],
|
||||
'mitglied-werden' => ['file' => 'mitglied-werden.php', 'sitemap' => true],
|
||||
'impressum' => ['file' => 'impressum.php', 'sitemap' => true],
|
||||
'datenschutz' => ['file' => 'datenschutz.php', 'sitemap' => true],
|
||||
|
||||
Reference in New Issue
Block a user