Leerer srcset-Kandidat macht zehn Bilder unbrauchbar #133

Closed
opened 2026-09-10 08:15:31 +00:00 by Phil · 1 comment
Owner

Symptom

Auf der Startseite laden zehn Bildquellen nicht — der Browser meldet naturalWidth === 0, im Layout steht ein leerer Rahmen mit Alt-Text. Betroffen sind unter anderem beide Goldader-Bilder der Goldprozess-Sektion, zwei Porträtbilder und der Würfel der Vergleichssektion.

Nachweis im gerenderten HTML:

srcset="/assets/images/_375xAUTO_.../goldader-schlaufe-v3-v3.png 375w,  750w, /assets/images/_1125xAUTO_..."

Der zweite Kandidat hat keine Adresse. Der Browser liest die nackte Breite 750w als URL, fragt /750w an, bekommt einen Fehler und verwirft das Bild komplett.

curl -s http://localhost:8090/ | grep -cE 'srcset="[^"]*(,|")\s+[0-9]+w'
# → 10

Ursache

templates/_components/core/image.twig:64-68 baut die srcset-Kandidaten in einer Schleife und hängt die Breite auch dann an, wenn image.getUrl(...) einen leeren String liefert:

{% set fallbackCandidates = fallbackCandidates|merge([image.getUrl({...}) ~ ' ' ~ w ~ 'w']) %}

Ist die URL leer, entsteht der String " 750w" — ein Kandidat ohne Adresse, der die gesamte srcset-Liste unbrauchbar macht.

Gegengeprüft: getUrl liefert für dieselben Assets heute alle vier Breiten korrekt. Das kaputte Markup stammt aus dem Blitz-Seiten-Cache (config/blitz.php, cachingEnabled => true), gefüllt in einem Moment, in dem der 750er-Transform noch nicht fertig war. config/general.php:49 setzt generateTransformsBeforePageLoad(true), was genau diesen Zwischenzustand einfrieren lässt.

Cache leeren ist deshalb kein Fix — der Zustand kommt wieder, sobald der Cache in einem ungünstigen Moment neu gefüllt wird. Die Komponente muss einen leeren Kandidaten überstehen.

Akzeptanzkriterien

  • Leere Kandidaten werden aus webpCandidates und fallbackCandidates gefiltert, bevor |join(', ') läuft
  • Bleibt kein Kandidat übrig, wird gar kein srcset/sizes ausgegeben, nur src
  • curl -s http://localhost:8090/ | grep -cE 'srcset="[^"]*(,|")\s+[0-9]+w' liefert 0
  • Alle <img> der Startseite haben naturalWidth > 0

Hinweis: Ein zweiter Befund (angeblich unsichtbare 96-px-Überschrift) stand ursprünglich hier und hat sich beim Nachprüfen als Gestaltungsentscheidung erwiesen — siehe Kommentar unten. Dieses Issue umfasst nur den srcset-Fix.

## Symptom Auf der Startseite laden zehn Bildquellen nicht — der Browser meldet `naturalWidth === 0`, im Layout steht ein leerer Rahmen mit Alt-Text. Betroffen sind unter anderem **beide Goldader-Bilder der Goldprozess-Sektion**, zwei Porträtbilder und der Würfel der Vergleichssektion. Nachweis im gerenderten HTML: ``` srcset="/assets/images/_375xAUTO_.../goldader-schlaufe-v3-v3.png 375w, 750w, /assets/images/_1125xAUTO_..." ``` Der zweite Kandidat hat keine Adresse. Der Browser liest die nackte Breite `750w` als URL, fragt `/750w` an, bekommt einen Fehler und verwirft das Bild komplett. ``` curl -s http://localhost:8090/ | grep -cE 'srcset="[^"]*(,|")\s+[0-9]+w' # → 10 ``` ## Ursache `templates/_components/core/image.twig:64-68` baut die srcset-Kandidaten in einer Schleife und hängt die Breite auch dann an, wenn `image.getUrl(...)` einen leeren String liefert: ```twig {% set fallbackCandidates = fallbackCandidates|merge([image.getUrl({...}) ~ ' ' ~ w ~ 'w']) %} ``` Ist die URL leer, entsteht der String `" 750w"` — ein Kandidat ohne Adresse, der die gesamte srcset-Liste unbrauchbar macht. Gegengeprüft: `getUrl` liefert für dieselben Assets **heute** alle vier Breiten korrekt. Das kaputte Markup stammt aus dem Blitz-Seiten-Cache (`config/blitz.php`, `cachingEnabled => true`), gefüllt in einem Moment, in dem der 750er-Transform noch nicht fertig war. `config/general.php:49` setzt `generateTransformsBeforePageLoad(true)`, was genau diesen Zwischenzustand einfrieren lässt. **Cache leeren ist deshalb kein Fix** — der Zustand kommt wieder, sobald der Cache in einem ungünstigen Moment neu gefüllt wird. Die Komponente muss einen leeren Kandidaten überstehen. ## Akzeptanzkriterien - [ ] Leere Kandidaten werden aus `webpCandidates` und `fallbackCandidates` gefiltert, bevor `|join(', ')` läuft - [ ] Bleibt kein Kandidat übrig, wird gar kein `srcset`/`sizes` ausgegeben, nur `src` - [ ] `curl -s http://localhost:8090/ | grep -cE 'srcset="[^"]*(,|")\s+[0-9]+w'` liefert `0` - [ ] Alle `<img>` der Startseite haben `naturalWidth > 0` --- *Hinweis: Ein zweiter Befund (angeblich unsichtbare 96-px-Überschrift) stand ursprünglich hier und hat sich beim Nachprüfen als Gestaltungsentscheidung erwiesen — siehe Kommentar unten. Dieses Issue umfasst nur den srcset-Fix.*
Author
Owner

Korrektur: der zweite Befund ist kein Fehler

Der Abschnitt „Zweiter Befund im selben Änderungssatz" (unsichtbare 96-px-Überschrift) ist beim Nachprüfen weggefallen. p.about__main-title ist absichtlich transparent:

  • Das Craft-Feld heißt block.coreTextBackground (templates/_components/_blocks/pl_coreSection.twig:17-19) — Hintergrund-Schriftzug, nicht Fließtext.
  • src/styles/components/about.scss:94-131 setzt color: transparent auf dem Element und rendert den Text über ::before (radialer Gradient, background-clip: text) und ::after (rgba($fontColor, .2)). Die Schrift wird also gezeichnet, nur sehr blass — als Geistertypo hinter der Sektion.
  • Die lesbaren Aussagen der Sektion stehen in block.coreItems als <h2> in den Karten davor (pl_coreSection.twig:30-34).

Beleg-Screenshot bei scrollY 900 zeigt den Schriftzug sichtbar, wenn auch kontrastarm und an beiden Viewport-Rändern beschnitten.

Ob dieser Effekt gestalterisch trägt, ist eine berechtigte Frage — aber sie gehört in die Gestaltungsarbeit an der Sektion, nicht in diesen Fehler-PR. Dieses Issue umfasst nur noch den srcset-Fix.

Ebenfalls hinfällig: der andernorts gemeldete Zahlenwiderspruch „139+ Stunden gegen 400 Stunden". Das Zahlenband zählt animiert hoch (data-counter-countdown, pl_coreSection.twig:9); die Screenshots der Kritiker entstanden mitten in der Animation. Die Endwerte sind 100+ / 400+ / 10+ und decken sich mit dem Rest der Seite.

## Korrektur: der zweite Befund ist kein Fehler Der Abschnitt „Zweiter Befund im selben Änderungssatz" (unsichtbare 96-px-Überschrift) ist beim Nachprüfen weggefallen. `p.about__main-title` ist **absichtlich** transparent: - Das Craft-Feld heißt `block.coreTextBackground` (`templates/_components/_blocks/pl_coreSection.twig:17-19`) — Hintergrund-Schriftzug, nicht Fließtext. - `src/styles/components/about.scss:94-131` setzt `color: transparent` auf dem Element und rendert den Text über `::before` (radialer Gradient, `background-clip: text`) und `::after` (`rgba($fontColor, .2)`). Die Schrift wird also gezeichnet, nur sehr blass — als Geistertypo hinter der Sektion. - Die lesbaren Aussagen der Sektion stehen in `block.coreItems` als `<h2>` in den Karten davor (`pl_coreSection.twig:30-34`). Beleg-Screenshot bei scrollY 900 zeigt den Schriftzug sichtbar, wenn auch kontrastarm und an beiden Viewport-Rändern beschnitten. Ob dieser Effekt gestalterisch trägt, ist eine berechtigte Frage — aber sie gehört in die Gestaltungsarbeit an der Sektion, nicht in diesen Fehler-PR. **Dieses Issue umfasst nur noch den srcset-Fix.** Ebenfalls hinfällig: der andernorts gemeldete Zahlenwiderspruch „139+ Stunden gegen 400 Stunden". Das Zahlenband zählt animiert hoch (`data-counter-countdown`, `pl_coreSection.twig:9`); die Screenshots der Kritiker entstanden mitten in der Animation. Die Endwerte sind 100+ / 400+ / 10+ und decken sich mit dem Rest der Seite.
Phil changed title from Leere Transform-URL erzeugt unbrauchbares srcset, zehn Bilder laden nicht to Leerer srcset-Kandidat macht zehn Bilder unbrauchbar 2026-09-10 08:17:56 +00:00
Phil closed this issue 2026-09-10 08:21:35 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Phil/philflow.io#133
No description provided.