techchrome2023

How a 10 year old Chrome bug impedes SVG adoption

Jan 22, 2023
How a 10 year old Chrome bug impedes SVG adoption
 James Harrison (unsplash.com)

Fahner IT maakt veel gebruik van SVG. Dit is een technologie die het mogelijk maakt om bepaalde grafische elementen - zoals icoontjes - op elke schermresolutie perfect scherp weer te geven tegen een fractie van de bandbreedte van bijvoorbeeld PNG icoontjes. In dit artikel bespreek ik hoe een tien jaar oude bug in Chrome het bijna onmogelijk maakte om een optimalisatie uit te voeren (als onderdeel van een CMS update) in de manier waarop Fahner IT icoontjes gebruikt en hoe het uiteindelijk "opgelost" is (of beter, hoe ik er omheen gewerkt heb). Let op, het is vrij technisch.


De oude situatie

De eerste implementatie van icoontjes (inmiddels jaren geleden gemaakt) was erg simpel. Voor elk icoontje werd een eigen SVG-bestand gegenereerd op basis van kleur (zoals text-666.svg in de bovenstaande screenshot). De standaardkleur van icoontjes was ergens in een database opgeslagen en voor speciale icoontjes moest de kleur specifiek aangegeven worden. Dit werkt op zich prima maar heeft twee grote nadelen:

  1. Kleuren kunnen niet dynamisch aangepast worden waar nodig (zoals bijvoorbeeld bij het inschakelen van een "donkere modus"). Overal moet van tevoren aangegeven worden welke kleuren mogelijk nodig gaan zijn. Gebruik maken van het "currentColor" sleutelwoord is hier niet mogelijk, aangezien de SVG-bestanden als extern gezien worden en dus de lokale kleur op de pagina niet overnemen.
  2. Het leidt tot een grote hoeveelheid verzoeken naar de server (het totaal liep op tot wel 100 in de situatie op de screenshot), welke elk ~400 bytes aan "rommel" toevoegen. Bovendien is het laden van al deze icoontjes één voor één duidelijk zichtbaar voor de bezoeker (niet mooi!).


De gewenste situatie

Het zou veel mooier zijn wanneer alle benodigde icoontjes allemaal in één keer ingeladen kunnen worden. Dit kost minder bandbreedte en ziet er mooier uit voor de bezoeker (alle icoontjes verschijnen in één keer al tijdens het laden van de pagina). Bovendien moeten de icoontjes inlijn geplaatst worden om gebruik te maken van currentColor, zodat de kleur dynamisch met behulp van CSS aangepast kan worden.

Om dit te bereiken moeten we twee dingen combineren. Ten eerste hebben we één bestand nodig dat elk icoon definiëert als een eigen <symbol>-element (deze methode is ook hier beschreven), waarbij elk icoon gekleurd wordt met behulp van het currentColor sleutelwoord. Ten tweede moeten we op de plaatsen waar we de icoontjes willen gebruiken een <svg>-element plaatsen dat het icoontje importeert met behulp van het <use>-element. Dit klinkt op zich eenvoudig genoeg, maar...


De Chrome bug

Tijdens het maken van icoontjes in SVG wordt soms gebruik gemaakt van zogenaamde <mask>-elementen (maar ook andere elementen worden door de bug getroffen). Deze elementen krijgen een eigen ID en worden dan later aangesproken via het gelijknamige mask-attribuut om het toe te passen op een ander element. Op deze manier kun je theoretisch verschillende mask-elementen definiëren in verschillende documenten en deze hergebruiken waar nodig.

Maar, door een 10 jaar oude bug in Chrome werkt deze functionaliteit niet wanneer het gaat om externe SVG-bestanden. Dit zou betekenen dat voor Chrome alsnog alle icoontjes (of ten minste diegene die gebruik maken van masks enz.) één voor één als losse bestanden ingeladen zouden moeten worden, met bijbehorende problemen. Deze bug lijkt niet voor te komen in Firefox.


De "oplossing"

Een eerste manier om hier omheen te werken is om gewoonweg niet gebruik te maken van een extern SVG-bestand en alle benodigde icoontjes inlijn in elke pagina mee te sturen. Dit werkt prima, maar heeft één groot nadeel: de icoontjes moeten voor elk paginaverzoek meegestuurd worden en worden daarmee ook in alle HTML caches opgeslagen. Dit is niet erg als het gaat om één of twee icoontjes per pagina (in dit geval kan de SVG ook prima overal inlijn geplaatst worden), maar dit gaat niet werken in mijn geval (het is ook gewoon een minder "mooie" oplossing).

De tweede manier is om niet gebruik te maken van de elementen die de bug veroorzaken. Dit kan door bijvoorbeeld alle icoontjes om te zetten naar <path>-elementen (welke wel altijd werken). Het nadeel hiervan is dat deze elementen vaak veel meer bandbreedte innemen voor complexere icoontjes vanwege het feit dat ze elk hoekje en elke bocht van het icoontje apart opslaan. Simpelweg een mask toepassen is veel efficiënter (en scherper, want een path laat vaak informatie weg tijdens de optimalisatie). Vanaf hier zijn er opnieuw twee keuzes:

  1. Alle icoontjes omzetten naar path-elementen, bijvoorbeeld via een grafisch programma dat SVG ondersteunt (zoals Inkscape), en de extra bandbreedtekosten "accepteren," ook voor browsers die wel een optimale versie van de SVG zouden kunnen ondersteunen. Dit verslaat een beetje het doel van de optimalisatie, aangezien ik onder andere de totale bandbreedte juist wilde verminderen.
  2. Alleen een path-variant maken van de icoontjes die getroffen worden door deze bug en deze variant alleen tonen op browsers die last hebben van deze bug.

Ik heb dus gekozen voor optie 2: bezoekers die Firefox gebruiken ontvangen een compactere en scherpere variant van de icoontjes ten opzichte van bezoekers die Chrome gebruiken. Helaas was ik door deze bug genoodzaakt om User-Agent sniffing toe te passen. Hopelijk wordt de bug in de toekomst alsnog opgelost zodat deze extra stap overbodig wordt.


Weet jij een betere oplossing of workaround? Neem contact op, ik ben zeer geïnteresseerd.