0
votes

My current bad implementation of icons

I am currently working on a new Angular project, and I am not happy with my SVG-icon implementation in my other project (using @ngneat/svg-icon). It requires SVG files to be in a folder, then registered in Typescript and then compiled into (iconName).ts files with a script that has to be run.

An easier Way

In my current project i am using Bootstrap and while studying the docs i stumbled upon the following svg implementation:

Source: https://getbootstrap.com/docs/5.0/components/alerts/

<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
  <symbol id="check-circle-fill" fill="currentColor" viewBox="0 0 16 16">
    <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
  </symbol>
  <symbol id="info-fill" fill="currentColor" viewBox="0 0 16 16">
    <path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/>
  </symbol>
  <symbol id="exclamation-triangle-fill" fill="currentColor" viewBox="0 0 16 16">
    <path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/>
  </symbol>
</svg>

<svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Info:"><use xlink:href="#info-fill"/></svg>
<svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Success:"><use xlink:href="#check-circle-fill"/></svg>

This usage would have me only paste the svg icons in the Symbols, and have all the icons in a hidden element that i load somewhere on the page. Giving them an appropriate id, this would allow me to use SVG icons anywhere on the page using xlink:href.

Too good to be true

According to MDN the xlink:href attribute is deprecated (Source). But since bootstrap is using it and i found many posts saying that it will never be removed because so many sites are using it, i am tempted to stil use it

A non-deprecated alternative

On the MDN Page of SVG <use> (Source) I found the following example not usin xlink:href but only href instead. At first i though this could only be used to reference another path within the same SVG but upon trying it on my site it worked.

<svg viewBox="0 0 30 10" xmlns="http://www.w3.org/2000/svg">
  <circle id="myCircle" cx="5" cy="5" r="4" stroke="blue"/>
  <use href="#myCircle" x="10" fill="blue"/>
  <use href="#myCircle" x="20" fill="white" stroke="red"/>
  <!--
stroke="red" will be ignored here, as stroke was already set on myCircle.
Most attributes (except for x, y, width, height and (xlink:)href)
do not override those set in the ancestor.
That's why the circles have different x positions, but the same stroke value.
  -->
</svg>

The Question

So the tl:dr question is "how should i implement my icons?", but here are the questions leading to that:

  • Would you still reccommend xlink:href since bootstrap is using it?
  • Is <use href> a valid alternative or is it intendet to be different?
  • Do Paths with display none have any bad impact like performance or page load, since all of the above mentioned possibilities would include all of my svg icons (even if not used on a specific page) in the html?
1
Yes, xlink: is no longer required, just href works in all modern browsers. Problem with use is you can't have duplicate IDs.. often a problem when you apply filters. display:none content is still content that needs to be parsed by the DOM parser. So yes, you pay a penalty; but if this causes performance issues you probably have loads more other bad-performing code... unless you have 7347 icons on one pageDanny '365CSI' Engelman

1 Answers

1
votes

Provided you do not have to support old browsers;
a modern <svg-icon> W3C standard Web Component can create the SVG client-side

If you define the Web Component (including icon path info) inside a <script> in the <head> of your HTML document, there are zero extra downloads and icons are displayed instantly when used.

All HTML code required, to display 3 icons

<svg-icon is="menu"></svg-icon>
<svg-icon is="settings"></svg-icon>
<svg-icon is="renew" rotate=45 fill=blue stroke=white></svg-icon>

All script:

<script>
((t,e={path:(t,e="")=>`<path d='${t}' ${e}/>`},r={v1:"",v2:"",v3:"",is:"",img:1,box:9,rect:"<rect width='100%' height='100%' fill='{tile}' {border}/>"
,border:"",filter:"",tile:"none",fill:"none",width:1,scale:1,opacity:1,rotate:0,stroke:"#000",xy:0,w:0,h:0,top:"",api:[t,e]})=>{
customElements.define("svg-icon",class extends HTMLElement{static get observedAttributes(){return Object.keys(r)}attributeChangedCallback(){this.svg()}svg(i=this,s=i.A||Object.keys(i.A={...r}).map((t=>Object.defineProperty(i,t,{set:e=>i.setAttribute(t,e),get:()=>i.getAttribute(t)||getComputedStyle(i).getPropertyValue("--svg-icon-"+t).replace(/"/g,"").trim()||i.A[t]},e[t]=e=>(i.A[t]=e,"")))),l,a=(t[i.is]||"").split`;`.map((t=>([s,l]=t.trim().split`:`,e[s]?e[s].apply(i,l.split`,`):t))).join``,o=i.box/2,
c=`<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 ${i.w||i.box} ${i.h||i.box}'>${i.rect}<g stroke-width='{width}' stroke='{stroke}' fill='{fill}' opacity='{opacity}' filter='{filter}' transform='translate({xy}) matrix({scale} 0 0 {scale} ${o-o*i.scale} ${o-o*i.scale}) rotate({rotate} ${o} ${o})'>${a}</g>${i.top}</svg>`.replace(/{\s?([^{}\s]*)\s?}/g,((t,e)=>i[e]))){return i.innerHTML=1==i.img?
`<img src="data:image/svg+xml,${c.replace(/#/g,"%23")}">`:c}})})(
{
   menu:"box:9;path:m1.5 2.8h6m0 2h-6m0 2h6,stroke-linecap='round'",
   settings:"box:96;<circle stroke-width='12' cx='48' cy='50' r='26'/><circle stroke-width='12' cx='48' cy='50' r='36' stroke-dasharray='14'/>",
   renew:"box:96;fill:black;path:M48 24v12l16-16-16-16v12c-18 0-32 14-32 32 0 6 2 12 5 17L27 60A23 23 0 0 1 24 48c0-13 11-24 24-24zm26 6L69 37c2 3 3 7 3 11 0 13-11 24-24 24v-12l-16 16 16 16v-12c18 0 32-14 32-32 0-6-2-12-5-16z"
});
</script>

<style>
  svg-icon {
    width: 80px;
    display: inline-block;
    background: grey;
  }
  svg-icon:hover { background: lightgrey; cursor:pointer }
</style>

<svg-icon is="menu"></svg-icon>
<svg-icon is="settings"></svg-icon>
<svg-icon is="renew" rotate=45 fill=gold stroke=white></svg-icon>

IconMeister

The <svg-icon> Web Component is fully documented at: https://iconmeister.github.io

Including 7347 icons extracted, and processed to JSON format, from all major Icon Sets:
Bootstrap, Feather, FontAwesome, Google Material, Eva, Radix, IBM Carbon, Linear, Vaadin and Clarity

So the site loads a bit slow, processing 7347 icons...

All <svg-icon> icons are configurable with attributes, properties and CSS properties

Web Component JavaScript code (excluding icons) by itself is only 800 GZipped Bytes.
But that will be less, because your whole HTML document is GZipped.