Mariana Beldi – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Tue, 17 Jan 2023 13:51:30 +0000 en-US hourly 1 https://wordpress.org/?v=6.2.2 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 Mariana Beldi – CSS-Tricks https://css-tricks.com 32 32 45537868 6 Common SVG Fails (and How to Fix Them) https://css-tricks.com/6-common-svg-fails-and-how-to-fix-them/ https://css-tricks.com/6-common-svg-fails-and-how-to-fix-them/#comments Tue, 17 Jan 2023 13:51:20 +0000 https://css-tricks.com/?p=376352 Someone recently asked me how I approach debugging inline SVGs. Because it is part of the DOM, we can inspect any inline SVG in any browser DevTools. And because of that, we have the ability to scope things out and …


6 Common SVG Fails (and How to Fix Them) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Someone recently asked me how I approach debugging inline SVGs. Because it is part of the DOM, we can inspect any inline SVG in any browser DevTools. And because of that, we have the ability to scope things out and uncover any potential issues or opportunities to optimize the SVG.

But sometimes, we can’t even see our SVGs at all. In those cases, there are six specific things that I look for when I’m debugging.

1. The viewBox values

The viewBox is a common point of confusion when working with SVG. It’s technically fine to use inline SVG without it, but we would lose one of its most significant benefits: scaling with the container. At the same time, it can work against us when improperly configured, resulting in unwanted clipping.

The elements are there when they’re clipped — they’re just in a part of the coordinate system that we don’t see. If we were to open the file in some graphics editing program, it might look like this:

Cat line art with part of the drawing outside the artwork area in Illustrator.
Screenshot of SVG opened in Illustrator.

The easiest way to fix this? Add overflow="visible" to the SVG, whether it’s in our stylesheet, inline on the style attribute or directly as an SVG presentation attribute. But if we also apply a background-color to the SVG or if we have other elements around it, things might look a little bit off. In this case, the best option will be to edit the viewBox to show that part of the coordinate system that was hidden:

Demo applying overflow="hidden" and editing the viewBox.

There are a few additional things about the viewBox that are worth covering while we’re on the topic:

How does the viewBox work?

SVG is an infinite canvas, but we can control what we see and how we see it through the viewport and the viewBox.

The viewport is a window frame on the infinite canvas. Its dimensions are defined by width and height attributes, or in CSS with the corresponding width and height properties. We can specify any length unit we want, but if we provide unitless numbers, they default to pixels.

The viewBox is defined by four values. The first two are the starting point at the upper-left corner (x and y values, negative numbers allowed). I’m editing these to reframe the image. The last two are the width and height of the coordinate system inside the viewport — this is where we can edit the scale of the grid (which we’ll get into in the section on Zooming).

Here’s simplified markup showing the SVG viewBox and the width and height attributes both set on the <svg>:

<svg viewBox="0 0 700 700" width="700" height="700">
  <!-- etc. -->
</svg>

Reframing

So, this:

<svg viewBox="0 0 700 700">

…maps to this:

<svg viewBox="start-x-axis start-y-axis width height">

The viewport we see starts where 0 on the x-axis and 0 on the y-axis meet.

By changing this:

<svg viewBox="0 0 700 700">

…to this:

<svg viewBox="300 200 700 700">

…the width and height remain the same (700 units each), but the start of the coordinate system is now at the 300 point on the x-axis and 200 on the y-axis.

In the following video I’m adding a red <circle> to the SVG with its center at the 300 point on the x-axis and 200 on the y-axis. Notice how changing the viewBox coordinates to the same values also changes the circle’s placement to the upper-left corner of the frame while the rendered size of the SVG remains the same (700×700). All I did was “reframe” things with the viewBox.

Zooming

We can change the last two values inside the viewBox to zoom in or out of the image. The larger the values, the more SVG units are added to fit in the viewport, resulting in a smaller image. If we want to keep a 1:1 ratio, our viewBox width and height must match our viewport width and height values.

Let’s see what happens in Illustrator when we change these parameters. The artboard is the viewport which is represented by a white 700px square. Everything else outside that area is our infinite SVG canvas and gets clipped by default.

Figure 1 below shows a blue dot at 900 along the x-axis and 900 along the y-axis. If I change the last two viewBox values from 700 to 900 like this:

<svg viewBox="300 200 900 900" width="700" height="700">

…then the blue dot is almost fully back in view, as seen in Figure 2 below. Our image is scaled down because we increased the viewBox values, but the SVG’s actual width and height dimensions remained the same, and the blue dot made its way back closer to the unclipped area.

Figure 1.
Figure 1
Figure 2

There is a pink square as evidence of how the grid scales to fit the viewport: the unit gets smaller, and more grid lines fit into the same viewport area. You can play with the same values in the following Pen to see that work in action:

2. Missing width and height

Another common thing I look at when debugging inline SVG is whether the markup contains the width or height attributes. This is no big deal in many cases unless the SVG is inside a container with absolute positioning or a flexible container (as Safari computes the SVG width value with 0px instead of auto). Excluding width or height in these cases prevents us from seeing the full image, as we can see by opening this CodePen demo and comparing it in Chrome, Safari, and Firefox (tap images for larger view).

Chrome
Safari
Firefox

The solution? Add a width or height, whether as a presentation attribute, inline in the style attribute, or in CSS. Avoid using height by itself, particularly when it is set to 100% or auto. Another workaround is to set the right and left values.

You can play around with the following Pen and combine the different options.

3. Inadvertent fill and stroke colors

It may also be that we are applying color to the <svg> tag, whether it’s an inline style or coming from CSS. That’s fine, but there could be other color values throughout the markup or styles that conflict with the color set on the <svg>, causing parts to be invisible.

That’s why I tend to look for the fill and stroke attributes in the SVG’s markup and wipe them out. The following video shows an SVG I styled in CSS with a red fill. There are a couple of instances where parts of the SVG are filled in white directly in the markup that I removed to reveal the missing pieces.

4. Missing IDs

This one might seem super obvious, but you’d be surprised how often I see it come up. Let’s say we made an SVG file in Illustrator and were very diligent about naming our layers so that you get nice matching IDs in the markup when exporting the file. And let’s say we plan to style that SVG in CSS by hooking into those IDs.

That’s a nice way to do things. But there are plenty of times where I’ve seen the same SVG file exported a second time to the same location and the IDs are different, usually when copy/pasting the vectors directly. Maybe a new layer was added, or one of the existing ones was renamed or something. Whatever the case, the CSS rules no longer match the IDs in the SVG markup, causing the SVG to render differently than you’d expect.

Underscores with numbers after the element IDs
Pasting Illustrator’s exported SVG file into SVGOMG.

In large SVG files we might find it difficult to find those IDs. This is a good time to open the DevTools, inspect that part of the graphic that’s not working, and see if those IDs are still matching.

So, I’d say it’s worth opening an exported SVG file in a code editor and comparing it to the original before swapping things out. Apps like Illustrator, Figma, and Sketch are smart, but that doesn’t mean we aren’t responsible for vetting them.

5. Checklist for clipping and masking

If an SVG is unexpectedly clipped and the viewBox checks out alright, I usually look at the CSS for clip-path or mask properties that might interfere with the image. It’s tempting to keep looking at the inline markup, but it’s good to remember that an SVG’s styling might be happening elsewhere.

CSS clipping and masking allow us to “hide” parts of an image or element. In SVG, <clipPath> is a vector operation that cuts parts of an image with no halfway results. The <mask> tag is a pixel operation that allows transparency, semi-transparency effects, and blurred edges.

This is a small checklist for debugging cases where clipping and masking are involved:

  • Make sure the clipping path (or mask) and the graphic overlap one another. The overlapping parts are what gets displayed.
  • If you have a complex path that is not intersecting your graphic, try applying transforms until they match.
  • You can still inspect the inner code with the DevTools even though the <clipPath> or <mask> are not rendered, so use it!
  • Copy the markup inside <clipPath> and <mask> and paste it before closing the </svg> tag. Then add a fill to those shapes and check the SVG’s coordinates and dimensions. If you still do not see the image, try adding overflow="hidden" to the <svg> tag.
  • Check that a unique ID is used for the <clipPath> or <mask>, and that the same ID is applied to the shapes or group of shapes that are clipped or masked. A mismatched ID will break the appearance.
  • Check for typos in the markup between the <clipPath> or <mask> tags.
  • fill, stroke, opacity, or some other styles applied to the elements inside <clipPath> are useless — the only useful part is the fill-region geometry of those elements. That’s why if you use a <polyline> it will behave as a <polygon> and if you use a <line> you won’t see any clipping effect.
  • If you don’t see your image after applying a <mask>, make sure that the fill of the masking content is not entirely black. The luminance of the masking element determines the opacity of the final graphic. You’ll be able to see through the brighter parts, and the darker parts will hide your image’s content.

You can play with masked and clipped elements in this Pen.

6. Namespaces

Did you know that SVG is an XML-based markup language? Well, it is! The namespace for SVG is set on the xmlns attribute:

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

There’s a lot to know about namespacing in XML and MDN has a great primer on it. Suffice to say, the namespace provides context to the browser, informing it that the markup is specific to SVG. The idea is that namespaces help prevent conflicts when more than one type of XML is in the same file, like SVG and XHTML. This is a much less common issue in modern browsers but could help explain SVG rendering issues in older browsers or browsers like Gecko that are strict when defining doctypes and namespaces.

The SVG 2 specification does not require namespacing when using HTML syntax. But it’s crucial if support for legacy browsers is a priority — plus, it doesn’t hurt anything to add it. That way, when the <html> element’s xmlns attribute is defined, it will not conflict in those rare cases.

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
  <body>
    <svg xmlns="http://www.w3.org/2000/svg" width="700px" height="700px">
      <!-- etc. -->
    </svg>
  </body>
</html>

This is also true when using inline SVG in CSS, like setting it as a background image. In the following example, a checkmark icon appears on the input after successful validation. This is what the CSS looks like:

textarea:valid {
 background: white url('data:image/svg+xml,\
    <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26">\
    <circle cx="13" cy="13" r="13" fill="%23abedd8"/>\
    <path fill="none" stroke="white" stroke-width="2" d="M5 15.2l5 5 10-12"/>\
    </svg>') no-repeat 98% 5px;
}

When we remove the namespace inside the SVG in the background property, the image disappears:

Another common namespace prefix is xlink:href. We use it a lot when referencing other parts of the SVG like: patterns, filters, animations or gradients. The recommendation is to start replacing it with href as the other one is being deprecated since SVG 2, but there might be compatibility issues with older browsers. In that case, we can use both. Just remember to include the namespace xmlns:xlink="http://www.w3.org/1999/xlink" if you are still using xlink:href.

Level up your SVG skills!

I hope these tips help save you a ton of time if you find yourself troubleshooting improperly rendered inline SVGs. These are just the things I look for. Maybe you have different red flags you watch for — if so, tell me in the comments!

The bottom line is that it pays to have at least a basic understanding of the various ways SVG can be used. CodePen Challenges often incorporate SVG and offer good practice. Here are a few more resources to level up:

There are a few people I suggest following for SVG-related goodness:


6 Common SVG Fails (and How to Fix Them) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/6-common-svg-fails-and-how-to-fix-them/feed/ 4 376352
Mastering SVG’s stroke-miterlimit Attribute https://css-tricks.com/mastering-svgs-stroke-miterlimit-attribute/ https://css-tricks.com/mastering-svgs-stroke-miterlimit-attribute/#respond Tue, 24 May 2022 14:04:17 +0000 https://css-tricks.com/?p=365828 So, SVG has this stroke-miterlimit presentation attribute. You’ve probably seen it when exporting an SVG from a graphic editor program, or perhaps you find out you could remove it without noticing any change to the visual appearance.

After a good …


Mastering SVG’s stroke-miterlimit Attribute originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
So, SVG has this stroke-miterlimit presentation attribute. You’ve probably seen it when exporting an SVG from a graphic editor program, or perhaps you find out you could remove it without noticing any change to the visual appearance.

After a good amount of research, one of the first things I discovered is that the attribute works alongside stroke-linejoin, and I’ll show you how as well as a bunch of other things I learned about this interesting (and possibly overlooked) SVG attribute.

TLDR;

stroke-miterlimit depends on stroke-linejoin: if we use round or bevel for joins, then there’s no need to declare stroke-miterlimit. But if we use miter instead, we can still delete it and maybe the default value will be enough. Beware that many graphic software editors will add this attribute even when is not necessary.

What is stroke-linejoin?

I know, we’re actually here to talk about stroke-miterlimit, but I want to start with stroke-linejoin because of how tightly they work together. This is the definition for stroke-linejoin pulled straight from the SVG Working Group (SVGWG):

stroke-linejoin specifies the shape to be used at the corners of paths or basic shapes when they are stroked.

This means we can define how the corner looks when two lines meet at a point. And this attribute accepts five possible values, though two of them have no browser implementation and are identified by the spec as at risk of being dropped. So, I’ll briefly present the three supported values the attribute accepts.

miter is the default value and it just so happens to be the most important one of the three we’re looking at. If we don’t explicitly declare stroke-linejoin in the SVG code, then miter is used to shape the corner of a path. We know a join is set to miter when both edges meet at a sharp angle.

But we can also choose round which softens the edges with — you guessed it — rounded corners.

The bevel value, meanwhile, produces a flat edge that sort of looks like a cropped corner.

What is stroke-miterlimit?

OK, now that we know what stroke-linejoin is, let’s get back to the topic at hand and pick apart the definition of stroke-miterlimit from the book Using SVG with CSS3 and HTML5:

[…] on really tight corners, you have to extend the stroke for quite a distance, before the two edges meet. For that reason, there is a secondary property: stroke-miterlimit. It defines how far you can extend the point when creating a miter corner.

In other words, stroke-miterlimit sets how far the stroke of the edges goes before they can meet at a point. And only when the stroke-linejoin is miter.

Miter join with miter limit in grey.

So, the stroke-miterlimit value can be any positive integer, where 4 is the default value. The higher the value, the further the corner shape is allowed to go.

How they work together

You probably have a good conceptual understanding now of how stroke-linejoin and stroke-miterlimit work together. But depending on the stroke-miterlimit value, you might get some seemingly quirky results.

Case in point: if stroke-linejoin is set to miter, it can actually wind up looking like the bevel value instead when the miter limit is too low. Here’s the spec again to help us understand why:

If the miter length divided by the stroke width exceeds the stroke-miterlimit then [the miter value] is converted to a bevel.

So, mathematically we could say that this:

[miter length] / [stroke width] > [stroke-miterlimit] = miter
[miter length] / [stroke width] < [stroke-miterlimit] = bevel

That makes sense, right? If the miter is unable to exceed the width of the stroke, then it ought to be a flat edge. Otherwise, the miter can grow and form a point.

Sometimes seeing is believing, so here’s Ana Tudor with a wonderful demo showing how the stroke-miterlimit value affects an SVG’s stroke-linejoin:

Setting miter limits in design apps

Did you know that miter joins and limits are available in many of the design apps we use in our everyday work? Here’s where to find them in Illustrator, Figma, and Inkscape.

Setting miter limits in Adobe Illustrator

Illustrator has a way to modify the miter value when configuring a path’s stroke. You can find it in the “Stroke” settings on a path. Notice how — true to the spec — we are only able to set a value for the “Limit” when the path’s “Corner” is set to “Miter Join”.

Applying stroke-miterlimit in Adobe Illustrator.

One nuance is that Illustrator has a default miter limit of 10 rather than the default 4. I’ve noticed this every time I export the SVG file or copy and paste the resulting SVG code. That could be confusing when you open up the code because even if you do not change the miter limit value, Illustrator adds stroke-miterlimit="10" where you might expect 4 or perhaps no stroke-miterlimit at all.

And that’s true even if we choose a different stroke-linejoin value other than “Miter Join”. Here is the code I got when exporting an SVG with stroke-linejoin="round".

<svg viewBox="0 0 16 10"><path stroke-width="2" stroke-linejoin="round" stroke-miterlimit="10" d="M0 1h15.8S4.8 5.5 2 9.5" fill="none" stroke="#000"/></svg>

The stroke-miterlimit shouldn’t be there as it only works with stroke-linejoin="miter". Here are a couple of workarounds for that:

  • Set the “Limit” value to 4, as it is the default in SVG and is the only value that doesn’t appear in the code.
  • Use the “Export As” or “Export for Screen” options instead of “Save As” or copy-pasting the vectors directly.

If you’d like to see that fixed, join me and upvote the request to make it happen.

Setting miter limits in Figma

Miter joins and limits are slightly different in Figma. When we click the node of an angle on a shape, under the three dots of the Stroke section, we can find a place to set the join of a corner. The option “Miter angle” appears by default, but only when the join is set to miter:

Applying stroke-miterlimit in Figma.

This part works is similar to Illustrator except for how Figma allows us to set the miter angle in degree units instead of decimal values. There are some other specific nuances to point out:

  • The angle is 7.17° by default and there is no way to set a lower value. When exporting the SVG, that value is becomes stroke-miterlimit='16‘ in the markup, which is different from both the SVG spec and the Illustrator default.
  • The max value is 180°, and when drawing with this option, the join is automatically switched to bevel.
  • When exporting with bevel join, the stroke-miterlimit is there in the code, but it keeps the value that was set when the miter angle was last active (Illustrator does the same thing).
  • When exporting the SVG with a round join, the path is expanded and we no longer have a stroke, but a path with a fill color.

I was unable to find a way to avoid the extra code that ends up in the exported SVG when stroke-miterlimit is unneeded.

Setting miter limits in Inkscape

Inkscape works exactly the way I’d expect a design app to manage miter joins and limits. When selecting a a miter join, the default value is 4, exactly what it is in the spec. Better yet, stroke-miterlimit is excluded from the exported SVG code when it is the default value!

Applying stroke-miterlimit in Inkscape.

Still, if we export any path with bevel or round after the limit was modified, the stroke-miterlimit will be back in the code, unless we keep the 4 units of the default in the Limit box. Same trick as Illustrator.

These examples will work nicely if we choose the Save AsOptimized SVG option. Inkscape is free and open source and, at the end of the day, has the neatest code as far as stroke-miterlimit goes and the many options to optimize the code for exporting.

But if you are more familiar with Illustrator (like I am), there is a workaround to keep in mind. Figma, because of the degree units and the expansion of the strokes, feels like the more distant from the specs and expected behavior.

Wrapping up

And that’s what I learned about SVG’s stroke-miterlimit attribute. It’s another one of those easy-to-overlook things we might find ourselves blindly cutting out, particularly when optimizing an SVG file. So, now when you find yourself setting stroke-miterlimit you’ll know what it does, how it works alongside stroke-linejoin, and why the heck you might get a beveled join when setting a miter limit value.


Mastering SVG’s stroke-miterlimit Attribute originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/mastering-svgs-stroke-miterlimit-attribute/feed/ 0 365828
How I Made a Generator for SVG Loaders With Sass and SMIL Options https://css-tricks.com/how-i-made-a-generator-for-svg-loaders-with-sass-and-smil-options/ https://css-tricks.com/how-i-made-a-generator-for-svg-loaders-with-sass-and-smil-options/#comments Thu, 26 Aug 2021 14:35:03 +0000 https://css-tricks.com/?p=350223 While learning Vue.js, I started building free web tools that involved the exploration of SVG, with the goal of learning something about both! Let’s take a look at one of those tools: a generator that makes SVG loaders and …


How I Made a Generator for SVG Loaders With Sass and SMIL Options originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
While learning Vue.js, I started building free web tools that involved the exploration of SVG, with the goal of learning something about both! Let’s take a look at one of those tools: a generator that makes SVG loaders and lets you choose between SMIL or Sass animation, different styles, colors, shapes, and effects. It even lets you paste in a custom path or text, and then download the final SVG, copy the code, or open a demo over at CodePen.

How it started

Three coincidences led me to build a generator for SVG loaders.

Coincidence 1: Sarah Drasner’s book

The first time I read about Sass loops was in Sarah Drasner’s SVG Animations. She shows how to stagger animations with a Sass function (like the does in Chapter 6, “Animating Data Visualizations”).

I was inspired by that chapter and the possibilities of Sass loops.

Coincidence 2: A GIF

At that same point in life, I was asked to replicate a “loader” element, similar to Apple’s old classic.

A round segmented spinner where each segment fades in and out in succession to create a circling effect.
This is a mockup of the loader I was asked to make.

I referenced Sarah’s example to make it happen. This is the Sass loop code I landed on:

@for $i from 1 to 12 {
  .loader:nth-of-type(#{$i}) {
    animation: 1s $i * 0.08s opacityLoader infinite;
  }
}
@keyframes opacityLoader {
 to { opacity: 0; }
}

This defines a variable for a number (i) from 1 to 12 that increases the delay of the animation with every :nth-child element. It was the perfect use case to animate as many elements as I wanted with only two lines of Sass, saving me CSS declarations for each of the delays I needed. This is the same animation, but written in vanilla CSS to show the difference:

.loader:nth-of-type(1) {
  animation: 1s 0.08s opacityLoader infinite;
}
.loader:nth-of-type(2) {
  animation: 1s 0.16s opacityLoader infinite;
}

/* ... */

.loader:nth-of-type(12) {
  animation: 1s 0.96s opacityLoader infinite;
}
@keyframes opacityLoader {
  to { opacity: 0; }
}

Coincidence 3: An idea

With these things going on in my head, I had an idea for a gallery of loaders, where each loader is made from the same Sass loop. I always struggle to find these kinds of things online, so I thought it might be useful for others, not to mention myself.

I had already built this kind of thing before as a personal project, so I ended up building a loader generator. Let me know if you find bugs in it!

One loader, two outputs

I was blocked by my own developer skills while creating a generator that produces the right Sass output. I decided to try another animation approach with SMIL animations, and that’s what I wound up deciding to use.

But then I received some help (thanks, ekrof!) and got Sass to work after all.

So, I ended up adding both options to the generator. I found it was a challenge to make both languages return the same result. In fact, they sometimes produce different results.

SMIL vs. CSS/Sass

I learned quite a bit about SMIL and CSS/Sass animations along the way. These are a few of the key takeaways that helped me on my way to making the generator:

  • SMIL doesn’t rely on any external resources. It animates SVG via presentation attributes directly in the SVG markup. That’s something that neither CSS nor Sass can do.
  • SMIL animations are preserved when an SVG is embedded as an image or as a background image. It is possible to add a CSS <style> block directly inside the SVG, but not so much with Sass, of course. That’s why there is an option to download the actual SVG file when selecting the SMIL option in the generator.
  • SMIL animations look a bit more fluid. I couldn’t find the reason for this (if anyone has any deeper information here, please share!). I though it was related to GPU acceleration, but it seems they both use the same animation engine.
Two spinners, one left and one right. They are both red and consist of circles that fade in and out in succession as an animated GIF.
SMIL (left) and Sass (right)

You might notice a difference in the chaining of the animations between both languages:

  • I used additive="sum" in SMIL to add animations one after the other. This makes sure each new animation effect avoids overriding the previous animation.
  • That said, in CSS/Sass, the W3C points out that [when] multiple animations are attempting to modify the same property, then the animation closest to the end of the list of names wins.

That’s why the order in which animations are applied might change the Sass output.

Working with transforms

Working with transformations in the loader’s styling was a big issue. I had applied transform: rotate inline to each shape because it’s a simple way to place them next to each other in a circle and with a face pointing toward the center.

<svg>
  <!-- etc. -->
  <use class="loader" xlink:href="#loader" transform="rotate(0 50 50)" />
  <use class="loader" xlink:href="#loader" transform="rotate(30 50 50)" />
  <use class="loader" xlink:href="#loader" transform="rotate(60 50 50)" />
  <!-- etc. -->
</svg>

I could declare a type in SMIL with <animateTransform> (e.g. scale or translate) to add that specific transform to the original transformation of each shape:

<animateTransform
  attributeName="transform"
  type="translate"
  additive="sum"
  dur="1s"
  :begin="`${i * 0.08}s`"
  repeatCount="indefinite"
  from="0 0"
  to="10"
/>

But instead, transform in CSS was overriding any previous transform applied to the inline SVG. In other words, the original position reset to 0 and showed a very different result from what SMIL produced. That meant the animations wound up looking identical no matter what.

The same two red spinners as before but with different results. The SMIL version on the left seems to work as expected but the Sass one on the right doesn't animate in a circle like it should.

The (not very pretty) solution to make the Sass similar to SMIL was to place each shape inside a group (<g>) element, and apply the inline rotation to the groups, and the animation to the shapes. This way, the inline transform isn’t affected by the animation.

<svg>
  <!-- etc. -->
  <g class="loader" transform="rotate(0 50 50)">
    <use xlink:href="#loader" />
  </g>
  <g class="loader" transform="rotate(30 50 50)">
    <use xlink:href="#loader" />
  </g>
  <!-- etc. -->
</svg>

Now both languages have a very similar result.

The technology I used

I used Vue.js and Nuxt.js. Both have great documentation, but there are more specific reasons why I choose them.

I like Vue for lots of reasons:

  • Vue encapsulates HTML, CSS, and JavaScript as a “single file component” where all the code lives in a single file that’s easier to work with.
  • The way Vue binds and dynamically updates HTML or SVG attributes is very intuitive.
  • HTML and SVG don’t require any extra transformations (like making the code JSX-compatible).

As far as Nuxt goes:

  • It has a quick boilerplate that helps you focus on development instead of configuration.
  • There’s automatic routing and it supports auto-importing components.
  • It’s a good project structure with pages, components, and layouts.
  • It’s easier to optimize for SEO, thanks to meta tags.

Let’s look at a few example loaders

What I like about the end result is that the generator isn’t a one-trick pony. There’s no one way to use it. Because it outputs both SMIL and CSS/Sass, there are several ways to integrate a loader into your own project.

Download the SMIL SVG and use it as a background image in CSS

Like I mentioned earlier, SMIL features are preserved when an SVG is used as a background image file. So, simply download the SVG from the generator, upload it to your server, and reference it in CSS as a background image.

Similarly, we could use the SVG as a background image of a pseudo-element:

Drop the SVG right into the HTML markup

The SVG doesn’t have to be a background image. It’s just code, after all. That means we can simply drop the code from the generator into our own markup and let SMIL do its thing.

Use a Sass loop on the inline SVG

This is what I was originally inspired to do, but ran into some roadblocks. Instead of writing CSS declarations for each animation, we can use the Sass loop produced by the generator. The loop targets a .loader class that’s already applied to the outputted SVG. So, once Sass is compiled to CSS, we get a nice spinning animation.

I’m still working on this

My favorite part of the generator is the custom shape option where you can add text, emojis, or any SVG element to the mix:

The same circle spinner but using custom SVG shapes: one a word, one a poop emoji, and bright pink and orange asterisk.
Custom text, emoji, and SVG

What I would like to do is add a third option for styles to have just one element where you get to work with your own SVG element. That way, there’s less to work with, while allowing for simpler outputs.

The challenge with this project is working with custom values for so many things, like duration, direction, distance, and degrees. Another challenge for me personally is becoming more familiar with Vue because I want to go back and clean up that messy code. That said, the project is open source, and pull requests are welcome! Feel free to send suggestions, feedback, or even Vue course recommendations, especially ones related to SVG or making generators.

This all started with a Sass loop that I read in a book. It isn’t the cleanest code in the world, but I’m left blown away by the power of SMIL animations. I highly recommend Sarah Soueidan’s guide for a deeper dive into what SMIL is capable of doing.

If you’re curious about the safety of SMIL, that is for good reason. There was a time when Chrome was going to entirely deprecated SMIL (see the opening note in MDN). But that deprecation has been suspended and hasn’t (seemingly) been talked about in a while.

Can I use SMIL?

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

ChromeFirefoxIEEdgeSafari
54No796

Mobile / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
11511536.0-6.1

How I Made a Generator for SVG Loaders With Sass and SMIL Options originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/how-i-made-a-generator-for-svg-loaders-with-sass-and-smil-options/feed/ 5 350223
How to Add a Double Border to SVG Shapes https://css-tricks.com/how-to-add-a-double-border-to-svg-shapes/ https://css-tricks.com/how-to-add-a-double-border-to-svg-shapes/#comments Wed, 21 Apr 2021 14:26:28 +0000 https://css-tricks.com/?p=338478 Let’s say someone asks you to add a double border to some random geometric SVG shapes. For some reason, you can’t use any graphic editor — they need to be generated at runtime — so you have to solve it …


How to Add a Double Border to SVG Shapes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Let’s say someone asks you to add a double border to some random geometric SVG shapes. For some reason, you can’t use any graphic editor — they need to be generated at runtime — so you have to solve it with CSS or within the SVG syntax.

Your first question might be: Is there anything like stroke-style: double in SVG? Well, the answer is not yet and it’s not that easy. But I’ll attempt it anyway to see what methods I can uncover. I’ll explore the possibilities of three different basic shapes: circle, rectangle, and polygon. Pointing the ones that can keep a transparent color in the middle of the two lines.

Spoiler alert: all the results have their downsides, at least with CSS and SVG, but let me walk you through my intents.

The simple solutions

These don’t work with all shapes, but they are the easiest of the solutions.

outline and box-shadow

The CSS properties outline and box-shadow only apply to the bounding box of the shape or SVG, and so both are great solutions only for squares and rectangles. They also allow flexible colors using custom properties.

It only takes two lines of CSS with outline, plus it keeps the background color visible through the shape.

  • 🙁 Solution only for one shape.
  • ✅ Simple code
  • ✅ Borders are smooth
  • ✅ Transparent background

box-shadow only needs one line of CSS, but we have to make sure that each shape has its own SVG as we can’t apply box-shadow directly to the shapes. Another thing to consider is that we have to apply the color of the background in the declaration.

  • 🙁 Solution only for one shape
  • ✅ Simple code
  • ✅ Borders are smooth
  • 🙁 No transparent background

SVG gradients

SVG radial gradients only work on circles ☺️. We can directly apply the gradient on the stroke, but it’s better to use variables as we have to declare the colors many times in the code.

  • 🙁 Solution only for one shape
  • ✅ Simple code
  • 🙁 Borders are smooth
  • 🙁 No transparent background

Solutions for all shapes

These will work with all shapes, but the code could become bloated or complex.

filter: drop-shadow()

Finally, one solution for all shapes! We must have each shape in its own <svg> since the filter won’t apply directly to the shapes. We are using one declaration in CSS and have flexible colors using variables. The downside? The borders don’t look very smooth.

  • ✅ One solution for all shapes
  • ✅ Simple code
  • 🙁 Borders look pixelated
  • 🙁 No transparent background

SVG filters

This is a very flexible solution. We can create a filter and add it to the shapes through SVG’s filter attribute. The complicated part here is the filter itself. We’ll need three paintings, one for the outside border, one for the background flood, and the last one to paint the shape on the front. The result looks better than using drop-shadow, but the borders are still pixelated.

  • ✅ One solution for all shapes
  • 🙁 Complex code
  • 🙁 Borders look pixelated
  • 🙁 No transparent background

Reusing shapes

There are a couple of possible options here.

Option 1: Transforms

This solution requires transforms. We place one figure over the other, where the main figure has a fill color and a stroke color, and the other figure has no fill, a red stroke, and is scaled and repositioned to the center. We defined our shapes on the <defs>. The trick is to translate half of the viewBox to the negative space so that, when we scale them, we can do it from the center of the figure.

  • ✅ One solution for all shapes
  • 🙁 Duplicated code
  • ✅ Borders are smooth
  • ✅ Transparent background
Option 2: <use>

I found a clever solution in the www-svg mailing list by Doug Schepers that uses SVG <use>. Again, it requires defining the shapes once and referring to them twice using <use>. This time the main shape has a bigger stroke. The second shape has half the stroke of the main shape, no fill, and a stroke matching the background color.

  • ✅ One solution for all shapes
  • 🙁 Duplicated code
  • ✅ Borders are smooth
  • 🙁 No transparent background

Here are the full results!

Just so you have them all in one place. Let me know it you can think of other possible solutions!

SolutionAll shapesSimple codeSmooth bordersTransparent background
outline🙁
box-shadow🙁🙁
SVG gradients🙁🙁🙁
filter: drop-shadow()🙁🙁
SVG filters🙁🙁🙁
Reusing shapes:
Tranforms
🙁
Reusing shapes:
<use>
🙁🙁


How to Add a Double Border to SVG Shapes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/how-to-add-a-double-border-to-svg-shapes/feed/ 4 338478
How to Simplify SVG Code Using Basic Shapes https://css-tricks.com/how-to-simplify-svg-code-using-basic-shapes/ https://css-tricks.com/how-to-simplify-svg-code-using-basic-shapes/#comments Thu, 03 Sep 2020 14:47:33 +0000 https://css-tricks.com/?p=320070 There are different ways to work with icons, but the best solution always includes SVG, whether it’s implemented inline or linked up as an image file. That’s because they’re “drawn” in code, making them flexible, adaptable, and scalable in any …


How to Simplify SVG Code Using Basic Shapes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
There are different ways to work with icons, but the best solution always includes SVG, whether it’s implemented inline or linked up as an image file. That’s because they’re “drawn” in code, making them flexible, adaptable, and scalable in any context.

But when working with SVG, there’s always the chance that they contain a lot of unnecessary code. In some cases, the code for an inline SVG can be long that it makes a document longer to scroll, uncomfortable to work with, and, yes, a little bit heavier than it needs to be.

We can work around this reusing chunks of code with the <use> element or apply native variables to manage our SVG styles from one place. Or, if we’re working in a server-side environment, we can always sprinkle in a little PHP (or the like) to extract the contents of the SVG file instead of dropping it straight in.

That’s all fine, but wouldn’t be great if we could solve this at the file level instead of resorting to code-based approaches? I want to focus on a different perspective: how to make the same figures with less code using basic shapes. This way, we get the benefits of smaller, controllable, and semantic icons in our projects without sacrificing quality or visual changes. I’ll go through different examples that explore the code of commonly used icons and how we can redraw them using some of the easiest SVG shapes we can make.

Here are the icons we’ll be working on:

Showing an close icon in the shape of an x, a clock with the hands pointing at 3 o-clock, and a closed envelope.

Let’s look at the basic shapes we can use to make these that keep the code small and simple.

Psssst! Here is a longer list of simple icons I created on holasvg.com! After this article, you’ll know how to modify them and make them your own.

Simplifying a close icon with the <line> element

This is the code for the “close” or “cross” icon that was downloaded from flaticon.com and built by pixel-perfect:

In this example, everything is happening inside the <path> with lots of commands and parameters in the data attribute (d). What this SVG is doing is tracing the shape from its borders.

A quick demonstration using mavo.io

If you are familiar with Illustrator, this is the equivalent of drawing two separate lines, converting them to shape, then combining both with the pathfinder to create one compound shape.

The <path> element allows us to draw complex shapes, but in this case, we can create the same figure with two lines, while keeping the same appearance:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50" height="50" overflow="visible" stroke="black" stroke-width="10" stroke-linecap="round">
   <line x1="0" y1="0" x2="50" y2="50" />
   <line x1="50" y1="0" x2="0" y2="50" />
</svg>

We started by defining a viewBox that goes from 0,0 to 50,50. You can choose whatever dimensions you prefer; the SVG will always scale nicely to any width and height you define. To make things easier, in this case, I also defined an inline width and height of 50 units, which avoids extra calculations in the drawing.

To use the <line> element, we declare the coordinates of the line’s first point and the coordinates of its last point. In this specific case, we started from x=0 y=0 and ended at x=50 y=50.

Grid of the coordinate system.

Here’s how that looks in code:

<line x1="0" y1="0" x2="50" y2="50" />

The second line will start from x=50 y=0 and end at x=0 y=50:

<line x1="50" y1="0" x2="0" y2="50" />

An SVG stroke doesn’t have a color by default — that’s why we added the black value on the stroke attribute. We also gave the stroke-width attribute a width of 10 units and the stroke-linecap a round value to replicate those rounded corners of the original design. These attributes were added directly to the <svg> tag so both lines will inherit them.

<svg ... stroke="black" stroke-width="10" stroke-linecap="round" ...>

Now that the stroke is 10 units bigger that its default size of 1 unit, the line might get cropped by the viewBox. We can either move the points 10 units inside the viewBox or add overflow=visible to the styles.

The values that are equal to 0 can be removed, as 0 is the default. That means the two lines end up with two very small lines of code:

<line x2="50" y2="50" />
<line x1="50" y2="50" />

Just by changing a <path> to a <line>, not only did we make a smaller SVG file, but a more semantic and controllable chunk of code that makes any future maintenance much easier. And the visual result is exactly the same as the original.

Same cross, different code.

Simplifying a clock icon with the <circle> and <path> elements

I took this example of a clock icon created by barracuda from The Noun Project:

This shape was also drawn with a <path>, but we also have a lot of namespaces and XML instructions related to the software used and the license of the file that we can delete without affecting the SVG. Can you tell what illustration editor was used to create the icon?

Let’s recreate this one from scratch using a circle and a path with simpler commands. Again, we need to start with a viewBox, this time from 0,0 to 100,100, and with a width and height matching those units.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100" fill="none" stroke="black" stroke-width="10" stroke-linecap="round" stroke-linejoin="round">
  <circle cx="50" cy="50" r="40"/>
  <path d="M50 25V50 H75" /> 
</svg>

We keep the same styles as the previous icon inside the <svg> tag. fill is black by default, so we need to explicitly give it a none value to remove it. Otherwise, the circle will have have a solid black fill, obscuring the other shapes.

To draw the <circle> we need to indicate a center point from where the radius will sit. We can achieve that with cx (center x) and cy (center y). Then r (radius) will declare how big our circle will be. In this example, the radius is slightly smaller than the viewBox, so it doesn’t get cropped when the stroke is 10 units wide.

What’s up with all those letters? Check out Chris Coyier’s illustrated guide for a primer on the SVG syntax.

We can use a <path> for the clock hands because it has some very useful and simple commands to draw. Inside the d (data) we must start with the M (move to) command followed by the coordinates from where we’ll start drawing which, in this example, is 50,25 (near the top-center of the circle). 

After the V (vertical) command, we only need one value as we can only move up or down with a negative or positive number. A positive number will go down. The same for H (horizontal) followed by a positive number, 75, that will draw toward the right. All commands are uppercase, so the numbers we choose will be points in the grid. If we decided to use lowercase (relative commands) the numbers will be the amount of units that we move in one direction and not an absolute point in the coordinate system.

Same clock, different code.

Simplifying an envelope icon with the <rect> and <polyline> elements

I drew the envelope icon in Illustrator without expanding the original shapes. Here’s the code that came from the export:

Illustrator offers some SVG options to export the graphic. I chose “Style Elements” in the “CSS Properties” dropdown so I can have a <style> tag that contains classes that I might want to move to a CSS file. But there are different ways to apply the styles in SVG, of course.

We already have basic shapes in this code! I unselected the “Shape to paths” option in Illustrator which helped a lot there. We can optimize this further with SVGOMG to remove the comments, XML instructions, and unnecessary data, like empty elements. From there, we can manually remove other extras, if we need to.

We already have something a little more concise:

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 310 190" xml:space="preserve">
  <style>.st0{fill:none;stroke:#000;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10}
  </style><rect x="5" y="5" class="st0" width="300" height="180"/>
  <polyline class="st0" points="5 5 155 110 305 5"/>
</svg>

We can remove even more stuff without affecting the visual appearance of the envelope, including: 

  • version="1.1" (this has been deprecated since SVG 2)
  • id="Layer_1" (this has no meaning or use)
  • x="0" (this is a default value)
  • y="0" (this is a default value)
  • xml:space="preserve" (this has been deprecated since SVG 2)
<svg xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 310 190">
  <style>.st0{fill:none;stroke:#000;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10}
  </style>
  <rect x="5" y="5" class="st0" width="300" height="180"/>
  <polyline class="st0" points="5 5 155 110 305 5"/>
</svg>

We can move the CSS styles to a separate stylesheet if we really want to get really aggressive.

<rect> needs a starting point from where we’ll extend a width and a height, so let’s use  x="5" and y="5" which is our top-left point. From there, we will create a rectangle that is 300 units wide with a height of 180 units. Just like the clock icon, we’ll use 5,5 as the starting point because we have a 10-unit stroke that will get cropped if the coordinates were located at 0,0.

<polyline> is similar to <line>, but with an infinite amount of points that we define, like pairs of coordinates, one after the other, inside the points attribute, where the first number in the pair will represent x and the second will be y. It’s easier to read the sequence with commas, but those can be replaced with whitespace without having an impact on the result.

Same envelope, different code.

Bonus shapes!

I didn’t include examples of icons that can be simplified with <polygon> and <ellipse> shapes, but here is a quick way to use them.

<polygon> is the same as <polyline>, only this element will always define a closed shape. Here’s an example that comes straight from MDN:

Remember the circle we drew earlier for the clock icon? Replace the r (radius) with rx and ry. Now you have two different values for radius. Here’s another example from MDN:

Wrapping up

We covered a lot here in a short amount of time! While we used examples to demonstrates the process of optimizing SVGs, here’s what I hope you walk away with from this post:

  • Remember that compression starts with how the SVG is drawn in illustration software.
  • Use available tools, like SVOMG, to compress SVG.
  • Remove unnecessary metadata by hand, if necessary.
  • Replace complex paths with basic shapes.
  • <use> is a great way to “inline” SVG as well as for establishing your own library of reusable icons.

How many icons can be created by combining these basic shapes?

I’m working my list on holasvg.com/icons, I’ll be constantly uploading more icons and features here, and now you know how to easily modified them just by changing a few numbers. Go ahead and make them yours!


How to Simplify SVG Code Using Basic Shapes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/how-to-simplify-svg-code-using-basic-shapes/feed/ 12 320070
Use and Reuse Everything in SVG… Even Animations! https://css-tricks.com/use-and-reuse-everything-in-svg-even-animations/ https://css-tricks.com/use-and-reuse-everything-in-svg-even-animations/#comments Tue, 28 Jan 2020 15:36:34 +0000 https://css-tricks.com/?p=302186 If you are familiar with SVG and CSS animations and started to work with them often, here are some ideas you might want to keep in mind before jumping into the job. This article will be about learning how to …


Use and Reuse Everything in SVG… Even Animations! originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
If you are familiar with SVG and CSS animations and started to work with them often, here are some ideas you might want to keep in mind before jumping into the job. This article will be about learning how to build and optimize your code with <use> element, CSS Variables and CSS animations.

Live Demo

Part 1: The SVG <use> element

If you are a developer that likes to keep your code DRY or a big fan of Sass/CSS variables, there is a good chance that you will like this tag.

Let’s say you have an element that is repeated many times in your graphic. Instead of having a complex part of your code repeated many times in your SVG, you can define this part once and then clone it somewhere else in your document with the <use> element. This will not only reduce an enormous amount of code, but also will make your markup simpler and easier to manipulate.

To start implementing the <use> element, go to your SVG and follow this steps:

  1. Identify the part of the code that you want to clone
  2. Add an ID to that part
  3. Link it inside your <use> tag like this: <use xlink:href="#id"/>

That’s it! Your new clone is ready, now you can change its attributes (e.g. x and y position) to fit your needs.

Let’s dive into a very convenient example

I want to share this real case where I needed to animate a big cube made of little cube units. (Imagine the classic Rubik’s Cube.)

We’ll start by drawing the cube unit in SVG using basic shapes and transforms:

<svg viewBox="-130 -20 300 100">
  <g id="cube">
    <rect width="21" height="24" transform="skewY(30)"/>
    <rect width="21" height="24" transform="skewY(-30) translate(21 24.3)"/>
    <rect width="21" height="21"  transform="scale(1.41,.81) rotate(45) translate(0 -21)"/>
  </g>
</svg>

Note that the shapes are grouped in a <g> element so we can add the ID to the whole figure.

Next, let’s build a bigger cube cloning this unit. First, we need to wrap the cube from the previous example inside the <defs> tag inside the SVG. In the <defs> element we can put whatever we want to reuse, which could be a single shape, a group, a gradient.. almost any SVG element. They won’t render anywhere unless we use them outside this tag.

Then we can link the unit as many times as we want using its ID and change the x and y position on every clone like this:

<use xlink:href="#cube" x="142" y="124"/>
<use xlink:href="#cube" x="100" y="124"/>
<!-- ... -->

Now we have to position every cube remembering that the last element will appear at the front, after that we’ll have our first big cube ready!

xlink:href is deprecated since SVG2, but is better to use it for compatibility purposes. In modern browsers you can just use href but I tested it on Safari and at the time of writing is not working there. If you use xlink:href make sure you include this namespace in your SVG tag: xmlns:xlink="http://www.w3.org/1999/xlink" (you won’t need it if you decide to use href).

Part 2: Using CSS variables to apply different styles to your reused graphic

I chose a main color for the cube, which is a lighter and a darker shade for the sides and a stroke color. But what if we want to make a second cube a different color?

We can replace the fills and strokes with CSS variables to make these attributes more flexible. That way, we’ll be able to reuse the same cube unit with another palette (instead of defining a second unit with different colors for a second cube).

Why not add a class to the new cube and change the fill color with CSS? We’ll do that, but first, try to inspect a <use> element. You’ll notice it renders in the Shadow DOM. which means it is not vulnerable to scripts and styles, like elements in the normal DOM. Whatever values you define in the figure inside <defs> will be inherited by all its instances and you won’t be able to rewrite those with CSS. But if you replace those values with variables, then you’ll be able to control them in CSS.

In our cube unit, we’ll go through each side and replace the fill and stroke values with semantic variable names.

For example, this:

<rect fill="#00affa" stroke="#0079ad" />

…can be replaced with this:

<rect fill="var(--mainColor)" stroke="var(--strokeColor)" />

From here, we must duplicate the SVG to build a second cube. However, we don’t need to duplicate <defs> if we are keeping both in the same document. We can add a class to each SVG and control the color palette through CSS, redefining the values of the variable.

Let’s create a palette for the blue cube and another one for the pink cube:

.blue-cube {
  --mainColor: #009CDE;
  --strokeColor: #0079ad;
  --lightColor: #00affa;
  --darkColor: #008bc7;
}

.pink-cube {
  --mainColor: #de0063;
  --strokeColor: #ad004e;
  --lightColor: #fa0070;
  --darkColor: #c7005a;
}

This way, we can add as many cubes as we want and change all colors from one place.

Part 3: Reusing animations

The idea for this instance is to break the cubes on hover — something like an exploded view so some pieces will move away from the center when we place the cursor over the cubes.

Let’s start by defining two movements, one for each axis: move Y and move X. By dividing the animations in movements, we’ll be able to reuse them in every cube. The animations will consist of moving the cube from its initial position to 30px or 50px away in one direction. We can use a transform translate (X or Y ) to achieve that. For example:

@keyframes moveX {
  to { transform: translateX(-35px);  }
}

But if we want to be able to reuse this animation, it’s better to replace the numeric value with a variable, like this:

@keyframes moveX {
  to { transform: translateX(var(--translate, 35px)); }
}

If the variable is not defined, the default value will be 35px.

Now we need at least one class to bind to the animation. In this case, though, we need two classes to move cubes in the x-axis: .m-left and .m-right.

.m-left, .m-right { 
  animation: 2s moveX alternate infinite; 
}

For the cube to move left, we need a negative value, but we can also declare a different number. We can define our variable like this inside the .m-left class:

.m-left { --translate: -50px; }

What’s happening here is we’re declaring that, when we add the class .m-left to one element, this will play the animation moveX (the one defined in the @keyframes) which will last two seconds to translate in the x-axis and reach a new position that is -50px left. Then, the animation alternates directions so that it moves from the last position and take two more seconds to go to its original state. And so on, because it’s an infinite loop.

We can declare another variable to the .m-right class but if we don’t, remember that it will take the 35px we declared at the beginning.

The default animation-play-state value is running but maybe we don’t want the cubes to move all the time. It would be very distracting and annoying to use on a site with some nearby content. So, let’s try to play the animation only on hover by adding this:

svg:hover .m-left {
  animation: 2s moveX alternate infinite;
}

You can try it by yourself and will find that the animation is jumping super fast to the initial state every time we place the cursor out of the cube. To avoid it, we can add the value paused at the end of the animation shorthand:

.m-left {
  animation: 2s moveX alternate infinite paused;
}

Now the animation is paused but will be running on hover by adding this line of CSS:

svg:hover * { 
  animation-play-state: running; 
}

We can apply each class to different elements in the SVG. In the first blue cube, we are moving single cubes; in the second one, we’re applying those classes to groups of cubes.

One last thing…

It wasn’t until later that I realized I could reuse a single unit to build them all. I worked on the small cube to make it isometric enough so it could align easily with the other ones next to it. At this point, my unit was a <path>, but I decided to replace it with SVG shapes to reduce the code and get cleaner markup.

I learned that it is better to take some time to analyze what can be done with SVG before drawing every single shape and dealing with a huge amount of code. It might take more time at the beginning, but will save you a lot of time and effort in the long run.


Use and Reuse Everything in SVG… Even Animations! originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/use-and-reuse-everything-in-svg-even-animations/feed/ 13 302186
Going Beyond Automatic SVG Compression With the “use” Element https://css-tricks.com/going-beyond-automatic-svg-compression-with-the-use-element/ https://css-tricks.com/going-beyond-automatic-svg-compression-with-the-use-element/#comments Mon, 27 Jan 2020 16:17:30 +0000 https://css-tricks.com/?p=302169 If you draw your own SVG files or if you download them from the internet, tools like this SVG-Editor or SVGOMG are your friends. Compressing the files with those tools takes only few seconds and reduces your file size a …


Going Beyond Automatic SVG Compression With the “use” Element originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
If you draw your own SVG files or if you download them from the internet, tools like this SVG-Editor or SVGOMG are your friends. Compressing the files with those tools takes only few seconds and reduces your file size a lot. But if you need to use your SVG inline to animate or interact with the code, there’s still a lot you can do about code legibility.

Reusing content with SVG’s <use> is not always an option, but when it is, you won’t regret taking a few extra minutes to put it in practice.

In this article, I’ll show an example where I was able to take a lot of advantage of this element — not only for keeping the file size down, but also a clearer markup that became more legible and easy to maintain.

This is the first design that I needed to work with. It was originally created in Illustrator:

Take a look at the following code, this is the original file exported directly from the software, weighs 2.05kb:

It’s not a heavy file at all. However, open it and you’ll find there are lots of empty tags, deprecated namespaces, unnecessary white space, commas and extra information applied by the software. This makes the code hard to work with, annoying to scan and creates a big scroll for those hundreds of lines in your document.

You’ll also notice that the file is indeed using <use> and <defs> elements, but not in the best way it could. And that’s not the software’s fault! Each astronaut illustration in the original file has a clipping mask: an invisible circle that acts like a window through which we can see our character. Without it, the suit of the astronaut would be flooding outside the circle. There are a few ways to avoid this in Illustrator, like cropping those extra parts with a pathfinder option. That way we would gain a few bytes and avoid using an extra circle only for clipping information of the graphic that we won’t show. The compression of the file starts in the software. Still, there are a lot of things we’ll be able to improve on the code in case we don’t want to edit the original file.

Compressing the SVG with SVGOMG and keeping the default options won’t take any effort and you’ll get a file that weighs 1.46kb. That is a reduction of 30% compared to the original size and the graphic will look exactly the same.

Reusing content

This will require going through the SVG and making some adjustments. I know this option takes more time regarding the previous example, but it’s not as hard as it seems. 

We have one repeated element, which is the astronaut inside the circle. That’s the one we’ll compress on SVGOMG. The result will look something like this:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 95.8 95.8">
<style>.st3,.st4{fill:#ffcb2f;stroke:#12192c;stroke-width:1.4891;stroke-miterlimit:10}.st4{fill:#69b2b1}</style>
<circle cx="47.9" cy="47.9" r="47.9" fill="#12192c"/>   
<circle cx="47.9" cy="47.9" r="40.7" fill="#f6a2a4"/>   
<defs><circle id="SVGID_1_" cx="47.9" cy="47.9" r="40.7"/></defs>   
<clipPath id="SVGID_2_"><use xlink:href="#SVGID_1_" overflow="visible"/></clipPath>
<g clip-path="url(#SVGID_2_)">     
<path class="st3" d="M63.9 45.6H32c-4 0-7.2 1.9-7.3 4.3l-.8 26.6H72l-.8-26.6c-.2-2.5-3.4-4.3-7.3-4.3z"/>     
<path class="st4" d="M74.3 86.9L66 88.2C53.8 90 41.4 90 29.1 88.1l-7.7-1.2v-14c0-4 3.2-7.2 7.2-7.2h38.5c4 0 7.2 3.2 7.2 7.2v14z"/>     
<path class="st3" d="M31.8 47.3h-.6c-.7 0-1.2-.6-1.2-1.2V23.2c0-.7.6-1.2 1.2-1.2h.6c.7 0 1.2.6 1.2 1.2v22.9c0 .7-.6 1.2-1.2 1.2z"/>     
<circle class="st4" cx="31.5" cy="20.7" r="2.8"/>     
<circle class="st4" cx="47.9" cy="51.4" r="20.3"/>     
<path d="M64.5 53.1c0 8-7.4 11.2-16.5 11.2S31.4 61 31.4 53.1s7.4-14.4 16.5-14.4 16.6 6.4 16.6 14.4z" fill="#13192d" stroke="#12192c" stroke-width="1.489" stroke-miterlimit="10"/>     
<path fill="none" stroke="#12192c" stroke-width="1.489" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-dasharray="9.6793,3.7228" d="M65.9 88V76.9"/>     
<path fill="none" stroke="#12192c" stroke-width="1.489" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M29.6 87.9v-11"/>   
</g> 
</svg>

First recommendations: 

  1. Move the <style> content to the CSS file (assuming you can use your SVG inline and that you have a stylesheet linked in your document).
  2. Rename the IDs with something that makes sense to you. 
  3. Round those complicated numbers, like stroke-width="1.489" to stroke-width="1.5". This is something that could happen if you resize your vectors in Illustrator with the option of scaling borders checked.
  4. Remove the stroke-miterlimit="10" as we don’t need it since our stroke-linejoin is round.
  5. This code will be our astronaut template. We need to wrap everything in a group, add an ID to that group and place it inside a <defs> tag. Notice that we already have a <defs> element with a circle inside it. We can remove that one as it will be part of a bigger <defs> tag.

Notice that the first two circles are filled shapes with different radius and color. We can keep the smaller one and add a stroke big enough to achieve the same effect — again, something that we could avoid using a circle with border in Illustrator in the first place.

Another important thing is that our current viewBox is too small for what we want to build. Let’s make it bigger and add some negative space on the X axis so we can start cloning our astronaut from the middle. 

To learn more about viewBox, check out this beautiful guide to scaling SVG by Amelia Wattenberger.

We will end with something like this:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-400 0 1000 5000">
 <defs>
  <g id="astronaut">
   <circle cx="94.5" cy="48" r="44" fill="currentColor" stroke="#12192c" stroke-width="8"/><clipPath id="a"><circle cx="94.5" cy="47.9" r="40"/></clipPath>
   <g clip-path="url(#a)"><path class="st3" d="M110.5 45.6H78.6c-4 0-7.2 1.9-7.3 4.3l-.8 26.6h48.1l-.8-26.6c-.1-2.5-3.4-4.3-7.3-4.3z"/><path class="st4" d="M121 86.9l-8.3 1.3C100.4 90 88 90 75.8 88.1l-7.7-1.2v-14c0-4 3.2-7.2 7.2-7.2h38.5c4 0 7.2 3.2 7.2 7.2v14z"/><path class="st3" d="M78.4 47.3h-.6c-.7 0-1.2-.6-1.2-1.2V23.2c0-.7.6-1.2 1.2-1.2h.6c.7 0 1.2.6 1.2 1.2v22.9c0 .7-.5 1.2-1.2 1.2z"/><circle class="st4" cx="78.1" cy="20.7" r="2.8"/><circle class="st4" cx="94.5" cy="51.4" r="20.3"/><path d="M111.1 53.1c0 8-7.4 11.2-16.5 11.2S78 61 78 53.1s7.4-14.4 16.5-14.4 16.6 6.4 16.6 14.4z" fill="#13192d" /><path fill="none" stroke="#12192c" stroke-width="1.5" stroke-linecap="round" d="M112.5 88V76.9"/><path fill="none" stroke="#12192c" stroke-width="1.5" stroke-linecap="round" d="M76.3 87.9v-11"/></g>
  </g>
 </defs>
</svg>

What goes inside the <defs> won’t render anywhere. To start cloning our astronaut, we need to link its ID inside a <use> element like this:

<use xlink:href="#astronaut"/>

xlink:href is deprecated since SVG2, but is better to use it for compatibility purposes. You can use href in modern browsers, but I tested it on Safari and it wasn’t working as of this writing. If you usexlink:href,make sure you include this namespace in your SVG tag: xmlns:xlink="http://www.w3.org/1999/xlink (you won’t need it if you decide to use href).

Now we can add the corresponding text to this first figure and align it with the transform attribute. We better place both elements inside a group so in the future instances we’ll be able to translate the whole group to the position we want:

<g transform="translate(-95 210)">
  <use xlink:href="#astronaut"/>
  <text transform="translate(25 130)">Tech Leader</text>
</g>

The connecting lines are simple shapes that can be drawn directly with <path>. Paths looks scary but, for rect lines, there is not much to worry about. I’ll explain this code:

<path class="line" d="M-4 200v-25h200"/>

d="" is for data and that’s where we’ll place our commands. M is to move our hand to the place where we’ll start drawing (but it’s not drawing anything). -4 200 means we place our pencil four units to the left and 200 to the bottom of our viewBox (following the orientation of the SVG coordinate system). v is the command to start drawing a vertical line that will go from this place to -25 units up. h is for horizontal, so we’re drawing a line from there to  200 to the right. It feels like logo writer.

I separated the lines in three paths, but we can use just one with the M value after a series of commands to move our hand and start drawing from a new point in the coordinate system.

Take a look at the final document. Now the file weighs 779 bytes and has 12 lines of legible and scalable code:

If we declare any value in the attributes we defined in the <defs> then it won’t be possible to change it in their clones because of the nature of the <use> element. That’s why in the example above the fill of the main circle was replaced with a value of currentColor to have control of the backgrounds of all the replications. currentColor will inherit the CSS color value of the element (or any element above it). In the SVG, I’m adding a class to some replicated astronauts and adding a color value in CSS to those classes. This way, I’ll be able to change all instances of the <use> element with that class. To understand more about <use> and how to style its content, this post by Sara Soueidan has everything you need to know.

With this code ready, we’ll be able to scale the graphic to something like this much more easily:

SVG org chart (Demo)

Here are the three examples side by side to compare legibility and amount of code, we went from 241 to 10 neat lines:

From left to right: Code direct from Illustrator, code after SVGOMG, code after optimization.

Going Beyond Automatic SVG Compression With the “use” Element originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/going-beyond-automatic-svg-compression-with-the-use-element/feed/ 6 302169