Dlaczego ikona przez <img> nie chce zmienić koloru
To najbardziej praktyczna lekcja w kursie. Nie o rysowaniu, tylko o tym, co dzieje się, gdy gotowa grafika trafia na stronę — i dlaczego zachowuje się wtedy zupełnie inaczej, niż w edytorze.
Wszystko sprowadza się do jednego pytania: czy przeglądarka traktuje Twój SVG jak część dokumentu, czy jak obrazek.
Cztery sposoby osadzenia
Inline — kod SVG wklejony wprost w HTML:
html
<svg viewBox="0 0 24 24" class="ikona">
<path d="..."/>
</svg>
Grafika jest częścią dokumentu. CSS strony do niej sięga, JavaScript może ją zmieniać, currentColor działa, fonty ze strony są widoczne. Pełna kontrola.
Koszty też są: kod powiększa HTML, nie jest cache'owany osobno i przy dziesięciu tych samych ikonach powtarza się dziesięć razy — chyba że użyjesz sprite'u z lekcji czternastej.
Znacznik <img>:
html
<img src="ikona.svg" alt="Ulubione">
Przeglądarka renderuje plik w izolacji, jak każdy inny obrazek. To jest ta cecha, która tłumaczy większość zaskoczeń:
- CSS strony nie sięga do środka — żadna reguła nie zmieni koloru
- JavaScript nie ma dostępu do wnętrza
- fonty wczytane na stronie nie są widoczne (o czym była lekcja piętnasta)
- odwołania do zewnętrznych plików nie działają
Działa za to wszystko, co siedzi wewnątrz pliku: SMIL, znacznik <style>, gradienty, filtry. I plik jest normalnie cache'owany, a alt daje gotową dostępność.
Tło w CSS:
css
.ikona { background-image: url("ikona.svg"); }
Zachowuje się jak <img> pod względem izolacji, ale bez tekstu alternatywnego — więc nadaje się wyłącznie do dekoracji.
Jeśli wstawiasz SVG jako data URI wprost w CSS, pamiętaj o jednej pułapce: znak # trzeba zakodować jako %23. Niezakodowany zaczyna być traktowany jak identyfikator fragmentu i tło po prostu znika. To najczęstsza przyczyna „ikona działa w pliku, nie działa w data URI".
<object> i <iframe> — dają dostęp z JavaScriptu, ale komplikują styling i rzadko są tego warte. Wspominam, żeby nie zaskoczyły w cudzym kodzie.
Jak wybrać
Prosta reguła, która obejmuje większość przypadków:
- Ikona ma zmieniać kolor razem z tekstem, stanem przycisku albo motywem → inline, najlepiej ze sprite'u.
- Grafika jest duża, złożona i statyczna — ilustracja, mapa, wykres tła →
<img>, żeby nie puchł HTML i żeby działał cache. - Element jest czysto dekoracyjny → tło w CSS.
Atrybut przegrywa z regułą
Teraz rzecz, którą trzeba znać, bo tłumaczy objaw wyglądający na zepsucie pliku.
W SVG istnieją dwa sposoby zapisania wyglądu. Atrybut prezentacyjny, czyli fill="#e05c1a" w znaczniku. I reguła CSS, czyli fill: red w arkuszu.
Wyglądają na równorzędne. Nie są. Atrybut prezentacyjny stoi w kaskadzie najniżej — przegrywa z każdą regułą CSS, nawet z selektorem uniwersalnym *.
Oba prostokąty mają identyczny atrybut
fill. Lewy ma dodatkowo klasę i jedną regułę w<style>— i to ona decyduje. Prawy zostaje pomarańczowy.
Konsekwencja praktyczna jest poważna: jedna nieostrożna reguła w motywie potrafi zmienić wygląd wszystkich osadzonych grafik naraz. Reset typografii z * obejmujący letter-spacing albo font-size zmieni każdy napis w każdym inline'owym SVG na stronie. Objaw: „grafika wygląda dobrze w pliku, źle po wgraniu", a w kodzie SVG wszystko jest poprawne, bo problem jest gdzie indziej.
To nie jest przykład teoretyczny — dokładnie ten błąd wystąpił przy budowie playgroundu do tego kursu. Reset chroniący interfejs widgetu obejmował też podgląd i zjadał font-size z kodu użytkownika. Grafika renderowała się poprawnie, tylko z cudzą typografią.
Jeśli grafika ma być odporna, jej wygląd warto zapisać w atrybutach i nie dawać jej klas, które motyw mógłby przypadkiem złapać. Jeśli ma być sterowalna — odwrotnie.
currentColor
Znasz to z lekcji o sprite'ach, ale tutaj widać, do czego naprawdę służy.
html
<g color="#e05c1a">
<circle fill="currentColor"/>
<rect stroke="currentColor"/>
</g>
Jedna wartość steruje i wypełnieniem, i obrysem. Trzeci kształt ma kolor wpisany na sztywno i nie reaguje — dokładnie tak samo zachowa się przy zmianie motywu.
currentColor czyta właściwość color, która jest dziedziczona. Wystarczy więc ustawić ją raz, wyżej w drzewie — na przycisku, na sekcji, na body — a wszystkie ikony pod spodem dopasują się same.
To jest podstawa działających ikon: jeden plik, dowolny kolor, zero duplikatów.
Tryb ciemny
Sposób zależy od osadzenia i tu jest jedna miła niespodzianka.
Inline — najprościej przez currentColor albo zmienne CSS:
css
.ikona { color: #0f0f0f; }
@media (prefers-color-scheme: dark) {
.ikona { color: #e8e4dc; }
}
W <img> — CSS strony nie dosięgnie grafiki, ale zapytanie medialne umieszczone wewnątrz pliku SVG działa. Przeglądarka przekazuje preferencję motywu także do obrazków renderowanych w izolacji:
html
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<style>
path { fill: #0f0f0f; }
@media (prefers-color-scheme: dark) {
path { fill: #e8e4dc; }
}
</style>
<path d="..."/>
</svg>
Jeden plik, dwa motywy, żadnego JavaScriptu — i działa nawet jako background-image. To jedna z najbardziej niedocenianych możliwości tego formatu.
Rozmiar na stronie
Na koniec przepis, który oszczędza sporo prób i błędów.
Żeby SVG zachowywał się jak porządny element blokowy i wypełniał kontener, usuń z pliku width i height, zostaw viewBox, a rozmiarem steruj z CSS. Wtedy proporcje pilnuje viewBox, a szerokość ustala układ strony.
css
.ikona { width: 24px; height: 24px; display: block; }
.ilustracja { width: 100%; height: auto; display: block; }
display: block jest tu ważne — domyślnie SVG jest elementem liniowym, więc pod spodem pojawia się kilkupikselowa przerwa na linię bazową tekstu. To ta sama zagadka co przy obrazkach w starym HTML i to samo rozwiązanie.
Podsumowanie
- Inline daje pełną kontrolę: CSS, JavaScript,
currentColor, fonty strony. <img>i tło w CSS renderują plik w izolacji — bez CSS strony, bez JS, bez fontów, bez odwołań zewnętrznych.- W data URI znak
#musi być zakodowany jako%23. - Atrybut prezentacyjny przegrywa z każdą regułą CSS, więc reset w motywie potrafi zmienić wygląd wszystkich grafik.
currentColorczyta dziedziczoną właściwośćcolor— jedna definicja obsługuje wszystkie kolory.- Zapytanie
prefers-color-schemewewnątrz pliku SVG działa nawet w<img>. - Do skalowania: bez
widthiheightw pliku, zviewBox, plusdisplay: blockw CSS.
W następnej lekcji zaczynamy blok o higienie: optymalizacja, czyli co eksporter wpycha do pliku niepotrzebnie i co wolno z tego wyciąć.
Do wypróbowania: w presecie kaskada dopisz do reguły CSS stroke: #0f0f0f; stroke-width: 4;, a do prawego prostokąta te same wartości jako atrybuty. Wyglądają identycznie — ale spróbuj teraz zmienić oba jedną regułą rect { stroke: none; }. Zniknie tylko ten z atrybutami, bo reguła z klasą jest silniejsza.