Wzorzec 26
Scroll, który zna granice
Gest przewijania, który dojedzie do końca zagnieżdżonej listy, domyślnie płynie dalej i rusza stroną pod spodem. W dialogu, menu i czacie ten łańcuch odbiera użytkownikowi kontekst, a blokowanie go skryptem dopisującym overflow: hidden do body psuje pozycję przewinięcia. Jedna deklaracja overscroll-behavior przecina łańcuch tam, gdzie panel się kończy.
Koniec listy to koniec gestu
Przewiń obie listy do samego końca i nie przerywaj gestu. Lewa przekazuje resztę ruchu stronie, która ucieka w dół. Prawa ma overscroll-behavior: contain, więc gest zatrzymuje się na krawędzi panelu.
✕ Łańcuch przewijania
- Skeleton dzieli layout z treścią, którą zapowiada.
- Slot na błąd czeka pod polem od pierwszego renderu.
- Przycisk trzyma szerokość podczas wysyłki.
- Kolumny siatki wynikają z minmax, nie z breakpointów.
- Subgrid wyrównuje stopki sąsiednich kart.
- Dialog otwiera showModal, nie div z z-index.
- Toast żyje w swojej warstwie nad treścią.
- Debounce ogranicza zapytania wyszukiwarki.
- Obraz deklaruje wymiary, zanim przyjdzie plik.
- Panel zakładek trzyma wysokość najwyższej treści.
- Akordeon animuje grid-template-rows, nie height.
- Cyfry tabelaryczne trzymają szerokość licznika.
- Karuzela jedzie na scroll-snap bez biblioteki.
- Walidację formatu robi :user-invalid, nie skrypt.
- Dymek trzyma się kotwicy czystym CSS.
✓ overscroll-behavior: contain
- Skeleton dzieli layout z treścią, którą zapowiada.
- Slot na błąd czeka pod polem od pierwszego renderu.
- Przycisk trzyma szerokość podczas wysyłki.
- Kolumny siatki wynikają z minmax, nie z breakpointów.
- Subgrid wyrównuje stopki sąsiednich kart.
- Dialog otwiera showModal, nie div z z-index.
- Toast żyje w swojej warstwie nad treścią.
- Debounce ogranicza zapytania wyszukiwarki.
- Obraz deklaruje wymiary, zanim przyjdzie plik.
- Panel zakładek trzyma wysokość najwyższej treści.
- Akordeon animuje grid-template-rows, nie height.
- Cyfry tabelaryczne trzymają szerokość licznika.
- Karuzela jedzie na scroll-snap bez biblioteki.
- Walidację formatu robi :user-invalid, nie skrypt.
- Dymek trzyma się kotwicy czystym CSS.
Ta sama deklaracja chroni dialogi, menu i czaty. Strona pod spodem nie potrzebuje skryptu dopisującego overflow: hidden do body, bo łańcuch jest przecięty u źródła.
Reguły
- Każdy przewijany panel w warstwie — dialog, menu, szuflada, czat — ma overscroll-behavior: contain.
- Dojechanie do krawędzi listy zatrzymuje gest, nie przekazuje go stronie pod spodem.
- contain zamiast none, dopóki pull-to-refresh i efekty krawędzi mają działać w samym panelu.
- Strona pod modalem nie potrzebuje skryptu blokującego body, wystarczy przecięty łańcuch przewijania.
Wzorzec w kodzie
dialog,
[popover],
.panel-czatu,
.menu-przewijane {
overscroll-behavior: contain;
}function openModal() {
document.body.style.overflow = "hidden";
}
function closeModal() {
document.body.style.overflow = "";
}Prompt dla Claude
Wklej do Claude Code w swoim projekcie. Prompt niesie komplet reguł tej lekcji i ogólne zasady Liquid Design, więc implementacja trafia w metodologię bez tłumaczenia jej od zera.
Zaimplementuj w moim projekcie wzorzec „Scroll, który zna granice" z metodologii Liquid Design. overscroll-behavior: contain zatrzymuje przewijanie na krawędzi panelu, więc koniec listy w dialogu nie przewija strony pod spodem. Wymagania: - Każdy przewijany panel w warstwie — dialog, menu, szuflada, czat — ma overscroll-behavior: contain. - Dojechanie do krawędzi listy zatrzymuje gest, nie przekazuje go stronie pod spodem. - contain zamiast none, dopóki pull-to-refresh i efekty krawędzi mają działać w samym panelu. - Strona pod modalem nie potrzebuje skryptu blokującego body, wystarczy przecięty łańcuch przewijania. Ogólne reguły Liquid Design, których implementacja nie może złamać: - HTML jest semantyczny: button, a, nav, form, label, dialog, details, ul, dl i nagłówki h1-h6 zamiast div z onClick i ARIA dopisywanym ręcznie. div i span służą wyłącznie do layoutu, nigdy do interakcji ani struktury treści. - Layout jest umową: treść, która dociera później, ma miejsce zarezerwowane od pierwszego renderu. Nic nie skacze. - Stany ładowania, pustki, błędu i treści dzielą jeden layout. - Typografia pochodzi z nazwanej, płynnej skali opartej o clamp(), nie z gołych rozmiarów. - Animacje dotyczą wyłącznie transform i opacity, a prefers-reduced-motion redukuje je do natychmiastowej zmiany stanu. - Breakpoint jest ostatecznością: najpierw container queries i repeat(auto-fit, minmax(...)). Sposób pracy — zanim napiszesz pierwszą linię kodu: - Sprawdź w plikach projektu, jak stylowane są komponenty (Tailwind, CSS Modules, vanilla-extract, styled-components, Sass, czysty CSS...) i pisz wyłącznie w tej konwencji. Niczego nie zakładaj z góry i nie dodawaj nowych zależności. - Wymagania powyżej opisują właściwości CSS i atrybuty HTML, nie klasy narzędziowe. Przełóż je na system stylowania zastany w projekcie. - Sprawdź framework komponentów, wersję i konwencje nazewnictwa, zamiast zakładać konkretny stack. - Używaj istniejących tokenów projektu (kolory, typografia, odstępy). Jeśli czegoś brakuje, zaproponuj minimalne uzupełnienie w duchu istniejącego kodu, nie osobny system. - Nie używaj klas, tokenów ani API, których nie znalazłeś w tym projekcie. Pełny opis z żywym demo i kodem: https://liquid-design.website/wzorce/scroll-bez-lancucha