Kto powinien kontrolować zachowanie interfejsu użytkownika? Wybór między HTML, CSS a JavaScript
Ramy decyzyjne określające, kiedy funkcje natywne HTML i CSS takie jak dialog, popover, :has() oraz zapytania kontenera powinny zastąpić JavaScript, a kiedy nie.
Rozwijający interfejs użytkownika instynktownie sięgają po JavaScript. Trzeba otworzyć menu, więc dodaje się stan; trzeba obsłużyć kliknięcie, więc przytwierdza się słuchacz; komponent musi dostosować się do swojej szerokości, więc mierzy się ją; podpowiedź musi znajdować się obok przycisku, więc odczytuje się współrzędne; potrzebny jest efekt przewijania, więc słucha się zdarzeń przewijania i aktualizuje style. Nowoczesne frameworki ułatwiają to wszystko: React ma stan i efekty, Angular ma sygnały, Vue ma referencje i reaktywność, a Svelte sprawia, że aktualizacje są niezwykle zwięzłe. Nic z tego nie jest błędne, a JavaScript odgrywa kluczową rolę w tym, że przeglądarka stała się platformą aplikacji zamiast tylko widżetem do przeglądania dokumentów. Mimo to HTML i CSS ciągle zyskują funkcjonalności, które pokrywają się z tymi zadaniami. Ten artykuł wyjaśnia, dlaczego tak się dzieje, przedstawia rdzenne elementy języka, które teraz obsługują typowe interakcje, oraz podaje praktyczną zasadę decyzyjną dotyczącą tego, który warstwa powinna zarządzać danym elementem.
zachowania.Dlaczego platforma nadal przejmuje zadania aplikacji
Skoro JavaScript może już rozwiązywać niemal każdy problem z interfejsem użytkownika, dlaczego platforma w ogóle się rozwija? Nie dlatego, że JavaScript zawodzi. Dzieje się tak dlatego, że przeglądarka stopniowo uczy się radzić sobie z problemami, które wcześniej każda aplikacja musiała rozwiązywać sama. Za każdym razem, gdy to się dzieje, deweloperzy zyskują kolejną opcję, a bardziej interesujące staje się pytanie, która z nich wiąże się z najmniejszymi kosztami w dłuższej perspektywie.
Każda zmiana konkujuje o następny kadrowanie
Pomaga przypomnieć sobie, co robi przeglądarka po zmianie czegoś na stronie. Uproszczony proces renderowania wygląda tak:
JavaScript
↓
Style calculation
↓
Layout
↓
Paint
↓
Composite
Prawdziwe silniki są o wiele bardziej złożone. Nie każda aktualizacja przechodzi przez wszystkie etapy – niektóre operacje wykonywane są na osobnej wątku, kompozycja może odbywać się oddzielnie, a niektóre animacje unikają wielokrotnego uruchamiania procesów układania lub rysowania. Mimo to jedna z idei tego uproszczonego modelu pozostaje aktualna: przeglądarka ma ograniczony budżet czasowy na wygenerowanie każdego klatki. Wykonywanie skryptów, obsługa danych wejściowych, obliczanie stylów oraz renderowanie muszą się o niego dzielić. Im więcej zadań nakładasz, tym dłużej przeglądarka potrzebuje czasu, zanim będzie mogła zareagować. Aby lepiej zrozumieć, ile kosztuje każdy typ zmiany, zapoznaj się z naszym opracowaniem tematu reflow, repaint i composite.
To łatwo przeoczyć na maszynie rozwojowej z szybkim procesorem, dużą ilością pamięci i dobrym połączeniem. Uruchom tę samą aplikację na starszym telefonie w trybie oszczędzania baterii z kilkoma otwartymi aplikacjami – wtedy małe opóźnienia stają się widoczne: animacja pomija jeden klatka, przewijanie jest powolne, a przycisk reaguje zbyt późno, przez co wydaje się niepracować.
Praca nad wydajnością nie polega tylko na przyspieszaniu istniejącego kodu. Często najlepszą optymalizacją jest nie tworzenie czegoś, czego przeglądarka w ogóle nie wymagała od nas.
Chodzi tu o odpowiedzialność, a nie o konflikt HTML z JavaScript
Latwo byłoby sprowadzić tę dyskusję do hasła:
HTML + CSS = good
JavaScript = bad
To hasło to bzdura. JavaScript rozwiązuje problemy, do których HTML i CSS nigdy nie były projektowane. Edycja współdzielona zależy od logiki aplikacji, a żywe tablice kontrolne wymagają synchronizacji danych. Zaawansowane funkcje takie jak przeciąganie i upuszczanie, reguły domeny, interaktywne wykresy, maszyny stanowe oraz wieloetapowe procesy nie znikną tylko dlatego, że pojawił się nowy selektor.
Sprawy, które warto ponownie rozważyć, są znacznie prostsze. Załóżmy, że musimy pokazywać i ukrywać dodatkowe informacje. Skrypt może to kontrolować:
button.addEventListener('click', () => {
panel.hidden = !panel.hidden;
});
To jest w pełni poprawny kod. HTML natomiast może bezpośrednio wyrazić element służący do pokazywania informacji. Należy zauważyć, że błądowy znak h po tagu zamknięciowym w tym fragmencie to pomyłka i powinien zostać usunięty:
<details>
<summary>Advanced settings</summary>
<p>...</p>
</details>h
Prawdziwa różnica nie leży w liczbie linii. Chodzi o to, kto kontroluje zachowanie. W przypadku skryptu relacja wygląda następująco:
Application owns the behavior
Z użyciem <details> sytuacja wygląda tak:
Browser owns the behavior
Gdy przeglądarka określa zachowanie, to ona również decyduje o szczegółach, które w przeciwnym razie trzeba byłoby pamiętać: obsłudze klawiatury, stanie rozszerzonym udostępnianym technologiom wspomagającym oraz spójnym zachowaniu na różnych stronach. Ta różnica ma znaczenie częściej, niż się na pierwszy rzut oka wydaje.
Dlaczego HTML nie zatrzymało się na akapitach i formularzach
W zasadzie HTML mogło dawno już przestać się rozwijać. Nagłówki, akapity, formularze, przyciski, linki, obrazy oraz ogólne kontenery wystarczały do tworzenia czegokolwiek, a wszystko bardziej złożone pozostawiano skryptom.
Rozważmy jednak, jak wygląda ten świat. Każde aplikacje na nowo projektuje zachowanie każdego elementu – ujawnień informacji, dialogów, okien powyładowych oraz form – przy czym każde z nich robi to nieco inaczej. Biblioteki mają na celu standaryzację tych wzorców, frameworki obejmują te biblioteki, a każdy użytkownik pobiera kolejną implementację czegoś, co przeglądarka mogłaby zrozumieć natywnie.
Gdy dany wzorzec pojawi się wszędzie, wbudowana prymitywa zaczyna się opłacać. Patrząc w ten sposób, nowoczesny HTML nie przekształca się w framework aplikacyjny; stopniowo koduje interakcje, które niemal każda aplikacja musi ciągle odrabiać od nowa.
Okna powyładowe bez stanu aplikacji
Weźmy małą panel ustawień. Wersja komponentu zazwyczaj zaczyna się od prymitywy stanu, takiej jak sygnał w tym przykładzie lub jego odpowiednik w innym frameworku:
const open = signal(false);
Następnie gromadzą się wymagania:
button click
↓
change state
↓
show overlay
↓
handle outside click
↓
handle Escape
↓
manage stacking
Czasami produkt rzeczywiście potrzebuje tego wszystkiego. W prostszych przypadkach HTML oferuje teraz API Popover. Przycisk wskazuje na element z atrybutem popover za pomocą popovertarget, a przeglądarka zajmuje się jego włączaniem i wyłączaniem:
<button popovertarget="settings">
Settings
</button>
<div id="settings" popover>
...
</div>
Dzięki domyślnemu zachowaniu popover (auto) przeglądarka sama zajmuje się łatwym zamknięciem elementu – zamyka go, gdy użytkownik kliknie poza nim lub naciśnie klawisz Escape – oraz renderuje go na najwyższej warstwie, dzięki czemu nie trzeba się męczyć z ustawieniami z-index. Zachowanie, które wcześniej należało wyłącznie do kodu aplikacji, ma teraz natywny punkt wyjścia.
To nie oznacza, że nigdy nie powinno się tworzyć niestandardowego menu rozwijanego. Autocomplete z wyszukiwaniem zdalnym, wirtualizacją, możliwością wyboru wielu elementów oraz niestandardowym obsługiwaniem klawiatury to zupełnie inny problem. Chodzi tylko o to, że prosty element typu popover nie musi już zaczynać swojego istnienia jako część stanu aplikacji.
Dialody rozpoczynające się od rzeczywistego elementu
Modale są dobrym przykładem, ponieważ wydają się proste, dopóki nie zostanie zbudowana ich prawidłowa wersja. Pierwsza wersja polega na jednej flagi:
open = true;
A wtedy pojawiają się prawdziwe pytania:
- Co dzieje się z treścią znajdującą się za modalem?
- Gdzie trafia uwaga klawiatury po jego otwarciu?
- Czy klawiatura może go zamknąć?
- Co się dzieje i gdzie wraca uwaga klawiatury po jego zamknięciu?
- Czy jest renderowany nad wszystkim innym, na najwyższej warstwie?
- Jak powinien wyglądać i jakie funkcje pełnić tło?
Przez lata wiele implementacji zaczynało się od ogólnego elementu <div> i wszystko budowano ręcznie. Dziś HTML oferuje dedykowany element:
<dialog>
...
</dialog>
Warto znać jeden praktyczny szczegół: zachowanie modalne powstaje dzięki otwarciu elementu za pomocą metody showModal(). Ta funkcja umieszcza okno dialogowe na górnym poziomie, sprawia, że reszta strony staje się nieruchoma, umożliwia zamknięcie strzałką Escape oraz dostarcza pseudo-element ::backdrop do stylizacji. Otwarcie go za pomocą show() lub atrybutu open generuje natomiast okno dialogowe niemodalne.
Element natywny nie rozwiązuje wszystkich wymagań związanych z modalem, a rzeczywiste aplikacje mogą nadal dodawać wokół niego logikę. Jednak punkt wyjścia jest inny: nie trzeba już wymyślać koncepcji dialogu, ponieważ przeglądarka już wie, czym on jest. To odzwierciedla wzorzec obowiązujący we współczesnych platformach: prymitywy natywne nie usuwają logiki aplikacji; zmniejszają jedynie ilość tej logiki, którą musimy sami zarządzać.
CSS reagujące na stan DOM
HTML nie jest jedyną warstwą, która przejmuje zadania. CSS może teraz wyrażać relacje, które wcześniej wymagały skryptów.
Rozważmy pole formularza, którego otaczający element powinien mieć określony styl, gdy wprowadzone dane są nieważne. Skrypt mógłby sprawdzić te dane i synchronizować klasę w elemencie nadrzędnym. Współczesny CSS często pozwala bezpośrednio opisać taką relację:
.field:has(input:invalid) {
/* invalid state */
}
:has() zmienia to, co CSS może wiedzieć o dokumencie. Od dawna domagano się jego istnienia jako brakującego selektora rodzica, ale konsekwencje architektoniczne są ważniejsze niż sama nazwa: niektóre synchronizacje stanu w ogóle nie muszą przechodzić przez JavaScript. DOM już zawiera te informacje, a CSS może na nie zareagować. Nie ma klas, które trzeba by zapomnieć zaktualizować, ani słuchaczy do usunięcia.
Komponenty reagujące na swój własny obszar
Projektowanie responsywne tradycyjnie opierało się na zapytaniach mediów:
@media (max-width: 700px) {
...
}
To dobrze działa, gdy liczy się obszar widoku. Komponenty jednak często interesują się czymś innym. Wyobraźmy sobie tę samą kartkę wyświetlaną w dwóch miejscach:
Main content
┌───────────────────────────────┐
│ Card │
└───────────────────────────────┘
Sidebar
┌─────────────┐
│ Card │
└─────────────┘
Przestrzeń widoku jest identyczna w obu przypadkach; przestrzeń dostępna dla karty nie jest. Systemy komponentów wielokrotnego użycia historycznie rozwiązywały ten problem za pomocą kodu pomiarowego: obserwuje się element, odczytuje jego szerokość, dodaje klasę i powtarza to za każdym razem, gdy zmienia się układ.
Zapytania kontenerów pozwalają CSS na odpowiedź na prawdziwe pytanie, mianowicie ile miejsca faktycznie ma dany komponent. Oznacza się przodka jako kontener i definiuje zasady dotyczące jego rozmiaru:
.card-container {
container-type: inline-size;
}
@container (width < 400px) {
.card {
/* compact layout */
}
}
To jest coś więcej niż nowa składnia responsywna; przenosi to odpowiedzialność. Przeglądarka już obliczyła układ, a CSS może wykorzystać ten wynik bez konieczności ponownego pomiaru przez kod aplikacji. Nasze porównanie zapytań kontenerów i zapytań mediowych omawia, w jakich sytuacjach każde z nich pasuje do systemu projektowego.
Pozycjonowanie pokazuje, jak rozwija się platforma
Dodanie podpowiedzi do przycisku wydaje się prostym zadaniem, dopóki rzeczywiście go nie spróbujesz. Gdzie dokładnie znajduje się przycisk? Jaki jest rozmiar podpowiedzi? Czy jest miejsce poniżej, czy powinna pojawić się na górze? Co się dzieje podczas przewijania, zmiany rozmiaru okna przeglądarki lub gdy inna część układu przesuwa przycisk? Prosta podpowiedź szybko zamienia się w problem geometryczny.
Dlatego istnieją dedykowane biblioteki do tworzenia interfejsów pływających, które skutecznie rozwiązują ten naprawdę trudny problem. CSS posiada teraz funkcję Anchor Positioning, która umożliwia deklaratywne pozycjonowanie elementu względem innego elementu. W chwili pisania wsparcie przeglądarek było nadal nierówne, więc sprawdź aktualne dane dotyczące kompatybilności i miej plan awaryjny, jeśli zdecydujesz się na jej użycie.
Najważniejsze nie jest to, czy CSS zastąpi wszystkie biblioteki do pozycjonowania od razu. Chodzi o to, że platforma uznała pozycjonowanie za na tyle powszechne, by zasługiwać na prymitywy niższego poziomu. Taki jest zwykły cykl rozwoju platformy: problemy pojawiają się w aplikacjach, powtarzają się wzorce, pojawiają się biblioteki, a w końcu platforma uczy się na ich podstawie.
Animacja i wartość deklaratywnych API
W przypadku animacji rozmowa na temat wydajności staje się bardziej złożona. Powszechnie przyjętym uproszczeniem jest twierdzenie, że animacje CSS działają na GPU, natomiast animacje JavaScript na CPU. W rzeczywistości sytuacja jest bardziej skomplikowana – animacje CSS nadal wymagają zasobów obliczeniowych, JavaScript może generować bardzo płynne animacje, a koszt wykonywania różnych właściwości CSS znacznie się różni. Animowanie wymiarów może powodować zmiany w układzie elementów, inne właściwości wymagają ponownego narysowania, natomiast transform i opacity zazwyczaj są znacznie bardziej przyjazne dla komponentu odpowiedzialnego za renderowanie.
Zatem istotnym rozróżnieniem nie jest CSS kontra JavaScript, lecz to, ile zasobów wymaga każda zmiana oraz jak dużą swobodę daje się przeglądarce w planowaniu wykonywania tych zadań.
Animacje sterowane przewijaniem ilustrują tę zmianę. Tradycyjnie efekt przewijania wyglądał jak ten pętla:
scroll event
↓
JavaScript calculation
↓
update styles
↓
repeat
Część z tych relacji można teraz bezpośrednio deklarować w silniku renderowania. Zamiast wielokrotnie prosić przeglądarkę o obliczenie wartości, jej zastosowanie i powtórzenie tego procesu, opisuje się w ten sposób, że animacja rozwija się zgodnie z harmonogramem przewijania. To zupełnie inna kategoria API, w której przeglądarka ma większą kontrolę nad mechanizmem i może go optymalizować. Podobnie jak w przypadku pozycjonowania elementów, należy sprawdzić obsługę w przeglądarkach, które są celem.
Usunięcie JavaScripta usuwa coś więcej niż tylko bajty
Zmniejszanie ilości JavaScriptu zwykle omawia się w kontekście kilobajtów, a to ma znaczenie, ponieważ każdy skrypt musi przejść przez ten proces, zanim będzie mógł wykonać jakąkolwiek użyteczną czynność:
downloaded
↓
parsed
↓
compiled
↓
executed
Jednak cykl życia kodu aplikacji trwa długo po pierwszym uruchomieniu. Zachowania implementowane w JavaScriptie zazwyczaj powodują pojawienie się dodatkowych elementów:
state
event listeners
synchronization
cleanup
framework lifecycle
hydration
tests
Załóżmy teraz, że jakaś rdzenna struktura prymitywna pozwala usunąć tę kombinację:
state
+
outside-click listener
+
Escape handler
+
DOM synchronization
Kod, który został usunięty, może ważyć zaledwie kilkaset bajtów, ale zmiana architektury jest rzeczywista. Twoja aplikacja już nie zachowuje tego zachowania. Jest o jedną mniej zmiennych stanu, które mogą stać się przestarzałe, o jednego mniej słuchacza, którego ktoś może zapomnieć usunąć, o jedną mniej relacji DOM, która musi być zsynchronizowana ze stanem, oraz o jedną mniej interakcji do odbudowy od zera, często przy gorszej dostępności niż w wersji rdzennej. Często największą zaletą funkcji platformy nie jest szybkość, lecz odpowiedzialność, którą zwracasz przeglądarce.
Platforma nie ma ostatecznej wersji
Jedno z uporczywych błędnych przekonań pomaga wyjaśnić, dlaczego programiści przeoczają te zmiany: mówienie o „HTML5”, jakby HTML osiągnął wersję piątą i potem praktycznie przestał się rozwijać. Tak nie jest. Nie ma w trakcie opracowywania HTML6 ani HTML7, na które trzeba by czekać. HTML jest utrzymywany jako żywy standard, a specyfikacja ewoluuje nieustannie.
CSS podąża za podobnym modelem. Wielu programistów nauczyło się „CSS3”, ale CSS nie jest już dostarczany jako jeden duży, wersjonowany pakiet. Odrębne moduły rozwijają się we własnym tempie, dlatego spotykamy takie nazwy, bez żadnego ogólnego „CSS6”:
Selectors Level 4
CSS Color Level 5
CSS Cascade Level
Ostatni wpis w tym fragmencie jest skrócony; moduł Cascading and Inheritance ma również własny numer poziomu. Ponieważ moduły są niezależne, struktura sieci może się rozwijać bez czekania na selektory, praca z kolorami przebiega oddzielnie od czcionek, a zapytania do kontenerów są dostarczane bez żadnych wymyślonych kamieni milowych CSS4. W skali sieci internetowej stopniowy rozwój jest jedynym rozsądnym podejściem.
JavaScript również się rozwija
To samo dotyczy tego języka. Ustandaryzowaną specyfikacją jest ECMAScript; JavaScript to nazwa, którą programiści używają dla języka, który silniki implementują zgodnie z tą specyfikacją. Dlatego postępy są opisywane w edycjach ECMAScript, a nie w numerowanych wersjach „JavaScript”, i ciągle pojawiają się nowe funkcje.
Zatem platforma nie jest zamarznięta dzięki tej kombinacji:
HTML5
+
CSS3
+
JavaScript
Wszystkie trzy warstwy nadal się rozwijają. ECMAScript zyskuje lepsze prymitywy, CSS obejmuje coraz więcej aspektów układu, stylizacji i relacji interakcji, a HTML uzyskuje semantykę oraz zachowania, które wcześniej aplikacje musiały implementować samodzielnie. Granica pomiędzy nimi zmienia się z upływem czasu, dlatego wiedza zdobyta o tej platformie lata temu wymaga regularnego odświeżania.
Wiedza o platformie przetrwa wybór frameworka
Ty te funkcje istnieją niezależnie od żadnego frameworka. Zapytania kontenerowe działają tak samo, niezależnie od tego, czy komponent został stworzony za pomocą Angular, React, Vue, Svelte czy zwykłego kodu DOM. Funkcja :has() nie przejmuje się sposobem utworzenia DOM-u. Natywny element <dialog> pozostaje dialogiem w ramach frameworka, a element popover to funkcja przeglądarki, niezależnie od tego, co go wyświetliło.
To sprawia, że wiedza o platformie jest niezwykle trwała. Umiejętności związane z konkretnym frameworkiem przenoszą się na projekty, które go używają; umiejętności związane z platformą przenoszą się niemal na każdy projekt internetowy. Nie oznacza to jednak umniejszania znaczenia frameworków, które rozwiązują poważne problemy architektoniczne oraz związane z doświadczeniem programistów. Zrozumienie warstwy leżącej u ich podstaw daje dodatkową opcję w sytuacjach, gdy abstrakcja jest niepotrzebna, a co równie ważne, pokazuje, jak abstrakcja działa na Twoją korzyść. Jeśli szukasz katalogu konkretnych zamienników, nasz przegląd naturalnych funkcji HTML, które zastępują powszechne biblioteki interfejsu użytkownika omawia je pojedynczo.
Kiedy naturalne rozwiązanie jest złym wyborem
Zanim się poniesiemy, mała uwaga: umiejętność tworzenia czegoś w HTML lub CSS nie oznacza, że powinniśmy to robić. Weźmy pod uwagę menu spadkowe, które wymaga:
- asynchronicznego wyszukiwania;
- setek wirtualizowanych opcji;
- mogłości wyboru wielu elementów jednocześnie;
- zaawansowanej nawigacji klawiaturą;
- szytych na miarę reguł filtrowania;
- opcji ładowanych z serwera;
- wyborów synchronizowanych ze stanem aplikacji.
Istnienie prostego elementu typu popover w niczym nie pomaga w rozwiązaniu tego problemu, a próby dostosowania projektu tak, by uniknąć JavaScript, pogorszą architekturę, zamiast ją ulepszyć.
CSS również zasługuje na taki realizm. Wykonuje rzeczywistą pracę: układ i rysowanie nie są bezkosztowe, duży DOM nadal wymaga dużo zasobów, a źle dobrane animacje mogą nadal działać słabo.
Zatem celem nie jest używanie HTML i CSS za każdym razem, gdy jest to technicznie możliwe. Znacznie bardziej przydatną zasadą jest następująca:
Niech będzie to najniższy poziom, który w sposób skuteczny rozwiązuje aktualne wymaganie.
Czasami jest to HTML, czasami CSS, a czasami JavaScript – większość dobrych aplikacji łączy wszystkie trzy. Szybkim sposobem na zastosowanie tej zasady jest zadanie sobie kolejnych pytań: czy element lub atrybut semantyczny wyraża to zachowanie? Jeśli nie, czy CSS może je wyprowadzić ze stanu już istniejącego w DOM? Tylko wtedy, gdy obie odpowiedzi są negatywne lub gdy wymaganie wykracza poza możliwości wersji natywnej, zachowanie powinno zostać przeniesione do kodu aplikacji.
Najprostsze przypadki wymagają najprostszych rozwiązań
Złożone funkcje będą nadal wymagać JavaScriptu, i to jest w porządku. Ale wokół tych funkcji większość aplikacji zawiera setki drobnych interakcji: sekcje, które można rozszerzyć, lekkie nakładki, karty dostosowujące się do swojego miejsca, stylizację nieprawidłowych pól, proste przejścia, jeden element śledzący inny. Gdy jakikolwiek element natywny, atrybut lub krótka reguła CSS obejmuje jedną z tych interakcji, aplikacja może w ogóle nie musieć nią zarządzać.
To, że JavaScript potrafi rozwiązać ten problem, nie jest niczym wyjątkowym; potrafi rozwiązać niemal wszystko. Cenne jest posiadanie dodatkowej opcji.
Zaktualizowanie modelu mentalnego
Wielu programistów poznało świat webu dzięki uporządkowanemu podziałowi zadań:
HTML → structure
CSS → presentation
JavaScript → behavior
Jest to dobry model do nauki, ale dzisiejsze przeglądarki są bardziej zaawansowane, niż sugeruje. HTML teraz obejmuje zachowania, CSS reaguje na złożone relacje w DOM i układzie, a sam język ciągle zyskuje nowe funkcjonalności. Platforma nieustannie się zmieniała, nawet gdy nawyki pozostawały bez zmian.
Wiele z tych nawyków było traktowanych jako niepodważalne fakty: nakładki wymagają stanu aplikacji, pozycjonowanie potrzebuje skryptów, komponenty mogą reagować tylko na punkty przerwania w widoku, a animacje przewijania wymagają słuchaczy. Wszystko to mogło kiedyś być prawdą. Niektóre już nie zawsze są prawdziwe.
Dlatego nadążanie za rozwojem HTML i CSS nie polega na gromadzeniu sprytnych składni. Nowa funkcjonalność przeglądarki może wyeliminować konieczność użycia zależności, słuchacza zdarzeń, fragmentu stanu, obiektu ResizeObserver lub jakiegokolwiek kodu, którego jedynym zadaniem było odtwarzanie czegoś, co przeglądarka teraz rozumie w sposób natywny.
Główne wnioski
- Brauzery coraz częściej przejmują zachowania, które aplikacje wielokrotnie implementowały na nowo; każde takie przejęcie oferuje opcję o niższych kosztach.
- Czas obsługi ramki stanowi wspólny budżet, więc unikanie niepotrzebnych działań jest często najskuteczniejszą optymalizacją.
- Pomyśl w kategoriach odpowiedzialności:
<details>, API Popover oraz<dialog>z metodąshowModal()pozwalają brauserowi przejąć kontrolę nad zachowaniami, w tym nad szczegółami dotyczącymi dostępności, które w przeciwnym razie musiałbyś sam utrzymywać. :has()oraz zapytania kontenerów umożliwiają CSS-owi reagowanie na stan DOM i rozmiar komponentów bez konieczności synchronizacji czy kodu do pomiarów.- Pozycjonowanie elementów łącznikowych oraz animacje sterowane przewijaniem zmierzają w tym samym kierunku, ale przed ich wykorzystaniem sprawdź aktualne wsparcie w brauserach.
- Rozwiązania natywne nie są automatycznie lepsze: złożone elementy interfejsu nadal powinny być realizowane w JavaScript, a praca z CSS również ma swoje rzeczywiste koszty.
Literatura pokrewna
- Struktura, styl, behawior: Jak HTML, CSS i JavaScript dzielą pracę — Przystępny dla początkujących przewodnik pokazujący, jakie zadania pełnią HTML, CSS i JavaScript na stronie internetowej, jak ze sobą współpracują oraz jakie powszechne błędy zamazują granice między nimi.
- Natywne API browserów zastępujące popularne pakiety npm w 2026 roku — Wyjaśnia, jak natywne funkcje JavaScript i CSS, takie jak Signals, operator pipeline, Temporal oraz Anchor Positioning, zastępują powszechne pakiety npm.
- Przycisk feedbacku w postaci gestu-palcem-w-górę z animacją bez żadnej biblioteki animacji — Stwórz widget feedbacku w postaci gestu-palcem-w-górę i gestu-palcem-w-dół z animacją typu keyframe, stanami aktywnymi oraz wywołaniem fetch, używając jedynie HTML, CSS i kilku linijek JavaScript.
- Typed CSS attr(): Wprowadzanie wartości atrybutów HTML bezpośrednio do stylów — Dowiedz się, jak typed CSS attr() przekształca atrybuty danych w wartości dotyczące długości, kolorów i czasu, dlaczego jego alternatywy zawodzą, dlaczego url() jest zablokowany oraz jak bezpiecznie go wdrożyć już dziś.