~/blog/banner-left-the-screenPOSTED
von Shady Nathan Tawfik · 9. Oktober 2026 · 7 min

Das Banner, das den Bildschirm verließ: eine Mobile-Layout-Postmortem

Ein Datenschutz-Banner sah am Desktop perfekt aus und bestand das Review. Bei 375px lag seine wichtigste Schaltfläche bei x=452 in einem 375px-Viewport — außerhalb des Bildschirms und nicht antippbar. Der Titel weigerte sich zu schrumpfen, die Wertespalte kollabierte auf null, und ein globaler Overflow-Schutz versteckte den Beweis. So hat ein Drei-Spalten-Grid das behoben.

Interaktiv[blog-2.0]

TL;DR: Ein Banner unter der Navigation maß 549px Breite in einem 375px-Handy-Viewport. Sein Titel hatte flex-shrink: 0, blieb also 399px breit, quetschte die Wertespalte auf null und drückte die wichtigste Schaltfläche auf x=452 — über den rechten Rand. Weil die Seite overflow-x: hidden nutzte, gab es keine Scrollbar: Der Button war schlicht weg. Der Fix war ein Mobile-only-Grid mit minmax(0, 1fr), ein Tap-Target für den Schließen-Button und eine sticky Action-Zeile für das aufgeklappte Panel.

Die Ausgangslage

Direkt unter der Navigation sitzt ein einzeiliges Status-Banner. Es sagt in kompakten Monospace-Chips ein paar Dinge über die aktuelle Sitzung und bietet einen "Alle Daten ansehen"-Call-to-Action, der ein Panel mit der vollständigen Aufschlüsselung öffnet. Am Desktop ist es eine saubere Reihe. Es wurde reviewt, es bestand, und niemand hatte Grund zu zweifeln.

Dann zog ich das Fenster auf die Handy-Breite, auf der die meisten Besucher tatsächlich ankommen — und die Reihe fiel leise auseinander. Nicht mit einem Fehler, nicht mit einer Scrollbar, sondern mit einem fehlenden Button. Wenn ein Control verschwindet, ohne den Build zu brechen, überlebt es jedes Review, das nur einen Desktop-Screenshot ansieht.

Die Messwerte unten kommen aus einem echten headless Chrome über das DevTools-Protokoll, das die Geometrie des Live-DOM ausliest. Es gibt keinen Ersatz dafür, die Elemente zu messen, die man tatsächlich ausgeliefert hat.

Das Muster

Ein nowrap-Titel, der nicht schrumpfen darf, garantiert Overflow

Das Titel-Element trug zwei völlig vernünftig wirkende Deklarationen:

~/code/css css
.barTitle { flex-shrink: 0; white-space: nowrap; }

Jede für sich ist in Ordnung. Zusammen, in einer Flex-Reihe, sind sie eine Falle: Der Titel weigert sich zu schrumpfen und weigert sich umzubrechen, bleibt auf einem schmalen Screen also breiter als der Platz, den er bekommen hat. Alles danach bekommt den Rest — und der ist nichts.

Eine Flex-Spalte hätte minmax(0, 1fr) sein müssen

Die Wertespalte war flex: 1. Flex-Items haben eine automatische Mindestgröße, ein flex: 1-Item schrumpft also nicht unter seine Inhaltsbreite, solange man es nicht explizit erlaubt. Der Titel fraß die Reihe, die Wertespalte kollabierte auf 0px, und die rechts fixierte Action wurde über den Rand gedrückt. Der mobile-taugliche Fix ist, die mittlere Spur wirklich schrumpfen zu lassen — mit Grid: minmax(0, 1fr).

overflow-x: hidden machte aus außerhalb ein: verschwunden

Das globale Stylesheet schützt vor horizontalem Scroll mit overflow-x: hidden. Genau dieser Schutz versteckt diese Bug-Klasse: body.scrollWidth meldete 375, für eine automatische Prüfung sah also nichts falsch aus, während der getBoundingClientRect().right der Action bei 549 lag. Es gab keine Scrollbar, die es verraten hätte, und keine Konsolenwarnung, die es gemeldet hätte. Ein Control, das existiert, fokussierbar ist und trotzdem nicht erreichbar ist, ist kein kosmetisches Problem — erst recht nicht, wenn es ein Datenschutz-Control ist.

Mobile-Bugs kommen im Paar

Der Fix der kollabierten Leiste legte einen zweiten, leiseren Fehler frei: das aufgeklappte Panel. Seine Action-Buttons — die, mit denen ein Nutzer das Panel schließen oder einklappen kann — lagen rund 2000px unterhalb des Folds, ganz unten in einem langen Scroll. Auf dem Handy sind "die Buttons funktionieren" und "die Buttons sind erreichbar" zwei verschiedene Aussagen.

TL;DR: Ein Datenschutz-Banner sah am Desktop perfekt aus und bestand das Review. Bei 375px lag seine wichtigste Schaltfläche bei x=452 in einem 375px-Viewport — außerhalb des Bildschirms und nicht antippbar. Der Titel weigerte sich zu schrumpfen, die Wertespalte kollabierte auf null, und ein Overflow-Schutz versteckte den Beweis. Der Fix war ein Drei-Spalten-Grid.

Die Messung, vorher und nachher

ViewportVorherNachher
375pxscrollWidth 549; rechter Rand der Action 549 (außerhalb); Wertespalte 0px breit; Close-Target 24×24scrollWidth 375; rechter Rand der Action 329; Wertespalte 221px; Close-Target 32×32; Titel bricht auf zwei Zeilen
768px—Balkenhöhe 66px, jedes Control im Viewport
1280pxEine Reihe, Höhe 37pxEine Reihe, Höhe 37px — Desktop pixelgleich

Der Fix ist ein reines Mobile-Layout:

~/code/css css
@media (max-width: 855px) {
  .barMain {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    flex-basis: calc(100% - 2.75rem);
  }
  .dot       { grid-area: 1 / 1; }
  .barTitle  { grid-area: 1 / 2 / 2 / 4; white-space: normal; min-width: 0; }
  .barValues { grid-area: 2 / 1 / 3 / 3; }
  .barAction { grid-area: 2 / 3; justify-self: end; }
  .barClose  { width: 32px; height: 32px; }
  .actions   { position: sticky; bottom: 0; }
}

Der Angriffspfad eines verschwindenden Controls

Desktop-Review bestehtTitel hat flex-shrink 0 und nowrapBei 375px bleibt der Titel 399px breitMittlere Spalte kollabiert auf 0pxAction wird an den rechten Rand 549 gedrücktoverflow-x hidden entfernt die ScrollbarCTA und Consent-Controls werden unerreichbar

Reproduktion im Labor

Jede Prüfung unten zielt auf eine Seite, die ich lokal kontrolliere. Es geht nicht um das Banner — es geht um die Messtechnik.

~/code/bash bash
# 1. die Geometrie des tatsächlich ausgelieferten Controls je Viewport auslesen
node cdp-measure.js --viewport 375x812
# <- Dump: Viewport, body.scrollWidth und das Rect jedes Elements

# 2. die entscheidende Zusicherung: nichts darf die Viewport-Breite überschreiten
node -e "const r=require('./rects.json'); \
  const over=r.filter(e=>e.right>r.viewport); \
  if(over.length){console.error('OFFSCREEN',over);process.exit(1)}"
# <- schlägt laut fehl, wenn ein Control jenseits des rechten Rands liegt

# 3. prüfen, ob der Schutz das Problem versteckt
node -e "const d=await page.evaluate(()=>({ \
  scrollW: document.scrollingElement.scrollWidth, \
  overflowX: getComputedStyle(document.documentElement).overflowX }));"
# <- scrollW entspricht dem Viewport, WEIL overflow-x hidden ist, nicht weil es passt
~/code/text text
# die Messung ist der Test:
#   für jeden Breakpoint (320, 375, 768, 1024, 1280):
#     jedes interaktive Element muss im Bereich 0 .. Viewport-Breite liegen
#     die Action-Zeile muss erreichbar sein, ohne unter den Fold zu scrollen
#     Tap-Targets müssen im schmalsten Layout >= 44px sein

Erkennung im eigenen Bestand

  • Messen statt schiefen. Ein Desktop-Screenshot beweist nichts über das Layout, das bei 375px rendert. Eine echte Engine steuern und getBoundingClientRect() auslesen.
  • Gegen den Viewport zusichern, nicht gegen einen Screenshot. Die Prüfung fehlschlagen lassen, wenn der right-Wert irgendeines interaktiven Elements die Viewport-Breite überschreitet — dann kann Overflow kein Review mehr passieren.
  • overflow-x: hidden nicht vertrauen. Es versteckt das Symptom. Wenn scrollWidth nur deshalb dem Viewport entspricht, weil Overflow geclippt wird, bist du nicht sicher — du bist blind.
  • Umbrechen als Feature behandeln. flex-shrink: 0 plus white-space: nowrap ist ein Versprechen, das man auf kleinen Screens nicht halten kann. Greif zu minmax(0, 1fr) und lass die Tagline umbrechen.
  • Den aufgeklappten Zustand testen, nicht nur den eingeklappten. Lange Panels verstecken ihre Action-Buttons am Ende eines Scrolls; eine sticky Action-Zeile hält sie erreichbar.
  • Die Tap-Targets gleich mitfixen. Ein 24×24-Close-Button unter der 44px-Empfehlung ist ein zweiter Bug im selben Component.

Behebung

~/code/text text
# sofort
1. der mobilen Leiste ein Grid geben: auto | minmax(0, 1fr) | auto
2. den Titel umbrechen lassen: white-space normal; min-width 0
3. die Action im Layout halten: justify-self end, nie margin-left auto
4. den Close-Button auf ein 32px+ Tap-Target heben; 44px, wo Platz ist

# innerhalb einer Woche
5. die Panel-Actions stickiy am unteren Viewport-Rand fixieren
6. 320 / 375 / 768 / 1024 / 1280 nachmessen und zusichern, dass nichts die Breite überschreitet
7. die Zusicherung in CI aufnehmen, damit die nächste Template-Änderung sie nicht wieder öffnet

# innerhalb eines Monats
8. jede weitere fixed-width-nowrap-Chip-Reihe im Designsystem auditieren
9. prüfen, dass Datenschutz- und Consent-Controls im schmalsten Layout erreichbar sind

Was es kostet

Das ist kein kosmetischer Fund. Das Control, das den Bildschirm verließ, war der Einstieg in die Detailansicht des Banners — dorthin, wo ein Besucher sieht und handelt, was gesammelt wurde. Ein Datenschutz- oder Consent-Control, das der Nutzer nicht erreicht, ist ein kaputtes Control, und ein kaputtes Control ausgerechnet auf der Seite, die Transparenz verspricht, ist der schlechteste Ort dafür.

  • Betroffen sind die Nutzer auf den kleinsten Screens — Mobile ist die Mehrheit des Traffics, kein Randfall.
  • Der Fehler ist lautlos: kein Fehler, keine Scrollbar, keine Konsolenwarnung. Er versteckt sich vor Reviews, die einem Desktop-Render vertrauen.
  • Es ist eine Klasse von Bug, kein Einzelfall. Jede flex-shrink: 0 + nowrap-Chip-Reihe im selben Designsystem hat denselben latenten Fehler.

Fazit — was ich daraus mitnehme

  • Miss das Layout, das du ausgeliefert hast, in den Breiten, die deine Besucher nutzen. Ein Screenshot in einer Größe ist eine Annahme, kein Beweis.
  • flex-shrink: 0 und white-space: nowrap sind in Ordnung, bis der Screen schmal wird. Auf Mobile fressen Titel so Buttons.
  • overflow-x: hidden behebt Overflow nicht; es versteckt den Beweis. Sichere stattdessen Element-Rechtecke gegen den Viewport zu.
  • Wenn ein Control ein Datenschutz- oder Consent-Control ist, gehört "auf Mobile erreichbar" zur Definition, nicht zum Nice-to-have.
  • Fix den Breakpoint, dann sichere ihn ab. Dieselbe Reihe kommt zurück, sobald jemand das Template bearbeitet.
Shady Nathan Tawfik

Gründer & Cyber IT Security Engineer in Bielefeld. Offensive Security, Audits und Fullstack-Entwicklung. Mehr auf

Nur zu Bildungszwecken. Alle reproduzierbaren Befehle zielen auf meine eigene Lab-Umgebung (localhost), nie auf ein laufendes System. © 2026