Jak zrobić dwadzieścia ikon z jednego pliku
Znasz już <defs> z gradientów — schowek na rzeczy, które nie są rysowane, tylko czekają na wywołanie. Teraz użyjemy go do czegoś innego: do przechowywania kształtów, które chcesz wstawić więcej niż raz.
To jest lekcja o niepowtarzaniu się. I o tym, jak działa mechanizm, na którym stoją wszystkie zestawy ikon w webie.
use to odwołanie, nie kopia
html
<defs>
<g id="znacznik">
<circle cx="0" cy="0" r="22" fill="#e05c1a"/>
<circle cx="0" cy="0" r="9" fill="#fdf1eb"/>
</g>
</defs>
<use href="#znacznik" x="60" y="65"/>
<use href="#znacznik" x="120" y="65"/>
Element <use> wstawia w danym miejscu zawartość wskazaną przez href. Atrybuty x i y działają jak przesunięcie — dlatego wygodnie jest rysować definicję wokół punktu 0,0, tak jak w lekcji o transformacjach.
Najważniejsze jest jednak to, że to nie jest kopiuj-wklej. To żywe odwołanie.
Zmień promień w definicji. Wszystkie trzy znaczniki reagują naraz, bo wszystkie pokazują to samo. W kodzie zmienia się jedna liczba, na ekranie trzy kształty.
Konsekwencje są praktyczne. Plik jest mniejszy, bo skomplikowana ścieżka występuje raz. Poprawka jest jedna, a nie dwadzieścia. I nie ma ryzyka, że przy trzeciej kopii ktoś zapomni zmienić kolor.
symbol — use z własnym kadrem
<g> w <defs> sprawdza się przy powtarzaniu tego samego kształtu w tej samej skali. Do ikon lepiej nadaje się <symbol>, bo ma dwie dodatkowe cechy.
Nigdy nie jest rysowany bezpośrednio. Nie musisz go chować w <defs> — sam z siebie jest niewidoczny, dopóki ktoś go nie wywoła.
Ma własny viewBox. To zmienia wszystko: symbol jest samodzielnym, małym płótnem, które przy wywołaniu dopasowuje się do podanych width i height. Rysujesz ikonę raz, w wygodnym układzie 24 na 24, a potem wstawiasz ją w dowolnym rozmiarze bez liczenia skali.
Trzy wystąpienia tej samej ikony w trzech rozmiarach i trzech kolorach. Suwak
viewBoxpokazuje mechanizm z pierwszej lekcji od nowej strony: powiększanie kadru symbolu zmniejsza ikonę wewnątrz każdego wystąpienia naraz.
currentColor, czyli sztuczka, która to wszystko spina
Zwróć uwagę na wypełnienie w symbolu:
html
<path d="..." fill="currentColor"/>
currentColor to specjalna wartość, która oznacza „kolor odziedziczony z otoczenia" — czyli wartość właściwości colorelementu, w którym ikona się znalazła.
Dzięki temu jedna definicja obsługuje wszystkie kolory. Ikona w przycisku jest biała, ta sama ikona w tekście jest grafitowa, a w stanie błędu czerwona — bez trzech plików i bez trzech definicji. Wystarczy w CSS ustawić color na elemencie nadrzędnym.
To jest też najprostsza droga do obsługi trybu ciemnego. Skoro ikona dziedziczy kolor tekstu, a tekst zmienia kolor razem z motywem, ikona zrobi to sama.
Sprite ikonowy
Złóż to razem, a dostaniesz standardowe rozwiązanie dla zestawów ikon. Jeden plik z wszystkimi symbolami:
html
<!-- ikony.svg -->
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="serce" viewBox="0 0 24 24"><path d="..."/></symbol>
<symbol id="kosz" viewBox="0 0 24 24"><path d="..."/></symbol>
<symbol id="lupa" viewBox="0 0 24 24"><path d="..."/></symbol>
</svg>
I wywołania w dokumencie:
html
<svg class="ikona"><use href="ikony.svg#serce"/></svg>
Zalety: jedno pobranie zamiast dwudziestu, jedna kopia w pamięci podręcznej, ikony sterowane kolorem i rozmiarem z CSS. Przez lata było to domyślne podejście do ikon i nadal jest bardzo dobre.
Dwie rzeczy, o których trzeba wiedzieć. Plik zewnętrzny podlega zasadzie tego samego pochodzenia — z innej domeny wymaga poprawnych nagłówków CORS. I wywołania zewnętrzne nie działają w <img>, tylko w SVG osadzonym w dokumencie.
Alternatywą jest wklejenie sprite'u bezpośrednio na początku strony, w ukrytym <svg>. Kosztuje trochę kodu w HTML, ale znosi obie powyższe niedogodności.
Pułapka: CSS nie wchodzi do środka
Ta jest nieoczywista i kosztuje ludzi sporo czasu.
Zawartość wstawiona przez <use> trafia do cienistego drzewa — osobnej gałęzi DOM, do której selektory CSS nie sięgają. Reguła w rodzaju .ikona path { fill: red; } nie zadziała, bo tej ścieżki formalnie nie ma tam, gdzie jej szukasz.
Przez granicę cienistego drzewa przechodzą tylko właściwości dziedziczone: color, fill, stroke, opacity i im podobne, ustawione na samym elemencie <use> albo wyżej.
Stąd praktyczna zasada projektowania ikon do sprite'u: nie wpisuj kolorów na sztywno. Zostaw fill="currentColor" albo w ogóle pomiń atrybut, a kolorem steruj z zewnątrz. Ikona z wpisanym fill="#333" będzie na zawsze szara i nic jej z CSS nie ruszy.
Podsumowanie
<use>wstawia odwołanie do istniejącego kształtu, a nie jego kopię; zmiana definicji zmienia wszystkie wystąpienia.xiyna<use>działają jak przesunięcie, więc definicje wygodnie rysuje się wokół punktu 0,0.<symbol>ma własnyviewBoxi nigdy nie renderuje się sam — to właściwy pojemnik na ikonę.currentColorsprawia, że jedna definicja obsługuje dowolny kolor i tryb ciemny.- Sprite to jeden plik z wieloma symbolami; wersja zewnętrzna wymaga tego samego pochodzenia i nie działa w
<img>. - Selektory CSS nie wchodzą do zawartości
<use>; przechodzą tylko właściwości dziedziczone.
W następnej lekcji zajmiemy się tekstem — w tym pytaniem, dlaczego napis w Twoim SVG wygląda inaczej na komputerze klienta.
Do wypróbowania: w presecie symbol usuń atrybut color z drugiego wystąpienia. Gwiazdka zrobi się czarna, bo currentColor bez ustawionej wartości spada do domyślnego koloru tekstu. To dobra ilustracja tego, że currentColor nic nie wymusza — tylko dziedziczy.