‹ Alle Artikel

overflow-x killt position:sticky — lautlos

Zwei CSS-Zeilen legten eine ganze Scroll-Inszenierung lahm. Kein Fehler, keine Warnung. Die Diagnose dauert drei Zeilen.

Eine Scroll-Inszenierung sollte über vier Stationen laufen: Ein 3D-Objekt baut sich auf, der Text daneben wechselt mit. Die Sektion war 340vh hoch, das Innere klebte per position:sticky am oberen Rand.

Nur klebte nichts. Man scrollte einfach daran vorbei. Station 1 blieb stehen, 2 bis 4 kamen nie. Keine Fehlermeldung, keine Warnung in der Konsole — es passierte schlicht nichts.

Die falschen Fährten

Weil das Ganze JavaScript-gesteuert war, habe ich dort gesucht. Drei Runden lang:

  • Verdacht 1: Der IntersectionObserver. Die Fortschrittsberechnung lief hinter einem if (inView)-Gate. Bei einer 340vh-Sektion feuert der Observer aber nur an den Rändern — mittendrin nie. Klang überzeugend. Gate raus. Keine Änderung.
  • Verdacht 2: rAF-Throttling. Die Berechnung lief überrequestAnimationFrame. Also direkt aufrufen. Keine Änderung.
  • Verdacht 3: WebGL-Kontexte. Auf der Seite lief noch ein zweiter 3D-Effekt, der bei jedem Scroll-Zyklus neu geladen wurde — ein neuer GL-Kontext pro Durchgang. Browser erlauben nur acht bis sechzehn und killen dann die ältesten. Ein echtes Problem, aber nicht dieses. Behoben. Keine Änderung.

Drei plausible Theorien, drei Fehlschläge. Der gemeinsame Nenner: Ich habe im JavaScript gesucht, weil das Feature JavaScript benutzt.

Die Ursache

Zwei Zeilen CSS, ganz am Anfang der Datei:

html { overflow-x: clip }
body { overflow-x: clip }

Die standen da aus einem vernünftigen Grund: horizontales Scrollen auf Mobilgeräten verhindern. Der Nebeneffekt steht in keinem Tutorial daneben.

overflow-x auf html oder body macht das Dokument zu einem Scroll-Container. Damit findet position:sticky seinen Bezugspunkt nicht mehr — und tut gar nichts. Lautlos.

Sticky sucht den nächsten scrollenden Vorfahren. Normalerweise ist das der Viewport. Setzt man aber overflow auf dem Wurzelelement, schiebt sich ein Scroll-Container dazwischen, und die Klebe-Mechanik greift ins Leere. Kein Fehler, kein Fallback, kein Hinweis. Das Element scrollt einfach mit.

Die Diagnose, die drei Zeilen dauert

Hätte ich das zuerst geprüft, wäre der Abend anders gelaufen. Der Test durchsucht alle Vorfahren nach overflow-Werten:

let el = stickyEl.parentElement, killer = [];
while (el) {
  const s = getComputedStyle(el);
  if (s.overflow !== 'visible' || s.overflowX !== 'visible' || s.overflowY !== 'visible')
    killer.push(el.tagName + '.' + el.className);
  el = el.parentElement;
}
console.log(killer.length ? killer : 'sticky kann kleben');

Dazu die zweite Prüfung: Ein sticky-Element braucht Klebe-Weg. Der Container muss höher sein als das Element selbst. Ist die Differenz null, klebt nichts — auch ohne overflow-Problem:

const weg = container.offsetHeight - stickyEl.offsetHeight;
// weg <= 0  →  kein Scrollweg, sticky ist wirkungslos

Die Lösung

Horizontales Scrollen verhindert man nicht am Dokument, sondern an der Stelle, wo es entsteht: overflow: clip auf der einzelnen Sektion, die überläuft, plusmin-width: 0 in Grid- und Flex-Kindern. Das ist ohnehin sauberer, weil es die Ursache trifft statt das Symptom zu überdecken.

Und: kein overflow auf dem sticky-Element selbst — das killt es ebenfalls.

Was ich daraus mitnehme

Das ist eine ganze Fehlerklasse: CSS macht eine JS-Mechanik lautlos wirkungslos.Dieselbe Falle gibt es woanders — ein IntersectionObserver auf einemposition:fixed-Element feuert nie, weil das Element immer im Viewport ist. Auch da: kein Fehler, es passiert einfach nichts.

Die Lehre ist unbequem, weil sie dem Instinkt widerspricht: Wenn ein JavaScript-Feature nichts tut und die Konsole schweigt, ist der erste Griff nicht der Debugger — sondern die Frage, ob CSS die Voraussetzungen überhaupt schafft.

Projekt im Kopf?

Wenn Sie an ähnlichen Problemen sitzen — wir bauen Software, die solche Fallen umgeht.

✉ andreklein99@gmx.de