Markenlogos einbinden: Seitenkopf und alle Kacheln

Zulieferung dev/2026-08-28/markenlogos/ (CABINET und Rotpunkt als SVG, Februe
als PNG). Die Dateien liegen unter public/img/assets/marken/ und haengen als
`logo` an der jeweiligen Marke.

- Kopf der Markenseite: Logo anstelle des Schriftzugs, im <h1>, damit die
  Ueberschrift erhalten bleibt – den Text liefert das alt-Attribut. Ohne Logo
  erscheint weiterhin der Schriftzug.
- Logo zusaetzlich auf der Startseiten-Kachel, im Block auf /marken und in den
  „Weitere Marken“-Kacheln am Seitenfuss.
- Feste Box (Hoehe + Maximalbreite + object-contain) statt fester Hoehe: die
  Seitenverhaeltnisse gehen weit auseinander (Februe 2,3:1, Rotpunkt 1,2:1),
  mit reiner Hoehenangabe waere Februe doppelt so breit erschienen. Konvention
  aus der Sponsoring-Sektion.
- Februe-Logo auf seinen Inhalt beschnitten: im Original waren knapp drei
  Viertel der Flaeche leerer Rand, in einer Box haette es winzig gewirkt.
  53 KB → 15 KB.
- BrandLogoSeeder traegt die Logos auf bestehenden Installationen nach –
  CmsBrandSeeder ueberspringt vorhandene Marken. Setzt nur `logo` und nur bei
  leerem Feld, ein im Admin hochgeladenes Logo bleibt stehen.

Tests: 871 gruen (3215 Assertions), Pint sauber, alle Themes gebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-03 11:16:41 +00:00
parent 093e0ece51
commit c8899e2d7d
12 changed files with 282 additions and 13 deletions

View file

@ -0,0 +1,59 @@
<?php
namespace Database\Seeders;
use App\Models\CmsBrand;
use Illuminate\Database\Seeder;
/**
* Traegt die Markenlogos nach.
*
* Grundlage: dev/2026-08-28/markenlogos/ (Zulieferung vom 03.09.2026). Die Dateien liegen unter
* public/img/assets/marken/; das Februe-Logo ist dabei auf seinen Inhalt beschnitten – im
* Original war knapp drei Viertel der Flaeche leerer Rand, wodurch es neben den beiden SVGs
* winzig gewirkt haette.
*
* {@see CmsBrandSeeder} bringt dieselben Werte fuer frische Installationen mit, ueberspringt
* aber bereits angelegte Marken. Dieser Seeder fasst nur das Feld `logo` an und auch nur dann,
* wenn es leer ist – ein selbst hochgeladenes Logo bleibt also stehen. Mehrfaches Ausfuehren
* ist unbedenklich.
*
* php artisan db:seed --class=BrandLogoSeeder
*/
class BrandLogoSeeder extends Seeder
{
/** @var array<string, string> */
private const LOGOS = [
'cabinet' => 'marken/cabinet.svg',
'rotpunkt' => 'marken/rotpunkt.svg',
'februe' => 'marken/februe.png',
];
public function run(): void
{
$set = 0;
$kept = 0;
foreach (self::LOGOS as $slug => $logo) {
$brand = CmsBrand::query()->where('slug', $slug)->first();
if (! $brand) {
$this->command?->warn("Marke „{$slug}“ nicht gefunden – uebersprungen.");
continue;
}
if (trim((string) $brand->logo) !== '') {
$kept++;
continue;
}
$brand->logo = $logo;
$brand->save();
$set++;
}
$this->command?->info("Markenlogos gesetzt: {$set}, vorhandene behalten: {$kept}.");
}
}

View file

@ -8,9 +8,11 @@ use Illuminate\Database\Seeder;
/**
* Legt die drei Kernmarken an. Texte aus dev/2026-08-28/umsetzung-inhalte.md §5.
*
* Bestehende Marken werden nicht überschrieben – Marcel pflegt sie im Admin.
* Logos, Stimmungsbilder und Galerien fehlen als Zulieferung und bleiben leer;
* die Kacheln und Detailseiten zeigen so lange einen Platzhalter.
* Bestehende Marken werden nicht überschrieben – Marcel pflegt sie im Admin. Für bereits
* angelegte Marken setzt {@see BrandLogoSeeder} die Logos nach.
*
* Stimmungsbilder und Galerien fehlen als Zulieferung und bleiben leer; die Kacheln und
* Detailseiten zeigen so lange einen Platzhalter.
*/
class CmsBrandSeeder extends Seeder
{
@ -38,6 +40,7 @@ class CmsBrandSeeder extends Seeder
return [
[
'slug' => 'cabinet',
'logo' => 'marken/cabinet.svg',
'name' => ['de' => 'CABINET', 'en' => 'CABINET'],
'tagline' => ['de' => 'Einbauschränke nach Maß.', 'en' => 'Fitted wardrobes made to measure.'],
'teaser' => [
@ -72,6 +75,7 @@ Experience the possibilities in our Bielefeld showroom.',
],
[
'slug' => 'rotpunkt',
'logo' => 'marken/rotpunkt.svg',
'name' => ['de' => 'Rotpunkt Küchen', 'en' => 'Rotpunkt Küchen'],
'tagline' => ['de' => 'Küchen, die Haltung zeigen.', 'en' => 'Kitchens with character.'],
'teaser' => [
@ -106,6 +110,7 @@ Consultation and planning in our kitchen studio.',
],
[
'slug' => 'februe',
'logo' => 'marken/februe.png',
'name' => ['de' => 'Febrü', 'en' => 'Febrü'],
'tagline' => ['de' => 'Arbeiten, wo man gerne bleibt.', 'en' => 'Work where you like to stay.'],
'teaser' => [

View file

@ -33,6 +33,7 @@ database/migrations/2026_08_28_110407_create_cms_brands_table.php
database/migrations/2026_08_28_112244_add_summary_and_highlights_to_cms_brands_table.php
database/migrations/2026_09_03_084254_add_page_sections_to_cms_brands_table.php
database/seeders/BrandGalleryCaptionNormalizerSeeder.php
database/seeders/BrandLogoSeeder.php
database/seeders/BrandPageTextSeeder.php
database/seeders/CmsArticleSeeder.php
database/seeders/CmsBrandSeeder.php
@ -45,6 +46,9 @@ database/seeders/InteriorRelaunchCmsSeeder.php
public/_cabinet/assets/b2in-interior-logo-negative.svg
public/_cabinet/assets/b2in-interior-logo-positive.svg
public/_cabinet/display/index.html
public/img/assets/marken/cabinet.svg
public/img/assets/marken/februe.png
public/img/assets/marken/rotpunkt.svg
public/img/assets/sponsoring/arminia-bielefeld.png
public/img/assets/sponsoring/tc-bad-waldliesborn-horn.jpg
public/img/assets/sponsoring/tc-hoevelhof.png

View file

@ -19,7 +19,7 @@ eingearbeitet. Offen: Bildmaterial, Rechtstexte und die Herstellerfreigaben.
**Stand 03.09.2026:** T1 und B1–B13 abgearbeitet, **alle Arbeitspakete AP0–AP6 umgesetzt**, die
Bestandsinhalte auf den Interior-Auftritt umgestellt, die Seite abgestimmt und alle vier Punkte
der Abstimmungsrunde vom 03.09. umgesetzt (Nachtrag 5). Alle drei Markenseiten haben jetzt einen
ausführlichen Text. `php artisan test --compact`: **863 grün** (3194 Assertions), Pint sauber,
ausführlichen Text. `php artisan test --compact`: **871 grün** (3215 Assertions), Pint sauber,
`npm run build:b2in` gebaut.
**Die Seite ist testbereit** – die ausstehenden Zulieferungen trägt Marcel selbst im CMS nach.
@ -45,7 +45,7 @@ gebaut. Was fehlt, ist Material, kein Code.
| Was | Wo im Admin |
|---|---|
| Logos und Stimmungsbilder der drei Marken | Kernmarken → Marke bearbeiten |
| Stimmungsbilder der drei Marken (Logos liegen seit 03.09. vor) | Kernmarken → Marke bearbeiten |
| Echte Projektfotos statt der Demobilder (siehe Warnung unten) | Kernmarken → Bildergalerie |
| Herstellerseiten-URLs | Kernmarken → Herstellerseite |
| Motiv für den Kopf von `/marken` | Inhalte → `marken` → `hero` |
@ -1026,6 +1026,36 @@ diese Lösung kommt ohne neue Abhängigkeit aus.
Betrifft **beide** Akkordeons – die Vertiefungen auf den Markenseiten und das FAQ; sie hatten
dasselbe Verhalten.
#### 6 · Markenlogos (Zulieferung 03.09.2026)
Grundlage: `dev/2026-08-28/markenlogos/` – CABINET und Rotpunkt als SVG, Febrü als PNG. Sie
liegen jetzt unter `public/img/assets/marken/` und hängen als `logo` an der jeweiligen Marke.
| Wo | Vorher | Jetzt |
|---|---|---|
| Kopf der Markenseite | Schriftzug als `<h1>` | Logo **anstelle** des Schriftzugs, im `<h1>` |
| Kachel auf der Startseite | ohne Logo | Logo über Name und Claim |
| Block auf `/marken` | ohne Logo | Logo über Name und Claim |
| „Weitere Marken" am Seitenfuß | ohne Logo | Logo über Name und Claim |
Das Logo sitzt **im** `<h1>`, nicht daneben: So bleibt die Seitenüberschrift erhalten, den Text
liefert das `alt`-Attribut. Fehlt ein Logo, erscheint weiterhin der Schriftzug – ein Test hält
beide Wege fest.
**Feste Box statt fester Höhe.** Die drei Logos haben sehr unterschiedliche Seitenverhältnisse
(Febrü 2,3:1, Rotpunkt 1,2:1). Mit einer reinen Höhenangabe hätte Febrü doppelt so breit gewirkt
wie Rotpunkt. Jedes Logo bekommt deshalb Höhe **und** Maximalbreite plus `object-contain` – die
Konvention aus der Sponsoring-Sektion.
**Am Febrü-Logo nachgearbeitet:** Im Original waren knapp drei Viertel der Bildfläche leerer
Rand (Inhalt 570 × 238 in 3084 × 1948). In einer Box hätte es dadurch winzig gewirkt. Es ist auf
seinen Inhalt beschnitten und auf 760 px Breite gerechnet – 53 KB → 15 KB.
**Nachziehen auf bestehenden Installationen:** `CmsBrandSeeder` überspringt vorhandene Marken,
trägt die Logos also nur bei einer frischen Installation ein. `BrandLogoSeeder` **(neu)** setzt
sie auf bereits angelegten Marken nach – nur das Feld `logo` und nur, wenn es leer ist. Ein
selbst hochgeladenes Logo bleibt damit stehen; mehrfaches Ausführen ist unbedenklich.
#### Tests
`BrandPageSectionsTest` und `BrandPageTest` erweitert:
@ -1045,8 +1075,12 @@ dasselbe Verhalten.
- Weder Markenseite noch FAQ verwenden noch den Flächen-Hover.
- Der Abschlussblock trägt das Verlaufsband; die weichen Kreise sind weg.
- Die Ausnahme für `.variante-glass-flow` ist vorhanden – ohne sie steht der Abschluss hellgrau da.
- Das Logo steht im `<h1>` der Markenseite; ohne Logo erscheint weiter der Schriftzug.
- Alle drei Logos erscheinen auf Startseite, `/marken` und den „Weitere Marken"-Kacheln.
- Die Dateien, auf die der Seeder zeigt, liegen tatsächlich unter `public/`.
- `BrandLogoSeeder` überschreibt kein selbst hochgeladenes Logo.
`php artisan test --compact`: **863 grün** (3194 Assertions), Pint sauber, `npm run build:b2in`
`php artisan test --compact`: **871 grün** (3215 Assertions), Pint sauber, `npm run build:b2in`
gebaut.
---
@ -1182,6 +1216,10 @@ php artisan db:seed --class=BrandPageTextSeeder --force
# Fasst nur den Text an, mehrfaches Ausführen ist unbedenklich.
php artisan db:seed --class=BrandGalleryCaptionNormalizerSeeder --force
# 6. Markenlogos nachtragen. Setzt `logo` nur, wenn das Feld leer ist – ein im Admin
# hochgeladenes Logo bleibt stehen. Braucht public/img/assets/marken/ aus dem Archiv.
php artisan db:seed --class=BrandLogoSeeder --force
php artisan config:cache
php artisan view:clear
@ -1205,7 +1243,8 @@ done
- [ ] Startseite: Reihenfolge, Kernmarken-Anker `#marken`, Sponsoring-Logos
- [ ] Markenseite `/marken` – Aufbau, Merkmale, „Alle Marken ansehen" von der Startseite
- [ ] Kernmarken-Detailseiten `/marken/cabinet`, `/marken/rotpunkt`, `/marken/februe` – je mit
ausführlichem Text, aufklappbaren Vertiefungen und eigenem Abschlussblock
Logo im Kopf, ausführlichem Text, aufklappbaren Vertiefungen und eigenem Abschlussblock
- [ ] Logos erscheinen auf der Startseite, auf `/marken` und in den „Weitere Marken"-Kacheln
- [ ] Auf dem Smartphone prüfen: Das Terminanfrage-Widget trägt auf der Markenseite die längere
Beschriftung und darf keine Inhalte verdecken
- [ ] `/netzwerk`, `/interior`, `/partner`, `/service` leiten auf `/marken`

View file

@ -186,6 +186,9 @@ php artisan db:seed --class=BrandPageTextSeeder --force
# Nur nötig, wenn die Demogalerien schon eingespielt sind. Mehrfach ausführbar.
php artisan db:seed --class=BrandGalleryCaptionNormalizerSeeder --force
# Markenlogos nachtragen. Setzt `logo` nur, wenn das Feld leer ist.
php artisan db:seed --class=BrandLogoSeeder --force
# Display-Branding auf das Interior-Logo
php artisan db:seed --class=DisplayInteriorBrandingSeeder --force

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 30.8.1, SVG Export Plug-In . SVG Version: 9.03 Build 0) -->
<svg version="1.0" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 595.3 419.5" style="enable-background:new 0 0 595.3 419.5;" xml:space="preserve">
<g>
<path d="M111.4,150.1c9,0,17.5,2.2,25.4,6.6c7.9,4.4,14.5,10.1,19.8,17l-9.3,23c-3-7.8-7.5-14.6-13.6-20.5
c-6.1-5.9-13.2-8.8-21.3-8.8c-10.8,0-20,4.2-27.6,12.6c-7.6,8.4-11.4,18.5-11.4,30.2c0,11.8,3.8,22,11.4,30.4
c7.6,8.4,16.8,12.6,27.6,12.6c9.3,0,17.1-2.6,23.4-7.8l23.9,0c-5.2,7.7-12.1,13.6-20.8,18c-8.7,4.3-17.8,6.5-27.4,6.5
c-15.8,0-29.3-5.9-40.5-17.6S54.1,226.5,54.1,210c0-16.5,5.6-30.5,16.8-42.3C82.1,156,95.6,150.1,111.4,150.1L111.4,150.1z"/>
<path d="M138.2,238.7l30.2-89l24.1,0l40,120.3l-16.9,0l-11.3-31.2L138.2,238.7L138.2,238.7z M179.4,172.6L164,221.5l30.4,0
L179.4,172.6L179.4,172.6z"/>
<path d="M239.2,197.7c1.4-0.1,4-0.1,7.6-0.1c3.7,0,7.5,0.1,11.5,0.1c4,0,7.7-0.1,11-0.2c3.3-0.2,5.3-0.5,6-1.1
c0.1,0,0.2-0.1,0.3-0.2c3.4-2.7,5.1-7.2,5.1-13.5c0-5.5-1.5-9.6-4.6-12.4c-3-2.6-7.7-4-13.9-4l-34.2,0l0,27.2l-14.1,0l0-43.8
l47.7,0c11.1,0,19.6,2.8,25.6,8.5c5.8,5.6,8.7,13.8,8.7,24.6c0,5-1,9.4-2.9,13.2c-1.9,3.8-4.6,6.9-8.3,9.4
c5.2,1.8,9.2,5.2,12.2,10.2c3,5.1,4.5,11.1,4.5,18c0,5.5-0.9,10.8-2.7,15.9c-1.9,4.9-4.4,9-7.6,12.3c-3.2,3.1-6.8,5.3-10.8,6.6
c-4.1,1.3-9.8,2-17,2l-24.2,0l0-16.1l13.9,0c0.4,0,0.8-0.1,1.2-0.2l11.8,0c7.1,0,12.2-1.5,15.5-4.5c3.3-3,5-7.6,5-14
c0-6.9-1.7-12.1-5.2-15.5c-1.6-1.6-3.6-2.9-5.9-3.8c-2.6-1-5.8-1.5-9.4-1.5l-26.7,0c0-1.1-0.1-2.3-0.1-3.6
C239.2,209.8,239.2,197.7,239.2,197.7L239.2,197.7z"/>
<polygon points="307.5,149.8 325.2,149.8 325.1,230.9 307.4,230.9 307.5,149.8 "/>
<polygon points="332.2,270.4 332.2,149.9 351.1,149.9 397.7,235.4 397.7,149.8 415.9,149.8 415.9,270.5 397,270.5 350,182
349.9,270.4 332.2,270.4 "/>
<path d="M430.4,199.7l-4.2,0L430.4,199.7L430.4,199.7z M436.4,199.9l-20.4,0L436.4,199.9L436.4,199.9z M421.3,149.8l56.3,0l0,17.8
l-56.3,0L421.3,149.8L421.3,149.8z M421.3,199.7l56.3,0l0,17.8l-56.3,0L421.3,199.7L421.3,199.7z M421.2,252.8l56.3,0l0,17.7
l-56.3,0L421.2,252.8L421.2,252.8z"/>
<polygon points="483.8,149.6 540.4,149.6 540.4,168.7 503.2,168.7 503.5,269.9 483.8,269.9 483.8,149.6 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 492.2 409.6" style="enable-background:new 0 0 492.2 409.6;" xml:space="preserve">
<style type="text/css">
.st0{fill:#E30421;}
.st1{fill:#2D2D2D;}
</style>
<circle class="st0" cx="357.5" cy="115.3" r="65"/>
<path class="st1" d="M123.3,215.6c-1.2-2.9-2.8-5.4-4.8-7.7c-2-2.3-4.5-4.1-7.3-5.5c-2.8-1.4-6-2.1-9.3-2.1H71.6v71H88v-22.9h14.4
c3.4,0,6.5-0.7,9.3-2c2.8-1.3,5.2-3.1,7.1-5.4c2-2.3,3.5-4.8,4.6-7.7c1.1-2.9,1.7-5.9,1.7-9C125.1,221.3,124.5,218.4,123.3,215.6z
M106.4,231.6c-1.4,1.7-3.1,2.5-5,2.5H88v-19.4h12.8c0.9,0,1.9,0.2,2.8,0.6c0.9,0.4,1.8,1,2.5,1.8c0.7,0.8,1.3,1.8,1.8,3
c0.4,1.2,0.7,2.6,0.7,4.3C108.5,227.5,107.8,229.9,106.4,231.6z"/>
<path class="st1" d="M160.8,255.6c-1.9-1.1-3.4-2.7-4.6-4.6c-1.2-1.9-2.1-4.1-2.6-6.6c-0.5-2.5-0.8-5.1-0.8-7.8v-36.3h-16.4v36.3
c0,4.7,0.6,9.1,1.8,13.4c1.2,4.3,3,8,5.5,11.3c2.5,3.2,5.8,5.8,9.8,7.8c4,1.9,8.8,2.9,14.5,2.9c5.5,0,10.2-0.9,14.2-2.8
c4-1.8,7.2-4.4,9.8-7.5c2.6-3.2,4.5-6.9,5.7-11.2c1.2-4.3,1.9-8.9,1.9-13.8v-36.3H183v36.3c0,2.6-0.2,5.2-0.8,7.7s-1.4,4.7-2.6,6.7
c-1.2,1.9-2.8,3.5-4.7,4.6c-1.9,1.2-4.3,1.7-7.1,1.7S162.7,256.7,160.8,255.6"/>
<polygon class="st1" points="264.7,271.3 277.9,271.3 277.9,200.3 261.5,200.3 261.5,242 228.4,200.3 215.6,200.3 215.6,271.3
232,271.3 232,230.7 "/>
<polygon class="st1" points="311.4,271.3 311.4,250.4 318.8,242.7 338.9,271.3 357.6,271.3 329.2,232 355.8,200.3 337.4,200.3
311.4,230.9 311.4,200.3 295,200.3 295,271.3 "/>
<path class="st1" d="M420.5,200.3h-59.7v14.4h21.7v56.6h16.4v-56.6h21.6V200.3z"/>
<polygon class="st1" points="272.4,108.3 212.7,108.3 212.7,122.7 234.4,122.7 234.4,179.3 250.8,179.3 250.8,122.7 272.4,122.7 "/>
<path class="st1" d="M115.2,152.9c1.8-1,3.4-2.2,4.8-3.7c1.4-1.4,2.7-3,3.7-4.9c1-1.8,1.8-3.7,2.3-5.8c0.5-2,0.8-4.2,0.8-6.4
c0-2.9-0.6-5.8-1.8-8.7c-1.2-2.9-2.8-5.4-4.9-7.7c-2.1-2.3-4.5-4.1-7.4-5.5c-2.8-1.4-5.9-2.1-9.2-2.1h-32v71H88v-22.9h10.7
l13.6,22.9h18.5L115.2,152.9z M108,139.3c-1.4,1.8-3.1,2.8-5,2.8H88v-19.4h14.5c1.9,0,3.6,0.9,5.2,2.6c1.6,1.7,2.4,4.1,2.4,7
C110.1,135.1,109.4,137.5,108,139.3z"/>
<path class="st1" d="M155.8,136c0.8-2.6,1.9-4.8,3.5-6.8c1.6-2,3.5-3.6,5.8-4.9c2.3-1.2,5-1.9,8.2-1.9c3,0,5.7,0.6,7.9,1.8
c2.3,1.2,4.2,2.7,5.9,4.7c1.6,2,2.8,4.2,3.6,6.8c0.8,2.6,1.2,5.2,1.2,7.9c0,2.7-0.4,5.3-1.2,7.9c-0.8,2.6-1.9,4.9-3.4,6.9
c-1.5,2-3.5,3.7-5.8,5c-2.3,1.3-5.1,1.9-8.2,1.9c-3,0-5.7-0.6-8-1.8c-2.3-1.2-4.3-2.8-5.9-4.8c-1.6-2-2.8-4.3-3.6-6.8
c-0.8-2.6-1.2-5.2-1.2-8C154.7,141.2,155.1,138.6,155.8,136 M187.4,176.9c4.3-2,8.1-4.6,11.2-7.9c3.1-3.3,5.6-7.1,7.3-11.5
c1.8-4.3,2.6-8.8,2.6-13.5c0-4.5-0.8-8.9-2.5-13.2c-1.7-4.3-4-8.2-7.1-11.6c-3.1-3.4-6.8-6.1-11.1-8.2c-4.3-2.1-9.1-3.1-14.4-3.1
c-5.1,0-9.9,1-14.2,2.9c-4.3,2-8.1,4.6-11.2,7.9c-3.2,3.3-5.6,7.1-7.4,11.5c-1.8,4.3-2.7,8.8-2.7,13.5c0,4.6,0.9,9.1,2.6,13.4
c1.7,4.3,4.1,8.2,7.1,11.6c3.1,3.4,6.8,6.1,11.1,8.1c4.3,2.1,9.1,3.1,14.4,3.1C178.3,179.9,183.1,178.9,187.4,176.9"/>
<polygon class="st1" points="76.1,338.3 76.1,324.8 84.5,316.2 101.3,338.3 106.1,338.3 87.1,313.7 105.1,294.3 100.3,294.3
76.1,319.9 76.1,294.4 71.8,294.4 71.8,338.3 "/>
<path class="st1" d="M140.6,333.2c-1.8-1-3.3-2.4-4.4-4.1c-1.1-1.7-1.9-3.6-2.3-5.8c-0.5-2.2-0.7-4.4-0.7-6.8v-22.2H129v22.2
c0,2.8,0.3,5.5,0.9,8.2c0.6,2.6,1.7,5,3.1,7.1c1.4,2.1,3.3,3.7,5.7,5c2.4,1.3,5.3,1.9,8.8,1.9c3.4,0,6.3-0.6,8.6-1.8
c2.4-1.2,4.3-2.8,5.7-4.8c1.5-2,2.5-4.4,3.2-7c0.7-2.7,1-5.5,1-8.5v-22.2h-4.3v22.2c0,2.2-0.2,4.4-0.7,6.6c-0.4,2.2-1.2,4.1-2.3,5.9
c-1.1,1.7-2.6,3.1-4.4,4.2c-1.8,1.1-4.1,1.6-6.9,1.6C144.7,334.7,142.4,334.2,140.6,333.2"/>
<rect x="150.3" y="281.7" class="st1" width="3.6" height="5.8"/>
<rect x="141.1" y="281.7" class="st1" width="3.6" height="5.8"/>
<path class="st1" d="M193.7,324.3c1,2.7,2.4,5.1,4.2,7.2c1.8,2.1,4,3.9,6.5,5.2c2.5,1.3,5.3,2,8.3,2c1.7,0,3.4-0.2,5.2-0.7
c1.8-0.5,3.4-1.1,5-2c1.6-0.9,3-1.9,4.3-3.2c1.3-1.3,2.3-2.7,3-4.4l-3.6-1.9c-0.7,1.4-1.6,2.7-2.6,3.7c-1,1-2.2,1.9-3.4,2.5
c-1.2,0.7-2.5,1.2-3.8,1.5c-1.3,0.3-2.6,0.5-3.8,0.5c-2.4,0-4.5-0.5-6.5-1.5c-2-1-3.7-2.4-5.2-4.1c-1.5-1.7-2.6-3.7-3.5-5.9
c-0.8-2.2-1.3-4.6-1.3-7c0-2.2,0.3-4.4,1.1-6.6c0.7-2.2,1.7-4.1,3.1-5.9c1.4-1.7,3.1-3.1,5.1-4.2c2-1.1,4.4-1.6,7-1.6
c1.2,0,2.4,0.1,3.7,0.4c1.2,0.3,2.4,0.7,3.6,1.3c1.2,0.6,2.2,1.4,3.2,2.4c1,1,1.8,2.2,2.5,3.7l3.5-2.1c-1.4-2.9-3.4-5.2-6.2-6.9
c-2.8-1.8-6.1-2.7-10-2.7c-3.3,0-6.3,0.7-8.9,2c-2.6,1.3-4.8,3-6.6,5c-1.8,2.1-3.1,4.4-4.1,7c-0.9,2.6-1.4,5.3-1.4,7.9
C192.1,318.8,192.6,321.6,193.7,324.3"/>
<polygon class="st1" points="286.6,294.3 286.6,313.9 260.5,313.9 260.5,294.3 256.2,294.3 256.2,338.3 260.5,338.3 260.5,317.8
286.6,317.8 286.6,338.3 290.9,338.3 290.9,294.3 "/>
<polygon class="st1" points="324.9,334.5 324.9,317.7 346.4,317.7 346.4,314 324.9,314 324.9,298.2 349.6,298.2 349.6,294.3
320.6,294.3 320.6,338.3 350.2,338.3 350.2,334.5 "/>
<path class="st1" d="M410.2,338.3h3.7v-43.9h-4.3v36.5l-29.1-36.6h-3.3v44h4.3v-35.9L410.2,338.3z"/>
</svg>

After

Width:  |  Height:  |  Size: 5 KiB

View file

@ -11,13 +11,20 @@
<div class="grid lg:grid-cols-2 gap-12 items-center">
<div>
{{-- Liegt ein Logo vor, steht es anstelle des Schriftzugs. Es sitzt im <h1>,
damit die Ueberschrift erhalten bleibt – den Text liefert das alt-Attribut.
Feste Box statt fester Hoehe: die Logos haben sehr unterschiedliche
Seitenverhaeltnisse (Februe breit, Rotpunkt fast quadratisch). --}}
@if (!empty($brand['logo']))
<img src="{{ theme_image_url($brand['logo']) }}" alt="{{ $brand['name'] }}"
class="h-10 w-auto mb-6 object-contain object-left" />
<h1>
<img src="{{ theme_image_url($brand['logo']) }}" alt="{{ $brand['name'] }}"
class="h-20 sm:h-24 max-w-[17rem] w-auto object-contain object-left" />
</h1>
@else
<h1 class="text-section-title">{{ $brand['name'] }}</h1>
@endif
<h1 class="text-section-title">{{ $brand['name'] }}</h1>
<p class="text-large text-secondary font-medium mt-2">{{ $brand['tagline'] }}</p>
<p class="text-large text-secondary font-medium mt-4">{{ $brand['tagline'] }}</p>
@if (!empty($brand['showroom']))
<p class="text-muted-foreground mt-4 inline-flex items-center gap-2">
@ -346,6 +353,11 @@
<a href="{{ route('marken.show', ['slug' => $other['slug']]) }}"
wire:key="other-{{ $other['slug'] }}"
class="card-elevated rounded-2xl p-8 block hover:shadow-lg transition-shadow duration-300">
@if (!empty($other['logo']))
<img src="{{ theme_image_url($other['logo']) }}" alt="{{ $other['name'] }}"
class="h-11 max-w-[10rem] w-auto mb-4 object-contain object-left" loading="lazy" />
@endif
<h3 class="text-xl font-medium">{{ $other['name'] }}</h3>
<p class="text-secondary font-medium mt-1">{{ $other['tagline'] }}</p>
<p class="text-muted-foreground leading-relaxed mt-3">{!! $other['teaser'] !!}</p>

View file

@ -19,9 +19,11 @@
</div>
<div class="{{ $loop->even ? '' : 'lg:order-1' }}">
{{-- Feste Box statt fester Hoehe: die Markenlogos haben sehr
unterschiedliche Seitenverhaeltnisse und wirken sonst ungleich gross. --}}
@if (!empty($brand['logo']))
<img src="{{ theme_image_url($brand['logo']) }}" alt="{{ $brand['name'] }}"
class="h-9 w-auto mb-5 object-contain object-left" loading="lazy" />
class="h-14 max-w-[12rem] w-auto mb-5 object-contain object-left" loading="lazy" />
@endif
<h2 class="text-2xl lg:text-3xl font-medium">{{ $brand['name'] }}</h2>

View file

@ -22,9 +22,11 @@
@endif
<div class="p-8 flex flex-col grow">
{{-- Feste Box statt fester Hoehe: die Markenlogos haben sehr
unterschiedliche Seitenverhaeltnisse und wirken sonst ungleich gross. --}}
@if (!empty($brand['logo']))
<img src="{{ theme_image_url($brand['logo']) }}" alt="{{ $brand['name'] }}"
class="h-8 w-auto mb-4 object-contain object-left" loading="lazy" />
class="h-11 max-w-[10rem] w-auto mb-4 object-contain object-left" loading="lazy" />
@endif
<h3 class="text-xl font-medium">{{ $brand['name'] }}</h3>

View file

@ -214,3 +214,64 @@ it('strips the old example prefix without touching image, category or order', fu
->and($gallery[0]['category'])->toBe('project')
->and($gallery[1]['caption'])->toBe('Entwurf Garderobe');
});
// ========================================
// Markenlogos
// ========================================
it('shows the logo instead of the wordmark at the top of a brand page', function () {
$html = $this->get('/marken/rotpunkt')->assertSuccessful()->getContent();
// Das Logo sitzt im <h1>, damit die Ueberschrift erhalten bleibt.
expect($html)->toMatch('/<h1>\s*<img[^>]*marken\/rotpunkt\.svg/')
->and($html)->toContain('alt="Rotpunkt Küchen"');
});
it('falls back to the wordmark for a brand without a logo', function () {
CmsBrand::where('slug', 'rotpunkt')->firstOrFail()->update(['logo' => '']);
$this->get('/marken/rotpunkt')
->assertSuccessful()
->assertSee('<h1 class="text-section-title">Rotpunkt Küchen</h1>', false);
});
it('shows the logo on every tile that leads to a brand page', function (string $path) {
$html = $this->get($path)->assertSuccessful()->getContent();
foreach (['cabinet.svg', 'rotpunkt.svg', 'februe.png'] as $file) {
expect($html)->toContain('marken/'.$file);
}
})->with([
'Startseite' => ['/'],
'Markenuebersicht' => ['/marken'],
]);
it('shows the logo on the other-brands tiles of a detail page', function () {
$html = $this->get('/marken/rotpunkt')->assertSuccessful()->getContent();
// Auf der Rotpunkt-Seite verweisen die Kacheln auf CABINET und Februe.
expect($html)->toContain('marken/cabinet.svg')
->and($html)->toContain('marken/februe.png');
});
it('ships the logo files the seeder points at', function () {
foreach (['cabinet.svg', 'rotpunkt.svg', 'februe.png'] as $file) {
expect(public_path('img/assets/marken/'.$file))->toBeFile();
}
});
it('keeps a logo that was uploaded in the admin', function () {
CmsBrand::where('slug', 'cabinet')->firstOrFail()->update(['logo' => 'eigenes-logo.svg']);
$this->seed(\Database\Seeders\BrandLogoSeeder::class);
expect(CmsBrand::where('slug', 'cabinet')->firstOrFail()->logo)->toBe('eigenes-logo.svg');
});
it('adds the logo to a brand that has none yet', function () {
CmsBrand::where('slug', 'cabinet')->firstOrFail()->update(['logo' => '']);
$this->seed(\Database\Seeders\BrandLogoSeeder::class);
expect(CmsBrand::where('slug', 'cabinet')->firstOrFail()->logo)->toBe('marken/cabinet.svg');
});