Include inline svg through symbol or css? - html

I am planning to use inline svg concept to create svg spritesheet for a project.
There are actually many ways to create svg spritesheet. I preferred two methods (because of performance) to create spritesheet. they are as follows:
Group all the svgs to single svg by wrapping each svg's content using symbol tag with unique ID, so that later we can refer this using use tag in HTML.
Generate a css file having all the svgs referred through css background-image property. each svg will have a unique class name.
Now, I am in dilemma of which method to use exactly. FYI, this is not opinion-based question because I am not looking for opinions but considering performance and optimal solution.
PS: I can generate the svg sprite sheets using gulp task runner.

Pre-Information
Performance within a browser is something very difficult to test for and gauge, simply because of the amount of variables which can cause changes, spikes or differences between browsers, hardware or other things which could be bottle-necking the performance.
The below test's I have run on a Dell laptop with the following hardware and browser
Intel Core i5-3320M CPU # 2.60GHz
8GB DDR3 Ram (unsure of timing's etc)
Windows 8.1 Enterprise - 64Bit
Google Chrome v45.0.2454.101 m
I've only ran 3 of the test's I would have liked to due to time constraints but may come back to continue with the tests and also rerun them on different browsers and machines.
The SVG I Used
I created an SVG element which uses 5 icons layered on top of each other.
All of these icons are from iconmonstr.com which provides free to use SVG icons.
CodePen
The Tests
Inline - <use>
The Code
<svg height="100px" width="100px" viewBox="0 0 512 512">
<use xlink:href="#menu-icon"></use>
</svg>
<svg height="100px" width="100px" viewBox="0 0 512 512">
<use xlink:href="#user-icon"></use>
</svg>
<svg height="100px" width="100px" viewBox="0 0 512 512">
<use xlink:href="#home-4-icon"></use>
</svg>
<svg height="100px" width="100px" viewBox="0 0 512 512">
<use xlink:href="#phone-icon"></use>
</svg>
<svg height="100px" width="100px" viewBox="0 0 512 512">
<use xlink:href="#globe-4-icon"></use>
</svg>
Full Codepen Example
The Results
1 Request - 221B Transfer
Results
Average
Finish: 10.3ms - DOMContentLoaded: 22.8ms - Load: 22.3ms
Inline - Individual <svg>'s
The Test
This file is too large so only giving CodePen Example
Full Codepen Example
The Results
1 Request - 221B Transfer
Results
Average
Finish: 9.7ms - DOMContentLoaded: 20.6ms - Load: 19.9ms
External File - <use>
The Test
<svg height="100px" width="100px" viewBox="0 0 512 512">
<use xlink:href="svg.svg#menu-icon"></use>
</svg>
<svg height="100px" width="100px" viewBox="0 0 512 512">
<use xlink:href="svg.svg#user-icon"></use>
</svg>
<svg height="100px" width="100px" viewBox="0 0 512 512">
<use xlink:href="svg.svg#home-4-icon"></use>
</svg>
<svg height="100px" width="100px" viewBox="0 0 512 512">
<use xlink:href="svg.svg#phone-icon"></use>
</svg>
<svg height="100px" width="100px" viewBox="0 0 512 512">
<use xlink:href="svg.svg#globe-4-icon"></use>
</svg>
Use this with the base file given at the top of the page
The Result
2 Requests - 440B Transfer
Results
Average
Finish: 57.5ms - DOMContentLoaded: 41.3ms - Load: 58.4ms
Conclusion
As we can see from the above tests and results, using an inline SVG and referencing it is much quicker than using an external file; cached or not.
Neither of the two inline SVG methods seem to have that many speed differences, but I would personally go for the <use> method, simply because it is easier to use in the long run and helps keep your body code clean.
Now, as I have stated, these results are entirely dependant on an infinite amount of variables, to name a few:
Browser
Hardware
Internet Connection
SVG File Size
Bottle-neck Software (Anti-virus etc)
I would personally use whatever you feel most comfortable with.
I hope these results are somewhat useful or satisfactory and help you with what you require.
View all the tests and results here!

I've had the most success with SVGs in a single sprite file with unique ids. Most of the svg minification and concatenation scripts will simply name each Id after the individual file name, which is easy enough.
Then, for the best chance of proper scaling and such, I included the SVGs via the HTML tag:
<svg viewBox="0 0 50 50"
class="svgIcon" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<use xlink:href="#myIconIdHere"></use>
</svg>
If you're lucky, the viewBox value will be the same, if not, you may need to provide that with a view helper of some sort.
In my past works, I've stored the individual viewBox values in a config for later dynamic generation. Of course you could also just generate each SVG tag in a file somewhere as a string. Here is a sample config we used on one project:
config = {
"arrow": {
"viewBox" :"0 0 50 49.999360957030746",
}
,
"close": {
"viewBox" :"0 100 50 49.999360957030746",
}
...
}
Performance-wise, I can only speak a small amount.
This solution worked in IE9+, Chrome, Firefox, and mobile devices. We have animations across the board that involve these SVGs, and scale them for each breakpoint. The CSS animations applied to elements didn't have any major lagging issues except for IE9. I did look over this analysis for more help.
I'd be happy to show you the high traffic page using these SVGs, but would prefer if you private messaged me.

Related

How to use the whole SVG with a <use> tag?

I have an SVG file freshly exported from Figma.
I don't want to inline it because it's used on multiple places.
I can't specify it as <img src="..."> because I need to style its parts.
So, I tried to include it like this:
<svg>
<use xlink:href="http://example.com/myshape.svg"></use>
</svg>
But it doesn't work. MDN states:
The <use> element takes nodes from within the SVG document, and duplicates them somewhere else.
It takes nodes from the document. With SVG sprites, you have <symbol> tags with id attributes and you reference them with <use> by adding xlink:href="mysprite.svg#symbolid". However, here I need to use the whole document, I.E. the root node. Is there a way to do that?
It would also be great if I don't have to edit the source SVG file, as it's uploaded via an admin panel by the user. It's just a file exported from any vector graphics program.
SVG 2 (when implemented in browsers) will allow to reference another SVG file without any fragment identifier:
New in SVG 2: An href without a fragment allows an entire SVG document to be referenced without having to ensure that it has an ID on its root element.
Before:
<!-- my-vector.svg -->
<svg id="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<circle r="10" cx="12" cy="12" />
</svg>
<use href="my-vector.svg#icon"></use>
After (there will be no need to define id="..." on the svg):
<!-- my-vector.svg -->
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<circle r="10" cx="12" cy="12" />
</svg>
<use href="my-vector.svg"></use>
SVG 2 seems to be in the process of development in major browsers (see this Chrome feature and specifically this Chromium issue: Issue 366545: [SVG2] Allow to reference entire files).

How to get rid of title on mouse hover on font awesome svg icon (using svg sprites)

Edit: this questions is irrelevant in case you're using Font Awesome v. >= 5.8.0 because its maintainers (after a long discussion:)), agreed that the "title" should be removed from the svg files and it's done in v. 5.8.0 : https://github.com/FortAwesome/Font-Awesome/issues/14595
Now, back to the original question:
I just tried the Font Awesome (5) in its 'svg sprites' version, following the official, pretty simple tutorial: https://fontawesome.com/how-to-use/on-the-web/advanced/svg-sprites
Everything's working as expected, except I can see a title popping up when I hover on the image (which is bad). I mean the one we see when we have e.g. <div title = "blah"></div>.
So, in compliance with the docs (I guess), I have copied the 'sprites/regular.svg' file to my server and:
<svg class = 'fa-svg-icon' title = 'my failed attempt to overwrite the title'>
<use xlink:href="icons/font-awesome/sprites/solid.svg#user"></use>
</svg>
The '.fa-svg-icon' class, for the sake of completeness:
.fa-svg-icon{
width:1em;
height:1em;
}
What I see, when hoovering the image, is a title "User" showing up. That's is because this is what is declared in the svg file, in "our" fragment:
<symbol id="user" viewBox="0 0 448 512">
<title id="user-title">User</title>
<path d="M224 256c70.7 0 ......"></path>
</symbol>
I tried adding title = 'something' to both the svg element, and the <use> one, but nothing works o.O
This happens on both FX and Chrome.
Edit 1: I made a test page: https://kpion.github.io/stuff/font-awesome-issue/
I'm pretty sure I'm missing something obvious here, because apparently I'm the only one in this world having this problem 😮 Or google is broken. One or the other :)
Edit 2:
To answer a question from comments, here is what my dev tools -> elements in chrome shows (after 'importing' the svg symbol):
<svg class="fa-svg-icon" title="my -not-working-title for user">
<use xlink:href="icons/font-awesome/sprites/solid.svg#user" title="blah - doesn't work either"></use>
#shadow-root (closed)
<svg id="user" viewBox="0 0 448 512" width="100%" height="100%">
<title id="user-title">User</title>
<path d="M224 256c70.7 0 128-57.3 128-..."></path>
</svg>
</svg>
And no, it does not change when hovering, please bear in mind there is no js involved, either mine or from font awesome.
I'm not able to recreate your code environment in a fiddle to try it myself, but you may try the following CSS code, to get the pointer-events only when the icon has a link:
.fa-svg-icon{
pointer-events: none;
}
.fa-svg-icon a {
pointer-events: auto;
}

How to use SVG for icons properly / best practices

I'm looking for an advice about work with SVG images, respectively icons.
What I've tried
If I'm using PNG icons, the CSS is like:
elem:after {background: url('icon.png'); ...}
I've tried the same way
elem:after {background: url('icon.svg'); ...}
but it isn't what I'm looking for. This solution lose advantages of SVG icons (they're small and blured, fuzzy).
What I have
I've generated this code online using Icomoon app.
<svg aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="icon-love" viewBox="0 0 33 32">
<title>love</title>
<path d="M32.916 15.597c0.604-0.66 ..."></path>
</symbol>
<symbol id="icon-shop" viewBox="0 0 34 32">
<title>shop</title>
<path d="M17.148 27.977c-..."></path>
</symbol>
</defs>
</svg>
<svg class="icon icon-shop">
<use xlink:href="#icon-shop"></use>
</svg>
My question
Is it semantically correct to put icons into HTML markup, instead of putting them into CSS?
Across whole website there is about 60 icons, any unique, any are repeated more times. Generated markup above has about 50kB.
Can I put all icons into general layout template and load them all in whole website, or include just icons I use in the page?
Here I've found How to change color of SVG image using CSS (jQuery SVG image replacement)? (3 years ago), but don't know if it's the right way.
How to cache SVG properly? Is it posible when I use it like I'm writing above?
I read many articles, but not definitely sure how to do that best way.
Thanks for advice.

Most efficient way to add inline SVG?

I have a webpage with lots of SVG icons that will be in the HTML. Rather than include them as an IMG tag and possibly slow the page with those HTTP requests, I'm placing the SVG code like this:
<svg xmlns="http://www.w3.org/2000/svg" width="9" height="9" viewBox="0 0 9 9"><path fill="#C5C2BD" fill-rule="nonzero" d="M4.5 3.435L1.286.22A...LOTS OF CODE HERE..."/></svg>
Note: where it says "lots of code here", there is a huge string of numbers/letters that make up the path for this SVG.
The issue this creates is it's very ugly when not easily maintainable in the HTML (to the point where my editor bogs own because these SVG strings are so long).
Is there a cleaner, simpler way to include these SVG icons in my HTML while still eliminating the extra HTTP requests?
Thanks for your time.
The most proven way is to collect all the icons SVG in one file - the sprite SVG
Creating a Sprite and connecting it to HTML
The action plan is as follows:
Creating a Sprite
Connecting it to HTML
Calling SVG images from the sprite
Styling icons
Creating a Sprite
The main task of the sprite is to be a repository for icons that before the call to a specific place HTML pages should be invisible.
To do this,the code for each icon is wrapped with <symbol id =" .. "> ... </ symbol> tags with a unique identifier, which will be followed by the <use>
Template Sprite:
<div id="container">
<svg version="1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 126 126" >
<symbol id="picasa">
<path d="M113.5 57.... 72.8z" />
</symbol>
<symbol id="wordpress" viewBox="0 0 126 126">
<path stroke ="black" d="M113.5 57.7l-8.5-11.4 .. 86.1 62.9z"/>
</symbol>
<symbol id="twitter">
<path d="M113.5 57.6l-8.5-11.4.... 7.4-2.4V85.4z"/>
</symbol>
<symbol id="apple">
<path d="M113.5 57.7l-8.5-11.4... 78.5 78.7 82z"/>
</symbol>
</div>
As you can see, all <path> attributes are removed for later styling of icons from an external CSS file.
Adding a sprite file to HTML
There are several ways to add SVG files to HTML, but the most reliable way is to add it using the <object>
<object type="image/svg+xml" data="Sprite.svg" width="200" height="200">
Your browser does not support SVG
</object>
Adding icons from the sprite
<div id="container">
<svg viewBox="0 0 126 126" >
<use xlink:href="#apple"></use>
</svg>
</div>
The viewBox attributes should be like thesvg icon or change the scaling inside HTML if necessary.
Icons as links
For this, in SVG, unlike HTML, its own form of record
<svg viewBox="0 0 126 126" >
<a xlink:href="https://www.apple.com/ru/"><use xlink:href="#apple"></use></a>
</svg>
Styling icons
When using the <use> command, the icon falls into the shadow DOM and its attributes behave strangely, - there is an icon of the icon, but it can not be controlled from the outside.
In addition, the icon attributes for example: style = "fill: gray; stroke: crimson;" have the highest priority. Therefore, we deleted these attributes. see the example of the sprite above.
To solve the problem of inheritance of parental properties by objects in shadow DOM, you must use forced inheritance
svg path{
fill:inherit;
stroke:inherit;
}
and then to the icons you can already apply CSS rules from the external table
svg path:hover{fill:yellow;}
#picasa{fill:black;}
#apple{fill:purple;}
#twitter{fill:black;}
#wordpress{fill:blue;}
If you go for the first time along this path, then inevitably there will be many questions.
Ask, do not be shy.
All sooner or later it turned out to be done and customized sprite.
The main recommendation is to do everything yourself manually for the first time, to understand how it is arranged, and then you can already openly use special utilities to automatically create sprites.
You should convert all your .svg files to one font file, here is how:
How to convert .svg files to a font?

How to use SVG clipPathUnits="objectBoundingBox"

I'm trying to use an SVG to mask an image.
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width=500 height=300>
<defs>
<clipPath id="clip">
<path d="M2.16,1.363h23.699c13.739,0,24.899,10.74,24.899,23.999s-11.16...
<img width="500" style="clip-path: url(#clip);-webkit-clip-path: url(#clip);" />
It works but I want the clipPath size to match the media. In Chrome I can control the size of the clipPath with CSS but in FF the clipPath stays small. In Safari with one asset nothing appears and with another it appears off-center.
I've read other questions that talk about using clipPathUnits:
<clipPath id="clip" clipPathUnits="objectBoundingBox">
But I cannot get this to work at all. Apparently it expects the paths units to be decimals... but my shape is too complicated to write by hand and I don't know of any design software that supports that format.
Update
Following Robert's comments, I tried adding a CSS transform to the clipPath to "translate" the units...
#clip {
-webkit-transform:scale(0.004195862879,0.005554321262);
transform:scale(0.004195862879,0.005554321262);
}
This allowed objectBoundingBox to work as expected in Chrome. But still no luck with Safari or FF. It appears that Safari still renders the clipPath outside the the element to be clipped... making it invisible. FF developer tools make it less clear where it's placing the clipPath.
Run into this problem right now and found the solution. Thanks #RobertLongson for mentioning that when you applying clipPathUnits="objectBoundingBox", you should be sure that all of your coordinates are between 0 and 1. It means, that if you have, for example, a circle
<svg viewBox="0 0 20 20">
<defs>
<clipPath id="clip">
<circle cx="10" cy="10" r="10" />
</clipPath>
</defs>
</svg>
then with clipPathUnits="objectBoundingBox" it should looks like this:
<svg>
<defs>
<clipPath id="clip" clipPathUnits="objectBoundingBox">
<circle cx="0.5" cy="0.5" r="0.5" />
</clipPath>
</defs>
</svg>
In case of complex paths, I found one solution. All you need is an application for vector images editing. For Linux it could be Gravit Designer (I used it and it worked).
Create new file of size 1x1
Open you svg that contains your complex path without clipPath tag. If you don't have such source, then you can simply create new text file, paste your <path .../> into <svg>...</svg> and save it with *.svg extension, and then open it in your application (Gravit Designer).
Copy your complex path from opened svg and paste it into created new file (1x1).
Turn off "Preserve aspect ratio" (or something simillar) function if it enabled
Set this parameters for your complex path: Width: 1px, Height: 1px, Top: 0px, Left: 0px.
Save this 1x1 file as svg.
Open this svg file in text editor and copy the value of d attribute.
Now you have your complex path in relative coordinates. Set it into you clip
...
<clipPath id="clip" clipPathUnits="objectBoundingBox">
<path d="/* your copied value */" />
</clipPath>
...
Done! Now you can enjoy your responsive clip path without any additional transformations or other workarounds.
I know, it looks too complicated, but it really will be done in approximately 2 minutes (if you have vector image editing software installed).
Hope this helps someone despite of question was asked 1.5 years ago :)
I was using Adobe Illustrator to try and shrink my vectors down to 1px by 1px, and it wasn't capturing all of the coordinates correctly. I instead found this awesome online tool that will convert SVG path coordinates to CSS clip-path friendly coordinates relative to a "1" unit without needing a vector editing program. The quality is much better than Illustrator and easier to use. 🙌
https://yoksel.github.io/relative-clip-path/
I got a similar problem, I solved it like this:
I transfer the svg file to figma (drag and drop)
I align to a square 100 by 100 (without preserving the sides)
export the file as svg
I upload the file to the service https://betravis.github.io/shape-tools/path-to-polygon/ (not advertising, you can use something else)
I get a clean polygon as a percentage, which can be used in clip-path;
example:
.elem-to-path{
clip-path: polygon(89.758% 99.457%, 67.178% 99.862%, 39.660% 99.621%, 7.748% 98.475%, 5.924% 98.004%, 5.742% 97.859%, 4.348% 92.036%, 1.807% 80.831%, 0.174% 36.869%, 1.052% 1.541%, 1.301% 1.012%, 2.237% 0.790%, 62.832% 0.559%, 78.860% 0.810%, 88.832% 0.810%, 96.913% 1.195%, 98.441% 3.283%, 99.616% 24.932%, 98.813% 86.202%, 96.703% 95.116%, 89.758% 99.457%)}