I got motivated by Bytes #293 main thing about HTML. Their motivation came from State of HTML 2023 survey (and money... but let's leave the sponsored section from mailing).
It appears that <svg>
elements are notable pain points in their daily work. It was brought in the "Content section"
Entry point
If you are not familiar with this stuff at all, I can help with a bunch of links for the start. After that, nothing will change but you will know something more about svg
¯\_(ツ)_/¯
- https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Adding_vector_graphics_to_the_Web
- https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Introduction
- SVG tutorial from MDN: https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Getting_Started
Tl;DR
SVG elements are vector graphic elements. This can be understood as a bunch of geometric figures represented in characters. You can create one for example with Adobe Illustrator or Inkscape as a free alternative.
Starting point
From people's answers, we can see a few patterns. I won't pick any specific one but I will try to share some knowledge around those topics so you can find here some tips&tricks, sharing experience and other common knowledge around this topic.
I will skip the SVG sprites topic like this example. It's a separate topic and is not very connected to the svg
tag. I can just agree that you could still find a few rare use cases for them.
File syntax
A proper SVG file should be a representation of the mentioned geometric figures. It's very easy to check if our icon is prepared in the right format by the designer or website where you are taking it.
Wrong example:
<svg ...><image link:href="data:image/png;base64 .../>
Expected syntax:
<svg><g><path r=""/><circle /></g></svg>
During my career, I stepped into this a few times. It's worth to remember that. It happens when you take raster graphics, import them to a vector graphic program and try to export them in .svg
extension. It does not magically convert your image to a vector graphic.
Notable SVG tags
I can present you a cheat sheet for most generic svg elements.
Tag | Description | Animated attributes |
---|---|---|
<g> |
used for grouping elements | - |
<path /> |
main tag for your actual icon. Avoid modifying the d="" attribute on your own 😬 It is possible but be careful with that. Grab a link with an explanation for each character group. To animate your path use animateMotion tag |
- |
<rect /> |
Basic rectangle shape in SVG. Simple as that | ✅ |
<line /> |
Simple as that. The line between two points. Don't mistake with the stroke attribute! It can also be used on the line tag. |
✅ |
<defs /> |
You can think about it as an enhancement version of the g tag but it keeps the elements inside invisible for later use with, let's guess it... use tag |
- |
<circle /> |
Next basic shape. Be careful as the size is defined by radius and you position your circle with cx and cy attributes. |
✅ |
<polygon /> |
Let's call it a custom shape. They are connected straight lines where the last point connects to the first point. | ✅ |
<polyline /> |
Very similar to the previous one but with a notable difference. The last point doesn't need to be connected to the first point. | ✅ |
Please note those are only a few selected SVG elements. I have just described the most common ones. There are a lot more available for you to use. A full list can be found here.
Sizing and positioning
As we are now familiar with some of the common svg tags we can go into the next paragraph of modifying the base parameters of our icon.
Let's say we work with a young designer. He/She doesn't fully know how to prepare things for the web yet as you do. As a result, you sometimes get slightly different sizes of an icon with missing cut padding on the sides.
good one | wrong one |
---|---|
Classic case. I know we should have a quick call with a designer and clarify this but we want to learn some svg here. We are not doing that in this scenario. We can fix it ourselves.
From the code perspective, it's all about attributes. The original SVG cross file looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="93.582886"
height="93.582893"
viewBox="0 0 24.760471 24.760474"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1"
transform="translate(-13.964461,-14.49055)">
<rect
style="display:inline;fill:#000000;stroke-width:0.257843"
id="rect2"
width="31.409456"
height="3.175"
x="-16.658115"
y="36.031288"
transform="rotate(-45.885349)" />
<rect
style="display:inline;fill:#000000;stroke-width:0.257843"
id="rect3"
width="31.409456"
height="3.175"
x="21.914061"
y="-0.63411272"
transform="rotate(44.114651)" />
</g>
</svg>
This is our raw svg exported from Inkscape. There are a few other things to fix, bear with me so we can fix them.
We know that 200px image is our goal. Here we have some ugly width/height values on the main svg tag.
Simply:
<svg
- width="93.582886"
+ width="200"
- height="93.582893"
+ height="200"
Results in:
Almost what we want! Let's dig further into how we can preserve padding as in the first icon.
We have 2 options. We can keep the same viewBox
value or we can adjust each width
, height
, x
and y
value on every nested tag.
The first option seems simple and it works for us quite well but there might be a catch. In my case, I had this weird transform
attribute on my group tag.
<g id="layer1" transform="translate(-13.964461,-14.49055)">
After removing it the result is pretty good now:
step1 - add viewBox
|
step2 - remove transform
|
---|---|
Universal width/height
We don't need to define width and height upfront to our icon. We can avoid overwriting it each time from 200px to 32px - let's treat it as an implementation detail. In that case, we can update our icon as follows:
<svg
- width="200"
- height="200"
viewBox="0 0 50 50"
Now our icon size is adjustable. Please note that it's vector graphics so we don't lose icon quality after resizing.
So why is viewBox
so important?
It's the key when we want to correctly scale or set a certain position to our icon. It has 4 arguments viewBox="min-x min-y width height"
.
For example, viewBox="0 0 100 100" means the top-left corner is at (0, 0) and the viewBox is 100 units wide and 100 units high.
That means you have to adjust your icon x
and y
attributes after changing viewBox
as they are set according to their value.
Managing colors and fill
Different icons allow different fill
. Of course, it can be as easy as adding a color
style but that's a rookie-level type of code.
Real developers are adding fill-rule="evenodd"
to the main element and deleting all other manual fill
attributes from tags 😎
You may wonder why.
I will provide the shortest possible answer to this. To freely customize our icon colours with one line of code straight from our IDE.
How is that possible you might ask.
If you are dealing with a simple icon from your company's design system you should be able to import only one icon. Modifiers or duplications in size or colour are not necessary.
From a code perspective, you just have to add fill
on the svg
element like you would normally do with for example colour attribute.
svg#close {
fill: #ddd;
}
There is a catch for evenodd
value which applies when you are drawing polygons or paths. It will fill only the inside of crossing path segments.
Consider analyzing this example in case you struggle with it.
So in the case of our cross icon, it will result in something like this:
Optimizing
Before we publish our icon to the web (or right after we download it from Figma) we should ensure it's properly optimized. This one is very important. I have caught and been caught way too many times during code review on this last mile.
SVGO (or SVGR for react) is making it a very easy process. Behind the scenes, it mostly deletes all unnecessary stuff for us.
In general, it is a go-to tool. Either you want to integrate it with your bundler or use it from CLI. It's ready to use right after installation as we get default preset configured out of the box.
Note: You can overwrite the default preset if you really want to. Check configuration in the project README and plugin list in docs
Result
The final icon code looks like this:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><g fill-rule="evenodd"><path d="M9 24h32v3H9z" style="display:inline;stroke-width:.25" transform="rotate(135)" transform-origin="center"/><path d="M8 24h32v3H8z" style="display:inline;stroke-width:.25" transform="rotate(45)" transform-origin="center"/></g></svg>
We have reduced the size from 871 bytes with raw icon exported from inkspace to only 322 bytes.
Pain with animations
The last topic that is bitter-sweet for me. It was brought in the survey answers a couple of times as well. I don't know better ways to do it except using Lottie or manually animating elements of SVG in CSS. The second option is absolute torture when you have more than 5 elements to animate.
Of course, it is possible and we can do it but being honest, there are more interesting things to do 🙈
Suppose you are still curious about how to do it. I'm leaving a link to a great article here. It should convince you that it requires patience and takes more than 5 minutes. I wonder if an AI bot can help you with that! If yes, maybe it's a bit less painful.
That's it for now.
Hopefully, you took something from this post and have a nice day 🍀
Top comments (0)