CSS,  Frontend

Czym jest specificity w CSS

Z pisaniem CSS-ów bywa różnie. Czasami jesteśmy pewni, że poprawnie zdefiniowaliśmy swoje style, a mimo to strona dalej ich nie pokazuje. Co robimy nie tak, przecież jasno napisaliśmy, że tutaj ma być kolor zielony, a nadal mamy niebieski. No i nie da się inaczej zdefiniować koloru, niż tak, jak to robimy. Ahhh, ten przeklęty CSS, szepczemy sami do siebie, rwąc włosy z głowy. Oooo, z !important działa dobrze. Więc zostawiamy tak, wiedząc, że to zła praktyka. Przecież działa i widać efekty, to nikt nie powinien się przyczepić. Trochę jak ze sprzątaniem, szybszy i taki sam efekt da wrzucenie wszystkiego pod łóżko, chociaż można by było poukładać rzeczy w szafkach. Ktoś przecież je potem z pod tego łóżka wyciągnie, gorzej jeśli znowu będziemy to my.

Ale napisany styl nie jest przyczyną tego problemu. My rzeczywiście dobrze zdefiniowaliśmy style, użyliśmy poprawnych atrybutów i wartości. Dlaczego więc nie działa? Jakiś mechanizm nadpisuje nasze style, nadając im wyższy priorytet, dlatego to one są wyświetlane. Tym mechanizmem jest specificity, który w taki sposób nokautuje nieświadomych programistów. Czas nauczyć się robić uniki i go pokonać.

Czym jest specificity?

Specificity, po polsku specyficzność, to reguła określająca, który styl zostanie użyty w przypadku konfliktu. A o konflikt nietrudno, bo w CSS może być tak, że do jednego, tego samego elementu przypisane zostaną sprzeczne style. Kiedy jeden styl zdefiniuje szerokość na całą stronę, a inny zwęzi ją do połowy, przeglądarka będzie musiała coś wybrać. Kolejny styl ustawi wyrównanie do prawej, a inny do lewej. Jeszcze inny ustawi kolor tła na zielony, a inny na niebieski. CSS nie wymiesza kolorów dając turkusowy, tylko wybierze jeden z nich, tak samo jak nie wypośrodkuje elementu. Specificity zdecyduje, jaki styl wybrać za pomocą wag i wyliczeń. O wyliczeniach i algorytmie zrobię oddzielny wpis, tutaj opiszę podstawowe reguły.

Przykład prostego konfliktu

Zobaczmy to na przykładzie. Mamy prostą stronę HTML z jednym elementem, któremu będziemy zmieniać kolor.

<body>
   <button>Click me</button>
</body>

Ma on zdefiniowany styl, który nadaje mu początkowy kolor i resetuje wygląd przycisku. Nie jest to jakieś wizualne arcydzieło, ale na potrzeby tego materiału nie musi.

button {
    border: 0;
    background-color: lightskyblue;
}

Chyba nie będzie zaskoczeniem, że wygląda on jak niebieski prostokąt z napisem Click me:

Tutaj jeszcze nie wystąpił żaden konflikt, dlatego dodajmy go. Przykładowo ustawmy inny kolor do naszego przycisku w osobnym selektorze. Nasz CSS wtedy będzie wyglądał tak:

button {
   border: 0;
   background-color: lightskyblue;
}

button {
   background-color: yellow;
}

Nie usunęliśmy starego stylu, on nadal tu jest. Powoduje to konflikt i jakiś kolor musi zostać wybrany. Przeglądarka zdecydowała się na żółty:

To oczywiście prosty przykład na potrzeby materiału. Nikt nie zdefiniuje tego samego stylu o takim samym selektorze jeden po drugim, jak ja tutaj. Jednak nadal, takie konflikty mogą mieć miejsce, jak styli w projekcie jest o wiele więcej. W takich przypadkach dalej zdefiniowany styl nadpisuje ten wcześniejszy. Tylko czy dzieje się tak zawsze?

Przykład specificity

Zmodyfikujmy HTML-a, dodając do przycisku klasę:

<button class="btn">Click me</button>

Oraz dodajmy styl po tej klasie, tylko niech tym razem znajduje się pośrodku, a nie na końcu wywołania.

 button {
    border: 0;
    background-color: lightskyblue;
}

.btn {
    background-color: violet;
}

button{
    background-color: yellow;
}

Jakby miało to działać tak, że kolejne style zawsze nadpisują te wcześniejsze, to nic nie powinno się zmienić, tzn. powinien zostać kolor żółty. Tutaj jest jednak inaczej. To środkowy styl zastąpił zarówno późniejsze, jak i wcześniejsze definicje. W efekcie mamy kolor:

Dlaczego tak się stało? Nie znając mechanizmu specificity możesz być zaskoczony. Definicja stylów za pomocą klas jest silniejsza niż definiowanie ich za pomocą elementów. I jest to rozsądne. Przyciski z klasą o nazwie btn, cancel, czy next są konkretniejsze. Mówią więcej do jakich przycisków chcesz przypisać jakie style. Więc, jeśli CSS ma styl wewnątrz klasy i elementu, to w pierwszej kolejności wybierze ten klasowy.

To by był dopiero problem, gdy masz już ostylowane i zatwierdzone ważne komponenty, i stylem ogólnym zmieniasz przez przypadek ich wygląd. Nie, ty chcesz, by przykładowo style do przycisków anulowania wyglądały nadal tak samo, jak to już zdefiniowałeś. Style ogólnie dla buttonów nie powinny ich zmienić, ale zmiany powinny być widoczne dla elementów, które nie są ostylowane przez klasy, idiki czy elementy inline. Dlatego też mechanizm nazywa się specificity, bo CSS w pierwszej kolejności nadaje priorytet stylom do rzeczy bardziej sprecyzowanych.

Specificity jest sprawdzane dla każdego stylu z osobna. Wiele stylów dla danego elementu nie znajduje się w tym samym miejscu. Dlatego część styli zostanie wziętych z bardziej znaczących selektorów, a część z mniej znaczących, bo tylko tam są zdefiniowane. Jak w powyższym przykładzie, kolor brany jest z klasy .btn, ale border już z elementu button, bo w klasie nie ma definicji, która by go nadpisała.

Specificity z życia codziennego

W życiu działa to dość podobnie. Jeśli ktoś zleci zrobienie czegoś mniej konkretnego, to masz większe pole manewru, ale jak już sprecyzuje dokładnie, o co mu chodzi, to nie powiesz mu, że nie zrozumiałeś. Poniżej dwa przykłady.

Przykład z szukaniem aut

Kiedy ktoś da ci kluczyki i powie, żebyś przyniósł jego bagaż z samochodu, a ty nie wiesz, czym on jeździ, to nie masz bladego pojęcia o jaki samochód chodzi. Musiałbyś posprawdzać wszystkie samochody na parkingu, tak jak CSS zmienia style wszystkim elementom <button>. Wiesz jednak, że mowa o samochodzie, a nie motorze (czyli wiesz, że mowa o elemencie button, a nie div). Jak poda markę to masz większe pojęcie czego szukać, ale zawsze możesz spotkać kilka takich samych aut. CSS-owi jak podasz klasę, to sprawdzi najpierw style dla niej, te dla elementu button będą drugorzędne. Ale jak już poda ci konkretny numer rejestracyjny, to nie możesz się pomylić, co do identyfikacji. Tak, jak CSS weźmie styl z selektora id, mając taki sam z klasy.

W tym przykładzie, bardziej precyzyjne polecenie zastępuje mniej konkretne, bo nie ma sensu szukać innych samochodów nawet tej samej marki, jak znasz już rejestrację. W CSS-ie działa to lekko inaczej. Szukając bagażu wśród wszystkich samochodów w końcu trafisz kluczykiem na właściwy i zaprzestaniesz dalszego szukania po innych autach. CSS będzie próbowało zmienić kolory wszystkim samochodom, jak nie będą miały zdefiniowanego swojego koloru o wyższej wadze.

Przykład z zakupami

Pójdź do sklepu kupić owoce. Wybierzesz dowolny owoc ze sklepu, tak jak styl CSS zmienni wszystkim przyciskom style. Ale jak powiedzą idź kupić owoce i jabłka, to może kupisz i inne owoce, ale bez jabłek nie wrócisz. Tak samo mając style dla danej klasy oraz ogólnie dla <button> to ten klasowy będzie ważniejszy. A jak się jeszcze dowiesz, że masz kupić owoce, kilka rodzajów jabłek, ale koniecznie musi być odmiana ligol, bo mają być na szarlotkę, to koniecznie je weźmiesz. Więc się nie dziw, że CSS działa podobnie, bo też nie chce dostać nagany, tak jak ty słuchać, że nie kupiłeś tego, co było najbardziej potrzebne.

Może się wydawać, że CSSy różnią się od podanych przykładów, ale to dlatego, że patrzysz na htmlowy element, który może mieć wiele styli, jak na przykładowe jabłko, czy samochód. Chodzi o to by spojrzeć na konkretny styl, np. kolor. To jemu wybierana jest wartość, a poleceniem jest selektor.

W przykładach chodzi o pokazanie, że specificity występuje również w życiu codziennym, a nie tylko w CSS-owym slangu technicznym. Dodatkowo wyobraźnia pomaga w przyswojeniu zagadnień.

Wagi specificity

Skoro wiesz już, że niektóre style mają wyższy priorytet, czas je wypisać od najmniej istotnych:

  1. Elementy (np. button, div, li) oraz pseudo-elementy (np ::before, ::first-line).
  2. Klasy (np. .btn), atrybuty (np. [type=”text”]) oraz pseudo-klasy (np. :link, :hover).
  3. Id (np. #id).
  4. Style inline (np. <button style=”background-color:brown;”>).

Kilka selektorów ma dokładnie taką samą wagę specificity, np. zdefiniowane style poprzez atrybuty i klasy. W takim przypadku działa zasada, że ten ostatnio zdefiniowany zastępuje wcześniejsze. Niektórzy mówią, że wybierany jest styl, który jest najbliżej elementu HTML. Jeżeli jednak selektor ma wyższą wagę, to go wybierze CSS.

Do tego, jeśli ta lista nie wystarcza, to !important pozwala nadpisać specificity i styl z !important staje się silniejszy od tego inline. Stosowanie jednak tego obejścia na dłuższą metę przysparza więcej problemów niż je rozwiązuje.

Podsumowanie

Zrozumienie specificity jest istotne w pracy z CSS-em. Wydaje się jednak, że dużo osób o tym pojęciu nie słyszało. Owszem, można pracować bez tego, nawet nie nadużywając słowa !important. Są przecież narzędzia developerskie, który w ładny sposób pokazują jak style są nadpisywane. Jednak wiedza zawsze pomaga w pracy. Będziesz wiedział co się dzieje i dlaczego to właśnie inne style nadpisują, a Twojego nie widać. Szybciej rozwiążesz problem, nie będziesz tak sfrustrowany i zaskoczony, że CSS robi dziwne rzeczy po swojemu. Jednak to nie wszystko, co można powiedzieć o specificity. Jest to również algorytm, który wylicza te wagi, szczególnie w trudniejszych przypadkach. O tym zrobię jednak oddzielny materiał, tak jak i spróbuje przedstawić pojęcie specificity bardziej zrozumiałe dla fanów sci-fi. To nie będzie mój przykład, tylko taki sposób nauki krąży po internecie, ale zobaczycie, jak już o tym napiszę.

Czego się dowiedziałeś?

  1. Wiesz czym jest specificity i poznałeś jego podstawy.
  2. Wiesz na co zwracać uwagę, jak Twój styl nie wyświetla się, mimo że zdefiniowany jest poprawnie.
  3. Jesteś świadomy, że w CSS łatwo o konflikt stylów i musi zostać wybrany jeden styl spośród tego konfliktu.
  4. Wiesz, która definicja stylów ma większy priorytet i zastąpi inne (dla prostych przypadków).
  5. Masz świadomość, że niektóre elementy mają takie same wagi w specificity i przykładowo styl zdefiniowany przez atrybut może zastąpić ten zdefiniowany przez klasę.
  6. Wiesz, że dla definicji stylów o takiej samej wadze specificity zostanie wybrany ostatni.
  7. Wiesz, że specificity jest wyliczane dla każdego stylu osobno, więc w obrębie jednego elementu style mogą znajdować się w różnych selektorach, np. kolor w selektorze klasy, a border w selektorze elementu.
  8. Da się złamać specificity za pomocą !important, ale nie warto tego robić.
  9. Wiesz dlaczego ten mechanizm nosi miano specificity.
  10. Masz wyobrażenie na temat specificity w rzeczywistych sytuacjach i że z ligoli można zrobić szarlotkę.
Social media & sharing icons powered by UltimatelySocial