Od 66% do 185px: Jak przeglądarki obliczają wartości CSS przed układem
Prześledź wartość CSS na wszystkich etapach: deklarowanej, kaskadowej, określonej, obliczonej, użytej i faktycznej, i zobacz, dlaczego jednostki względne oraz trik konwersji rem zachowują się w taki sposób.
Wygranie w hierarchii reguł nie oznacza końca procesu przetwarzania deklaracji CSS. Nawet jeśli width: 50% pokonuje wszystkie konkurencyjne reguły, przeglądarka nadal nie może przekazać wartości 50% swojemu silnikowi układu; musi najpierw określić, co dokładnie stanowi te pięćdziesiąt procent w danym kontekście, i przekształcić to wartość konkretną. Niniejszy przewodnik śledzi daną wartość na każdym etapie przetwarzania określonym w specyfikacji, pokazuje, gdzie rozstrzygane są jednostki względne takie jak rem oraz procenty, i wyjaśnia popularną sztuczkę konwersji px na rem wraz z jej ograniczeniami dotyczącymi dostępności.
Wartość, którą wpisujesz, to nie ta, która zostanie użyta
Znaczna część tego, co wpisujesz do pliku stylów, to relacja, a nie bezpośrednia miara. Szerokość może stanowić udział w przestrzeni rodzica:
width: 50%;
lub, w innej regule, inny udział:
width: 66%;
Rozmiar czcionki może być odnoszony do rozmiaru czcionki elementu korzeniowego:
font-size: 2rem;
a wysokość może stanowić ułamek rozdzielczości ekranu:
height: 50vh;
Żadna z tych wartości nie jest wyrażona w pikselach, a mimo to układ wymaga rzeczywistych wymiarów, aby określić, gdzie ma się znajdować każda ramka. Aby przezwyciężyć tę różnicę, każda wartość przechodzi przez określoną serię etapów:
Declared Value
↓
Cascaded Value
↓
Specified Value
↓
Computed Value
↓
Used Value
↓
Actual Value
Początkowo nazwy te wydają się żargonem, ale każdy etap odpowiada na konkretne pytanie. Poniższe sekcje omawiają je po kolei, używając jednego przykładu.
Wartość zadeklarowana: to, o co prosiłeś
Wartość zadeklarowana to po prostu wartość, jaka występuje w deklaracji. Przy założeniu:
width: 66%;
zadeklarowana wartość dla width to 66%. Jest to jedynie prośba, nic więcej. Element może mieć kilka zadeklarowanych wartości dla tej samej właściwości pochodzących z różnych reguł, więc pierwszym krokiem jest wybór jednej z nich.
Wartość kaskadowa: deklaracja, która przetrwała
Załóżmy dwa reguły, które obie ustalają szerokość akapitu. Jedna z nich używa selektora typu:
p {
width: 100px;
}
a druga używa klasy:
.amazing {
width: 66%;
}
Jeśli akapit ma klasę amazing, obie reguły do niego pasują. Selektor klasy jest bardziej specyficzny niż prosty selektor elementu, więc kaskada wybiera:
width: 66%;
To przetrwałe 66% to wartość kaskadowa. Ten etap jest ważny, ponieważ każdy kolejny krok działa tylko na jedynego zwycięzcę; przegrane deklaracje są odrzucane, zamiast być przetwarzane.
Wartość określona: upewnienie się, że każda właściwość ma taką wartość
Wartość określona jest wynikiem zapewnienia, że każda właściwość na każdym elemencie ma jakąś wartość. Reguła jest prosta:
- jeśli istnieje wartość kaskadowa, staje się ona wartością określoną;
W naszym przykładzie występuje wartość kaskadowa, więc określona wartość pozostaje 66%, a ten etap wydaje się nic nie robić. Staje się to ważne dla właściwości, o których w ogóle nie wspomniano żadnej reguły – właśnie tutaj przydaje się dziedziczenie. Ten mechanizm jest omówiony w ostatniej sekcji.
Wartość obliczona: rozwiązywanie problemów, które można rozwiązać bez układu strony
Wartość obliczona to moment, w którym przeglądarka rozwiązuje wszystko, co może ustalić bez faktycznego układu strony. Wielkości względne oparte na rozmiarach czcionek stają się wielkościami bezwzględnymi, a słowa kluczowe są przekształcane w konkretne wartości. Rozważmy:
font-size: 2rem;
Jednostka tutaj:
2rem
Opisuje to związek typu „dwukrotność rozmiaru czcionki elementu korzeniowego”, a nie stałą wartość. Na etapie obliczeń przeglądarka sprawdza rozmiar czcionki elementu korzeniowego i przekształca tę wartość na piksele. Jeśli rozmiar czcionki elementu korzeniowego wynosi 16px, to obliczony rozmiar czcionki wyniesie 32px.
Jednym z powodów, dla których ta konwersja następuje na tym etapie, jest dziedziczenie: wartość obliczona jest tą, którą odziedziczają elementy potomne, więc otrzymują one konkretną długość zamiast relacji, którą musiałyby ponownie interpretować w swoim własnym kontekście. Dlatego też nawarstwione rozmiary czcionki typu em działają w taki sposób, a procentowe wysokości wierszy czasami powodują ciasne ułożenie tekstu w elementach z większymi czcionkami.
Część wartości nie może zostać jeszcze rozstrzygnięta. Szerokość w procentach zależy od rozmiaru bloku, który zawiera ją, a jego rozmiar jest znany dopiero po ustaleniu układu, dlatego 66% pozostaje zazwyczaj wartością procentową w obliczonej wartości i zostaje ostatecznie ustalony dopiero na następnym etapie.
Użyta wartość: informacje o układzie
Użyta wartość to wynik po uzyskaniu informacji o układzie. Wróć do:
width: 66%;
Sama w sobie wartość procentowa jest niekompletna, dopóki nie wiadomo, jaki jest jej udział w całości. W przypadku width tym odniesieniem jest szerokość bloku, który ją zawiera. Załóżmy, że sekcja nadrzędna jest:
280px
szeroka. Przeglądarka może teraz obliczyć:
66% of 280px
co daje następujący wynik:
184.8px
To wartość faktycznie użyta. To jest punkt zwrotny w procesie: przeglądarka nie przechowuje już procentu, który wpisałeś, lecz rzeczywistą wartość wynikającą z aktualnej geometrii strony. To samo dotyczy wartości takich jak szerokość auto, które mają sens dopiero po znaniu otaczającego je układu. Jeśli zapytasz element w JavaScript za pomocą getComputedStyle, wiele właściwości zależnych od układu, takich jak width, zwraca tę ustaloną wartość w pikselach, a nie procent, co jest przydatnym sposobem na obserwację tego procesu w praktyce.
Rzeczywista wartość: dostosowanie do ograniczeń urządzenia
Ostatni etap uwzględnia ograniczenia środowiska renderowania. Urządzenie nie musi koniecznie rysować wartości z dowolną dokładnością, więc użyta wartość może wyglądać mniej więcej tak:
184.8px
może ostatecznie zostać wyświetlona mniej więcej w ten sposób:
185px
To skorygowane wartość jest rzeczywistą wartością. W praktyce nowoczesne przeglądarki układają elementy z precyzją sub-pikselową i stosują zaokrąglanie lub dopasowanie w różnych momentach, w zależności od właściwości i silnika, więc nie należy polegać na konkretnej zasadzie zaokrąglania. Kluczowa koncepcja polega po prostu na tym, że może dojść do końcowej korekty pomiędzy obliczoną wartością a tą, która trafia na ekran.
Gdy spojrzymy szerzej, cała ta sekwencja sprowadza się do jednego zdania: to, co zapisujemy w pliku stylów, nie musi koniecznie być tym, co ostatecznie wykorzysta przeglądarka, a pomiędzy nimi istnieją dobrze zdefiniowane etapy.
Dlaczego jednostki względne stają się mniej tajemnicze
To model wyjaśnia, dlaczego jednostki względne są tak cenne, a jednocześnie tak często błędnie rozumiane. Jednostki takie jak te:
%
rem
em
vh
vw
umożliwia opisywanie rozmiarów w odniesieniu do czegoś innego (kontenera, rozmiaru czcionki głównej, własnego rozmiaru czcionki elementu, obszaru widoku) zamiast używania stałych wartości w pikselach. To właśnie sprawia, że układy mogą dostosowywać się do różnych ekranów i ustawień użytkownika.
Każda z nich musi jednak zostać rozwiązana w odniesieniu do swojego punktu odniesienia, zanim układ będzie mógł ją wykorzystać, i odbywa się to na różnych etapach: jednostki względne do czcionki – na etapie obliczeń, a procenty – w odniesieniu do bloku zawierającego – na etapie użycia. Biorąc pod uwagę ten proces, możesz zastąpić niejasną ideę, że „przeglądarka sama to rozwiązuje”, bardziej precyzyjnym pytaniem podczas debugowania: od czego zależy ta wartość i jaki jest aktualnie ten punkt odniesienia? Gdy responsywny układ zachowuje się niewłaściwie, przyczyną jest bardzo często punkt odniesienia różny od tego, co zakładałeś – na przykład procent rozwiązany w odniesieniu do nieoczekiwanego bloku zawierającego lub em kumulujący się poprzez kilka elementów wplecionych w siebie.
Konwertowanie px na rem przy użyciu korzenia 10px
Zrozumienie przetwarzania wartości pomaga również wyjaśnić powszechny proces dostosowywania rozmiarów: konwersję wartości pikselowych z projektu na rem. Załóżmy, że projekt określa:
padding: 30px;
a ty wolisz to przedstawić w ten sposób:
padding: 3rem;
Popularnym podejściem jest ustawienie rozmiaru czcionki korzeniowej na liczbę zaokrągloną:
html {
font-size: 10px;
}
Gdy to zostanie zrealizowane:
1rem = 10px
konwersje stają się prostymi obliczeniami mentalnymi:
30px → 3rem
40px → 4rem
20px → 2rem
Ogólnie rzecz biorąc:
rem = pixels / 10
Korzyścią nie są tylko uporządkowane liczby. Ponieważ każdy rem odnosi się do rozmiaru czcionki korzeniowej na etapie obliczeń, zmiana tej jednej wartości korzeniowej automatycznie skaluje wszystkie wymiary oparte na rem na stronie, co daje możliwość jednolitej kontroli nad systemem rozmiarów.
Istnieje ważna uwaga. Ustawienie wartości korzeniowej na stałe 10px anuluje domyślną wielkość czcionki, którą użytkownik mógł wybrać w ustawieniach przeglądarki, co podważa jeden z głównych powodów używania rem w kwestiach dostępności. Powszechnie stosowaną alternatywą jest html { font-size: 62.5%; }: przy domyślnej wartości 16px w przeglądarce nadal otrzymujemy 10px, ale wartość ta skaluje się proporcjonalnie, gdy użytkownik zwiększa swoją preferowaną wielkość czcionki. Jeśli przyjmiesz ten model, pamiętaj również o ustawieniu czytelnej wielkości czcionki dla elementu body (na przykład 1.6rem), ponieważ w przeciwnym razie tekst przyjmie domyślną, zmniejszoną wielkość korzeniową.
Gdy w ogóle nic nie zostało zadeklarowane
Jedno pytanie pozostaje bez odpowiedzi po omówieniu powyższych kroków. Weźmy następujący kod:
<div class="parent">
<p>Hello World</p>
</div>
oraz regułę dla elementu otaczającego:
.parent {
color: red;
}
Nigdzie nie ma reguły w stylu:
p {
color: red;
}
Mimo to tekst akapitu jest czerwony. Akapit nie ma łańcuchowej wartości color, więc jego określona wartość musi pochodzić skąd indziej. Ponieważ color jest właściwością dziedziczoną, przeglądarka używa obliczonej kolorystyki elementu nadrzędnego. To właśnie jest dziedziczenie, które bezpośrednio wpływa na etap określania wartości opisany wcześniej. Aby szczegółowo zrozumieć, które właściwości są dziedziczone, dlaczego elementy potomne otrzymują wartości obliczone zamiast deklarowanych oraz w jaki sposób inherit i initial modyfikują domyślne ustawienia, dziedziczenie wymaga osobnego omówienia.
Główne wnioski
- Wartość CSS przechodzi przez sześć etapów: deklarowany, łańcuchowy, określony, obliczony, użyty i faktyczny.
- Łańcuch wartości sprowadza wiele deklarowanych wartości do jednej łańcuchowej; tylko ta wygrana wartość jest dalej przetwarzana.
rem i em, stają się długościami bezwzględnymi na etapie obliczeń; szerokości procentowe czekają na ustalenie układu i są rozstrzygane w momencie użycia.62.5% lepiej uwzględnia preferencje czcionkowe użytkownika niż stałe 10px.Literatura pokrewna
- Skąd pochodzą wartości CSS gdy ustawiasz None: Jak działa dziedziczenie — Dowiedz się, jak przeglądarki decydują, czy dana właściwość jest dziedziczona, dlaczego elementy potomne otrzymują obliczoną wartość elementu nadrzędnego oraz jak funkcje inherit i initial umożliwiają precyzyjną kontrolę.
- Flow, przerysowanie, kompozycja: Jakie koszty generuje każda zmiana w CSS dla przeglądarki — Prześledź proces obsługi HTML i CSS w DOM, CSSOM, układzie, przerysowywaniu i kompozycji, a także dowiedz się, dlaczego zmiany szerokości są droższe w utrzymaniu niż zmiany koloru oraz jak uniknąć problemów z układem.