Bezpieczne używanie nowoczesnego CSS: kotwice, pasy siatki, zakres oraz funkcja light-dark()
Dowiedz się, jak pozycjonowanie kotwic, elementy typu popover, układy w rzędach i kolumnach, przejścia między widokami, funkcje light-dark() oraz @scope zastępują biblioteki JavaScript, a także jak wdrożyć każdą z nich z rozwiązaniami awaryjnymi.
Źródła: CSS Can Already Do This. Developers Are Still Scared to Ship It.
Wiele pakietów front-endowych nadal zawiera biblioteki, których jedynym zadaniem jest określenie miejsca umieszczenia podpowiedzi, takie jak Floating UI lub jego poprzednik Popper, a także inne biblioteki służące do układania karty w siatce w stylu Pinterest. Przeglądarki mogą teraz wykonywać obie te funkcje natywnie, oprócz kilku innych zadań, które wcześniej wymagały JavaScriptu, jednak niewiele zespołów usunęło te zależności. Ten przewodnik omawia pięć funkcji CSS, które mogą zastąpić powszechny kod biblioteczny, wyjaśnia, jak każda z nich działa, oraz pokazuje, jak je wdrożyć bez ryzyka nierównego wsparcia przez przeglądarki.
Jednocześnie poszukiwane i unikane
Ankiety wśród programistów dobrze oddają tę sytuację. W wynikach raportu State of CSS z 2026 roku pozycjonowanie elementów anchor zostało wybrane jako nowa funkcja, która najbardziej ich ekscytuje, zastępując :has(), który jest obecnie tak powszechnie używany, że nie wydaje się już czymś nowym. Ta sama ankieta pytała również, jakie funkcje ludzie celowo unikają ze względu na brak pewności co do ich obsługi w przeglądarkach, a pozycjonowanie elementów anchor znalazło się również na szczycie tej listy. Zamykanie tego rozdźwięku polega głównie na wdrażaniu funkcji za pośrednictwem zapytań o ich dostępność. Dane z ankiet oraz statusy obsługi szybko się zmieniają, więc należy traktować je jako chwilowy obraz sytuacji i sprawdzać aktualne dane dotyczące kompatybilności przed poleganiem na jakiejkolwiek funkcji.
Pozycjonowanie elementów anchor zamiast biblioteki tooltip
Podejście oparte na JavaScriptzie przyczepia element pływający do swojego wyzwalacza poprzez pomiar obu elementów i ponowny obliczanie ich pozycji przy każdym przewijaniu lub zmianie rozmiaru. Pozycjonowanie anchor przenosi tę relację do CSS.
Poniżej wyzwalacz określa nazwę, a menu pozycjonuje się względem niej. Ostatnia deklaracja zastępuje funkcję wykrywania kolizji w bibliotece:
.trigger {
anchor-name: --menu-trigger;
}
.menu {
position: fixed;
position-anchor: --menu-trigger;
top: anchor(bottom);
left: anchor(center);
position-try-fallbacks: flip-block, flip-inline;
}
Każdy element pełni określoną rolę:
anchor-namenadaje elementowi identyfikator w postaci kreskowanych znaków, który umożliwia pozycjonowanie innego elementu względem niego.position-anchorinformuje element pozycjonowany, który nazwany anchor ma być śledzony.anchor()odczytuje współrzędną, taką jak dolny brzeg lub poziome centrum anchora, bezpośrednio z tego elementu.
position-try-fallbacks wymienia alternatywy, które przeglądarka wypróbuje, gdy element miałby przekroczyć obszar widoku; flip-block i flip-inline odzwierciedlają go pionowo lub poziomo.Łączenie z atrybutem popover
Atrybut popover, który również jest dostępny, ale często ignorowany, określa zachowanie rozwijanej listy: przycisk z atrybutem popovertarget włącza element o odpowiadającym mu id, nie jest potrzebny żaden skrypt:
<button popovertarget="menu">Options</button>
<div id="menu" popover anchor-name-target="--menu-trigger">...</div>
Bądź ostrożny z tym fragmentem kodu: anchor-name-target nie jest standardowym atrybutem HTML, więc nie kopiuj go w takiej postaci. Połącz element popover z jego punktem przyczepienia za pomocą CSS, używając anchor-name i position-anchor, jak pokazano powyżej. Niektóre przeglądarki traktują również przycisk uruchamiający jako domyślny punkt przyczepienia dla elementu popover otwartego za pomocą popovertarget; sprawdź aktualną dokumentację, zanim na to polegasz.
Tego, co popover oferuje w sposób wbudowany, jest renderowanie na najwyższej warstwie, dzięki czemu menu pojawia się nad wszystkim bez konfliktów związanych z wartością z-index w całym aplikacji, łatwe zamknięcie po kliknięciu poza elementem lub naciśnięciu klawisza Escape, oraz rozsądne zarządzanie fokusem podczas otwierania i zamykania. Nie blokuje on fokusa w taki sposób jak modala dialog, więc dla treści naprawdę modalnych użyj zamiast tego dialog.
Aby uzyskać szerszy przegląd API przeglądarki, które sprawiają, że pakiety npm stają się niepotrzebne, zapoznaj się z naszym przeglądem natywnych API przeglądarki zastępujących popularne pakiety.
Natywne układy typu masonry z pasami siatki
Odkąd CSS Grid pojawił się w przeglądarkach w 2017 roku, układy typu masonry nadal wymagały JavaScripta, dlatego biblioteka Masonry oraz podobne pakiety były instalowane miliony razy. To się zmienia. Poniższe zasady stanowią kompletny schemat układu:
.gallery {
display: grid-lanes;
gap: 10px;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
Każda karta jest umieszczana w kolumnie, która obecnie ma najwięcej miejsca, dzięki czemu elementy układają się ciasno bez luźnych przerw. Nie ma tu ResizeObserver, ani automatycznego ponownego równoważenia kolumn po zakończeniu ładowania obrazu. Syntakta ta była przedmiotem lat debat pomiędzy producentami przeglądarek, przy czym Apple i Google opowiadali się za różnymi podejściami, zanim display: grid-lanes stało się kompromisem.
Zgodnie z informacjami o wsparciu z czasu pisania tego tekstu, Safari wprowadził tę funkcję jako pierwszy, podczas gdy Chrome i Firefox trzymały ją w trybie flag. To tłumaczy, dlaczego tak niewiele stron jej używa oraz dlaczego warto o nią teraz pisać. Umieść grid-lanes w bloku @supports (display: grid-lanes), a jako alternatywę użyj zwykłej siatki.
Przejścia widoku – z wielką ostrożnością
Tutaj ostrożność jest uzasadniona. Istnieje wsparcie dla API View Transition, ale jest ono nierówne, co sprawia, że znajduje się ono obok pozycjonowania anchor wśród najczęściej unikanych funkcji, a ta rezerwa jest uzasadniona.
Bazowy mechanizm jest prosty: otocz aktualizację DOM funkcją document.startViewTransition(), a przeglądarka zrobi zdjęcia stanu starego i nowego oraz przejedzie pomiędzy nimi stopniowo, bez potrzeby użycia biblioteki animacji ani ręcznego rejestrowania stanów przed i po. Pseudo-elementy poniżej kontrolują ten domyślny przejście, skracając go tutaj do 0,3 sekundy:
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.3s;
}
Dziś można mu ufać jako narzędziu do postępowego ulepszania funkcjonalności. Przeglądarka, która nie ma danej API, po prostu przechodzi do nowego stanu, więc nic się nie psuje. Dzięki temu nadaje się do użycia w sytuacjach niewymagających natychmiastowej reakcji, takich jak zmiana strony lub przestawienie elementów na liście. W skrypcie należy sprawdzić, czy funkcja document.startViewTransition istnieje przed jej wywołaniem, a w przeciwnym razie zastosować aktualizację bezpośrednio.
Jedna paleta dla trybu jasnego i ciemnego za pomocą light-dark()
Typowy tryb ciemny wykorzystuje dwa pełne systemy kolorów – jeden w ramach zapytania mediowego prefers-color-scheme: dark, a drugi poza nim. Funkcja light-dark() łączy każdą parę kolorów w jedną deklarację.
Poniższy fragment najpierw ustawia dokument na oba tryby za pomocą atrybutu color-scheme, który jest wymagany przez light-dark() do poprawnego działania, a następnie podaje dla każdej właściwości wartości jasne i ciemne obok siebie:
:root {
color-scheme: light dark;
}
body {
background: light-dark(#ffffff, #1a1a1a);
color: light-dark(#111111, #eeeeee);
}
Gdy tylko ustawiono color-scheme, każde wywołanie light-dark() przyjmuje odpowiednią wartość zgodnie z ustawieniami systemowymi użytkownika, dzięki czemu nie musimy już powtarzać zapytań mediów w całym pliku stylów. Aby zapoznać się z innymi sposobami implementacji tematyzacji, sprawdź nasze porównanie technik trybu ciemnego.
Zawieranie stylów za pomocą @scope
Głównym powodem stosowania CSS-in-JS lub ścisłego systemu nazewniczego BEM jest wyciek specyficzności: reguła .card przeznaczona dla jednego komponentu może również stylizować niepowiązany element .card znajdujący się głębiej w kodzie innej osoby. @scope naturalnie ogranicza stylizacje do określonego poddrzewa.
W poniższej regule style akapitów są stosowane wewnątrz .card, ale przestają działać przy pierwszym elemencie typu .card-nested:
@scope (.card) to (.card-nested) {
p {
color: var(--card-text);
}
}
Pierwszy selektor określa korzeń zakresu. Klauzula to ustala dolną granicę, tworząc przestrzeń, w której zagnieżdżony komponent może zarządzać własnymi stylami zamiast odziedziczać je od rodzica. Wszystko to nie wymaga kroku budowania, generowanych nazw klas ani JavaScripta w czasie wykonywania. Nie zastąpi ono wszystkiego, co robi CSS-in-JS, takiego jak stylówki obliczane na podstawie właściwości komponentu, ale eliminuje jeden z głównych powodów, dla których zespoły go przyjmują.
Brauzer to ekosystem
Wszystkie pięć tych funkcji jest dostępnych przynajmniej w jednym rzeczywistym przeglądarce obecnie, a żadna z nich nie zależy od Reacta, pluginu do pakietowania ani wersji frameworku. Różnica pomiędzy tym, co potrafi CSS, a tym, co wdrażają zespoły, wynika głównie ze zwyczaju: przez dekadę rozwiązywanie problemów z interfejsem oznaczało instalację pakietu.
Wybierz jedno miejsce w swoim projekcie, gdzie użyto rozwiązania opartego na JavaScriptie do radzenia sobie z jednym z tych problemów, zastąp je instrukcją @supports i sprawdź, co przestanie działać. Zazwyczaj jest to mniej, niż się spodziewasz.
Częste pytania
Czy pozycjonowanie elementów typu anchor jest gotowe do użycia w produkcji?
Już jest dostępne i można je stosować od dziś po sprawdzeniu za pomocą @supports. To, że znajduje się na liście najczęściej unikanych rozwiązań, wynika z ostrożności związanej z nierównomiernym wdrożeniem w różnych przeglądarkach, a nie z błędnego funkcjonowania. Odpowiednim podejściem jest połączenie go z rozwiązaniem awaryjnym, takim jak prostsze statyczne pozycjonowanie.
Czy nadal potrzebna jest biblioteka do układania elementów w szachownicę?
W nowym projekcie prawdopodobnie nie, pod warunkiem że display: grid-lanes znajduje się w bloku @supports z zwykłą siatką jako rozwiązaniem awaryjnym. W istniejącym projekcie produkcyjnym z rygorystyczną matrycą kompatybilności przeglądarek warto jeszcze przez jakiś czas zachować tę bibliotekę.
Którą funkcję należy wprowadzić najpierw?
light-dark(). Jest to zmiana jedną linią kodu dla każdej wartości koloru, miała najszersze wsparcie spośród pięciu w momencie pisania tego tekstu i eliminuje blok powtarzającego się kodu CSS dla trybu ciemnego bez żadnego ryzyka dla układu.
Główne wnioski
- Traktuj każdą funkcję jako ulepszenie stopniowe: zachowanie natywne tam, gdzie jest obsługiwane, lub istniejące rozwiązanie w innym miejscu.
- Pozycjonowanie elementów przy użyciu anchor w połączeniu z
popoverpokrywa większość potrzeb dotyczących podpowiedzi i menu rozwijanych, ale należy skonfigurować anchor w CSS, a gdy potrzebny jest prawdziwy moduł dialogowy, użyćdialog. grid-lanesoraz przejścia między widokami są najmniej udoskonalone; należy je włączać za pomocą@supportslub sprawdzenia w czasie wykonywania.light-dark()oraz@scopenatychmiast usuwają kod i niosą najmniejsze ryzyko.
Pozycjonowane materiały
- Natywne API przeglądarek zastępujące popularne pakiety npm w 2026 — Wyjaśnia, w jaki sposób natywne funkcje JavaScript i CSS, takie jak Signals, operator pipeline, Temporal oraz Anchor Positioning, zastępują powszechne pakiety npm.
- Dziewięć technik trybu ciemnego porównanych, od trików z filtrami po pliki cookie serwera — Porównuje dziewięć sposobów dodania trybu ciemnego do aplikacji internetowej, od odwracania filtrów po tokeny, funkcję light-dark() oraz pliki cookie serwera, i pokazuje, jakie błędy potajemnie wprowadza każda z tych metod.