Small introduction to SVG

For this first post, I would like to take a step back and have a look at some SVG basis. Understanding the basis of the tools we use everyday let us be more effective and creative: to break the rule, you have to know them first! Anyway, I felt like I could use a refresh and maybe it can also be useful to someone else! :)

SVG is a very particular format for 2 main reasons:

For more details, you can check out MDN Documentation page SVG: Scalable Vector Graphics

Now, let's have a look at how to integrate SVG documents into your dev workflow.

Most of the time, the developer is provided with either a raw SVG file or even have to make the export himself from the design app, Sketch, Figma, Adobe XD or Illustrator. In the latter case, we often need to extract part of the graphic, from a high fidelity mockup or from a icon collection board.

At this point, we need to make some contextual decisions: what will be the viewport of you image, how is it supposed to scale, and how flexible it must be ?

Layout

ViewBox

Check the documentation of the viewBox attribute on MDN

This attribute is not exactly mandatory but there is only very few use cases where you would not want to set it (check Lea Verou's talk in the resources at the end of this article). If no unit is provided, the default is px or user units.

I am referring here to the viewBox attribute on the svg element, but it is interesting to point out, that there are other "non-root" elements which can also make use of it, such as view, marker, pattern and symbol. Those elements are useful to define reusable part.

On a integration task specification, we will often get size requirements for our visual elements. The viewBox has no impact on this constraint. You can choose any suitable value, but most of the time you will want it to respect the aspect ratio of the final result. The viewBox defines which part of the SVG canvas can be visible.

It is expressed as follows:

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">...</svg>

We have 4 values, min-x min-y width height.

min-x and min-y
They describe the origin of the SVG grid. Most of the time we want to keep them at 0 as it makes math easier, but this can still be useful for animation or SVG sprites. Sara's articles - linked at the end of this page - explore this aspect in depth.

width and height
They are pretty much what we can expect, with one important point: they are unitless, they are not related to any pixel convention. That said, they often use the same values as the expected size in the final document. This is purely for convenience: you should choose the values for readability and making the math inside the SVG easier, in case you plan to animate the svg later. In most cases, for simple elements (ex: icons) you should keep the coordinate system in sync with the design expectation. The lowest values that avoid floating numbers on the inner elements are optimal.

Height and Width:

This pair of attributes is referred to as viewport, not to confuse with viewBox or the browser window viewport. The height and width attributes of the svg element define how the viewport will be sized on the screen: the size it will be drawn to. Contrary to the height and width values of the viewBox attribute, we are dealing with units here. You do not have to use one, in which case it will most probably resolve to px. Other possible values are the same as the one defined in CSS (em, ex, px, pt, pc, cm, mm, in and percentages).
Those attributes are not mandatory and will default to the value auto, which is equivalent to 100% (when viewBox is defined, otherwise we have a special case, described later in this article).

To keep things generic you will probably skip those values when working with components and rely on CSS or props to suit your need depending on the context.

preserveAspectRatio

MDN, preserveAspectRatio
If you have read the points above, you may have realized that it is possible that the ratio expressed by the viewBox is different from the one defined by the given attributes height and width . preserveAspectRatio let you define how the viewBox will fill the viewport (height and width, remember). It is kind of the older brother of the CSS object-fit property: the syntax is a bit more rough, but also let you manage the origin of the applied scaling transformation .

<svg preserveAspectRatio="<align> [<meetOrSlice>]">...</svg>

<align> is a string value composed of an alignment type (Min, Mid, Max) for both axis (x, Y) like the following examples: xMidYMax or xMaxYMin or the value none.

There is little chance you need to use this attribute if you keep your SVG viewport in sync with its viewBox. However, in more complex scenario, it could become handy to use them.

Points of cautions:

SVG are scalable but it does not solve everything.

the above container is `resizable`

If you specify the same values directly, the svg will take up all the available space in the container: The default values for height and width work in this situation as if they were 100%.

Going further with Layout

I hope the above part was sufficient to get a simple understanding of how SVG can behave. However, I have only scratched the surface. Fortunately, Sara Soueidan did an amazing job explaining the subject in detail in her article series "Understanding SVG Coordinate Systems and Transformations" (all articles are listed separately at the end of this page).

Manual optimizations

Now that we covered some basis, let's dive into SVG syntax.
The asset source plays a important role in the state of your file when you receive it.
Each editor has its own quirks. I am thinking of Inkscape in particular, where SVG is the raw format for working files. That may sound great, but it makes the SVG bloated with Inkscape-specific shenanigans (private namespaces). Other artefacts may have been added by the designer for convenience, but are no longer useful for production-ready assets.

Namespaces

SVG

 <svg xmlns="http://www.w3.org/2000/svg">...</svg>

SVG is a namespaced XML format. This is an important part of the root element, as it defines what functionality will be usable.
The bare minimum namespace you should have is the SVG namespace itself. Just like HTML doctype, this namespace allows the engine to know how the syntax should be interpreted. There is only one value for it at the moment, therefore it has been made optional for inline SVG.
This is still mandatory if consumed in another way, so I would recommend keeping it. If you really want it and are sure it will always be used inline, you can remove it and save a few bytes.

SVG can do more than just static graphics so you may encounter other namespaces like xlink. It can be used to add hypertext features within SVG itself.

  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<a xlink:href="https://google.com">google</a>
</svg>

As you can see in this example, we first declare the xlink namespace on the root element and then we can use the prefixed attributes that belong to it. SVG2 rejects the use of this specific namespace, but support can still be problematic: be careful in such case. Chris Coyier mentioned this briefly in his "On xlink:href being deprecated in SVG" article.

xml-events and others

Since XML is extensible (eXtensible Markup Language), you will encounter a large variety of exotic namespaces on your journey. I will mention only one last one, since it has a lot to do with interaction: xml-events. If you want to know more, you can find the spec as a starting point.

Useless elements

The <g> does not draw anything on the screen, but it is still common. There are several reasons for this situation:

Depending on the situation, you can either optimize or remove these elements.

Ignored attributes

Some artifacts may be left over from the app or a fellow developer moving fragments around.
Let's take a look at the following elements:

<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M4.5 0H5.5V1H14.5V0H15.5V1H20V5V6V16H0V6V5V1H4.5V0ZM14.5 2V3H15.5V2H19V5H1.00263V2H4.5V3H5.5V2H14.5ZM1.00263 6V15H19V6H1.00263Z"
/>

</svg>

<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M4.5 0H5.5V1H14.5V0H15.5V1H20V5V6V16H0V6V5V1H4.5V0ZM14.5 2V3H15.5V2H19V5H1.00263V2H4.5V3H5.5V2H14.5ZM1.00263 6V15H19V6H1.00263Z"
/>

</svg>

<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 20 20" fill="currentColor">
<path
d="M4.5 0H5.5V1H14.5V0H15.5V1H20V5V6V16H0V6V5V1H4.5V0ZM14.5 2V3H15.5V2H19V5H1.00263V2H4.5V3H5.5V2H14.5ZM1.00263 6V15H19V6H1.00263Z"
/>

</svg>

Can you spot the issue? Yep, that's right, clip-rule has no effect on this calendar icon. However, you can see on the third icon that the missing fill-rule attribute was extruding the shape: Be careful, as this could be easy to miss.

Inheritance conflict

<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
<path
d="M4.5 0H5.5V1H14.5V0H15.5V1H20V5V6V16H0V6V5V1H4.5V0ZM14.5 2V3H15.5V2H19V5H1.00263V2H4.5V3H5.5V2H14.5ZM1.00263 6V15H19V6H1.00263Z"
fill="#333333"
/>

</svg>

In this example, we have a SVG root element that uses the fill attribute with the currentColor value. With this CSS value, you can use the value of the color property inherited from any previously set rule. Very convenient to adapt your icon to the surrounding text. The above markup overwrites the attribute on the path tag, rendering the former ineffective. In this situation, you should probably use the fill value only once on the SVG tag with the appropriate value.

Caution: currentColor will only works for inline SVG, if used in background-image or a <img /> tag, currentColor won't be accessible and will be set to black. This is easy to miss because the color text ist often also black or a dark shade of gray.

Useless Clipping

<svg width="80" height="80" viewBox="0 0 20 20" fill="currentColor">
<g clipPath="url(#clip0_471_37763)">
<path
d="M14.8278 3.71094C14.3637 4.1239 14.0052 4.6659 13.8151 5.30283C13.4056 6.67501 13.8945 8.0969 14.9414 8.9447L12.5509 16.9549C12.2663 17.9087 12.8087 18.9075 13.7625 19.1858C14.7163 19.4642 15.7202 18.9166 16.0049 17.9628L16.3386 16.8447L16.6603 15.7667L18.5607 9.39863C19.4407 8.99718 20.1493 8.23355 20.4464 7.23803C20.9907 5.41392 19.9476 3.50199 18.1164 2.9676C17.4844 2.78317 16.8412 2.78692 16.2473 2.94592L16.4501 3.31292L17.8813 5.84925L17.4427 6.64917L16.508 6.60498L15.0732 4.15497L14.8278 3.71094ZM17.8683 8.68462C18.6579 8.44319 19.3145 7.82489 19.5663 6.9812C19.9647 5.64634 19.2021 4.24113 17.8538 3.84764C17.827 3.83984 17.8002 3.83249 17.7735 3.8256L18.8061 5.60961L18.935 5.83223L18.8107 6.05893L18.1089 7.33884L17.97 7.59214L17.6819 7.57852L16.2101 7.50895L15.9613 7.49718L15.8368 7.28241L14.7476 5.40282C14.7285 5.45418 14.7111 5.50647 14.6952 5.55966C14.3435 6.73794 14.8965 7.97104 15.9584 8.51473L15.9501 8.54239L16.0166 8.54732L13.431 17.2117C13.2914 17.6794 13.5574 18.1693 14.0251 18.3058C14.4929 18.4423 14.9852 18.1738 15.1248 17.706L15.7801 15.5103L17.0955 11.0913L17.7802 8.79121C17.7872 8.75376 17.7918 8.71633 17.7939 8.6791L17.8683 8.68462Z"
/>

<path
d="M13.4737 3.651C13.6054 3.36038 13.7674 3.09053 13.9546 2.84406C13.013 2.63484 11.8734 2.5 10.5 2.5C4.37434 2.5 2.9 5.18235 2.9 5.18235V7.86471C2.9 7.86471 1.60082 7.86414 1.475 7.86471C1.32614 7.86537 1 7.97726 1 8.31347V17.7H12.101C11.9916 17.4191 11.9327 17.1171 11.9319 16.8081C8.5082 16.8075 4.46306 16.8063 4.325 16.8061C4.2288 16.806 3.85 16.7296 3.85 16.3608V10.9961C3.85 10.6871 4.15108 10.5479 4.325 10.5473C4.44412 10.5469 10.1283 10.5477 13.7432 10.5483L14.0183 9.65534C10.4386 9.65502 4.49289 9.65323 4.325 9.65302C4.19482 9.65285 3.85 9.74589 3.85 10.1001V5.43046C3.85 5.43046 5.22455 3.39248 10.5 3.39248C11.6756 3.39248 12.6575 3.49369 13.4737 3.651ZM18.1 13.4028L16.8347 17.5093C16.8147 17.5743 16.7923 17.6379 16.7676 17.7H20V8.87849C19.8047 9.01539 19.5983 9.1356 19.3832 9.23788L19.05 10.3194V16.8056H18.1V13.4028ZM1.94999 8.7585H2.9V16.8056H1.94999V8.7585Z"
/>

</g>
<defs>
<clipPath id="clip0_471_37763">
<rect width="20" height="20" fill="white" transform="matrix(-1 0 0 1 20.8799 0.803711)" />
</clipPath>
</defs>
</svg>

<!-- ////////// -->

<svg width="80" height="80" viewBox="0 0 20 20" fill="currentColor">
<path
d="M14.8278 3.71094C14.3637 4.1239 14.0052 4.6659 13.8151 5.30283C13.4056 6.67501 13.8945 8.0969 14.9414 8.9447L12.5509 16.9549C12.2663 17.9087 12.8087 18.9075 13.7625 19.1858C14.7163 19.4642 15.7202 18.9166 16.0049 17.9628L16.3386 16.8447L16.6603 15.7667L18.5607 9.39863C19.4407 8.99718 20.1493 8.23355 20.4464 7.23803C20.9907 5.41392 19.9476 3.50199 18.1164 2.9676C17.4844 2.78317 16.8412 2.78692 16.2473 2.94592L16.4501 3.31292L17.8813 5.84925L17.4427 6.64917L16.508 6.60498L15.0732 4.15497L14.8278 3.71094ZM17.8683 8.68462C18.6579 8.44319 19.3145 7.82489 19.5663 6.9812C19.9647 5.64634 19.2021 4.24113 17.8538 3.84764C17.827 3.83984 17.8002 3.83249 17.7735 3.8256L18.8061 5.60961L18.935 5.83223L18.8107 6.05893L18.1089 7.33884L17.97 7.59214L17.6819 7.57852L16.2101 7.50895L15.9613 7.49718L15.8368 7.28241L14.7476 5.40282C14.7285 5.45418 14.7111 5.50647 14.6952 5.55966C14.3435 6.73794 14.8965 7.97104 15.9584 8.51473L15.9501 8.54239L16.0166 8.54732L13.431 17.2117C13.2914 17.6794 13.5574 18.1693 14.0251 18.3058C14.4929 18.4423 14.9852 18.1738 15.1248 17.706L15.7801 15.5103L17.0955 11.0913L17.7802 8.79121C17.7872 8.75376 17.7918 8.71633 17.7939 8.6791L17.8683 8.68462Z"
/>

<path
d="M13.4737 3.651C13.6054 3.36038 13.7674 3.09053 13.9546 2.84406C13.013 2.63484 11.8734 2.5 10.5 2.5C4.37434 2.5 2.9 5.18235 2.9 5.18235V7.86471C2.9 7.86471 1.60082 7.86414 1.475 7.86471C1.32614 7.86537 1 7.97726 1 8.31347V17.7H12.101C11.9916 17.4191 11.9327 17.1171 11.9319 16.8081C8.5082 16.8075 4.46306 16.8063 4.325 16.8061C4.2288 16.806 3.85 16.7296 3.85 16.3608V10.9961C3.85 10.6871 4.15108 10.5479 4.325 10.5473C4.44412 10.5469 10.1283 10.5477 13.7432 10.5483L14.0183 9.65534C10.4386 9.65502 4.49289 9.65323 4.325 9.65302C4.19482 9.65285 3.85 9.74589 3.85 10.1001V5.43046C3.85 5.43046 5.22455 3.39248 10.5 3.39248C11.6756 3.39248 12.6575 3.49369 13.4737 3.651ZM18.1 13.4028L16.8347 17.5093C16.8147 17.5743 16.7923 17.6379 16.7676 17.7H20V8.87849C19.8047 9.01539 19.5983 9.1356 19.3832 9.23788L19.05 10.3194V16.8056H18.1V13.4028ZM1.94999 8.7585H2.9V16.8056H1.94999V8.7585Z"
/>

</svg>

We probably have this situation because the designer defined the clipping in a larger document. Extracted, the clipping is now also handled by the viewBox attribute of the <svg> tag. Let's verify this hypothesis by slightly reducing the clipping rectangle area:

If you increase the width of the viewBox a bit, you can see that the clipping was a bit extreme: a reasonable resizing of the element to fit the whole image into the viewBox would probably have given a better result. Unfortunately, with the original size of 20 x 20, this was not identified during the review.

Automatic or programmatic optimization

SVGO

Available as a cli and as node.js module on npm, svgo let you optimize svg, including math and transformation with several options, from light to heavy gain. However, some changes can be destructive, so be cautious and check the result!

svgo on github

SVGOMG

SVGO's missing GUI interface, SVGOMG, was built by Jake Archibald. It gives you direct feedback on the effects of the chosen SVGO options chosen and a neat way to compare the original and the optimized graphic.

SVGR

React tool that can be used manually or at build time
react-svgr.com

Accessibility

Since SVG is a structured document, there are many accessibility enhancement options to consider. As always, there are several articles that cover this topic in detail.

To lay the groundwork, you can start by treating SVG as images. This means that you should either provide an alternative description or indicate that it is a decorative element.

Conclusion

That's it, We have gone through a large overview of the points to consider when working with SVG, and we have some lead and link to go further when necessary. To sum up:

Next time, maybe we can explore how we can handle animation for SVG Icon, let me know if you are interested or if you would like to see something else.
If your are still there, thank you for reading. It was a very nice exercise for me and I hope It was interesting and not to difficult to read. Feel free to reach if you wish to share any feedback, and see you next time!

Antoine Corre,

Other resources

Specification and Documentation

Deep look at the Core

Clever tricks

Video and podcast

Compatibility

Performance

Day to Day SVG usecases

Accessibility


arrow_upward