DEV Community

Cover image for SVG hacks for you
Filip Kudla
Filip Kudla

Posted on

SVG hacks for you

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"

content section with 17% of svg answers (top)

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
¯\_(ツ)_/¯

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 .../>
Enter fullscreen mode Exit fullscreen mode

Expected syntax:

<svg><g><path r=""/><circle /></g></svg>
Enter fullscreen mode Exit fullscreen mode

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
Image description Image description

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>
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

Results in:

Image description

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)">
Enter fullscreen mode Exit fullscreen mode

After removing it the result is pretty good now:

step1 - add viewBox step2 - remove transform
Image description Image description

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"
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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.

Mozilla playground with polygon evenodd example

So in the case of our cross icon, it will result in something like this:

demo with updated fill rule to evenodd value

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>
Enter fullscreen mode Exit fullscreen mode

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)