<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>SVG on Smashing Magazine — For Web Designers And Developers</title><link>https://www.smashingmagazine.com/category/svg/index.xml</link><description>Recent content in SVG on Smashing Magazine — For Web Designers And Developers</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Tue, 14 Oct 2025 04:02:41 +0000</lastBuildDate><item><author>Andy Clarke</author><title>Smashing Animations Part 5: Building Adaptive SVGs With `&lt;symbol>`, `&lt;use>`, And CSS Media Queries</title><link>https://www.smashingmagazine.com/2025/10/smashing-animations-part-5-building-adaptive-svgs/</link><pubDate>Mon, 06 Oct 2025 13:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/10/smashing-animations-part-5-building-adaptive-svgs/</guid><description>SVGs, they scale, yes, but how else can you make them adapt even better to several screen sizes? Web design pioneer &lt;a href="https://stuffandnonsense.co.uk">Andy Clarke&lt;/a> explains how he builds what he calls “adaptive SVGs” using &lt;code>&amp;lt;symbol&amp;gt;&lt;/code>, &lt;code>&amp;lt;use&amp;gt;&lt;/code>, and CSS Media Queries.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/10/smashing-animations-part-5-building-adaptive-svgs/" />
              <title>Smashing Animations Part 5: Building Adaptive SVGs With `&lt;symbol&gt;`, `&lt;use&gt;`, And CSS Media Queries</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Smashing Animations Part 5: Building Adaptive SVGs With `&lt;symbol&gt;`, `&lt;use&gt;`, And CSS Media Queries</h1>
                  
                    
                    <address>Andy Clarke</address>
                  
                  <time datetime="2025-10-06T13:00:00&#43;00:00" class="op-published">2025-10-06T13:00:00+00:00</time>
                  <time datetime="2025-10-06T13:00:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                
                

<p>I’ve written quite a lot recently about how I <a href="https://www.smashingmagazine.com/2025/06/smashing-animations-part-4-optimising-svgs/">prepare and optimise</a> SVG code to use as static graphics or in <a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-1-classic-cartoons-inspire-css/">animations</a>. I love working with SVG, but there’s always been something about them that bugs me.</p>

<p>To illustrate how I build adaptive SVGs, I’ve selected an episode of <em>The Quick Draw McGraw Show</em> called “<a href="https://yowpyowp.blogspot.com/2012/06/quick-draw-mcgraw-bow-wow-bandit.html">Bow Wow Bandit</a>,” first broadcast in 1959.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png"
			
			sizes="100vw"
			alt="Bow Wow Bandit illustration"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The Quick Draw McGraw Show © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>In it, Quick Draw McGraw enlists his bloodhound Snuffles to rescue his sidekick Baba Looey. Like most Hanna-Barbera title cards of the period, the artwork was made by Lawrence (Art) Goble.</p>

<div class="refs">
  <ul><li><a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-1-classic-cartoons-inspire-css/">Smashing Animations Part 1: How Classic Cartoons Inspire Modern CSS</a></li><li><a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-2-css-masking-add-extra-dimension/">Smashing Animations Part 2: How CSS Masking Can Add An Extra Dimension</a></li><li><a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-3-smil-not-dead/">Smashing Animations Part 3: SMIL’s Not Dead Baby, SMIL’s Not Dead</a></li><li><a href="https://www.smashingmagazine.com/2025/06/smashing-animations-part-4-optimising-svgs/">Smashing Animations Part 4: Optimising SVGs</a></li></ul>
</div>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png"
			
			sizes="100vw"
			alt="Quick Draw McGraw character pulling back on a dog leash attached to his bloodhound, Snuffles."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Andy Clarke’s Bow Wow Bandit Toon Title recreation (16:9). (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Let’s say I’ve designed an SVG scene like that one that’s based on Bow Wow Bandit, which has a 16:9 aspect ratio with a <code>viewBox</code> size of 1920×1080. This SVG scales up and down (the clue’s in the name), so it looks sharp when it’s gigantic and when it’s minute.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png"
			
			sizes="100vw"
			alt="16:9 aspect ration vs. 3:4."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Left: 16:9 aspect ratio loses its impact. Right: 3:4 format suits the screen size better. (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>But on small screens, the 16:9 aspect ratio (<a href="https://stuffandnonsense.co.uk/toon-titles/quick-draw-3a.html">live demo</a>) might not be the best format, and the image loses its impact. Sometimes, a portrait orientation, like 3:4, would suit the screen size better.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="729"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png"
			
			sizes="100vw"
			alt="Andy Clarke’s Bow Wow Bandit Toon Title recreation (3:4)."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Andy Clarke’s Bow Wow Bandit Toon Title recreation (3:4). (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>But, herein lies the problem, as it’s not easy to reposition internal elements for different screen sizes using just <code>viewBox</code>. That’s because in SVG, internal element positions are locked to the coordinate system from the original <code>viewBox</code>, so you can’t easily change their layout between, say, desktop and mobile. This is a problem because animations and interactivity often rely on element positions, which break when the <code>viewBox</code> changes.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png"
			
			sizes="100vw"
			alt="Left: 16:9 for larger screens. Right: 3:4 for smaller screens."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Left: 16:9 for larger screens. Right: 3:4 for smaller screens. (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>My challenge was to serve a 1080×1440 version of Bow Wow Bandit to smaller screens and a different one to larger ones. I wanted the position and size of internal elements &mdash; like Quick Draw McGraw and his dawg Snuffles &mdash; to change to best fit these two layouts. To solve this, I experimented with several alternatives.</p>

<p><strong>Note:</strong> Why are we not just using the <code>&lt;picture&gt;</code> with external SVGs? The <a href="https://www.smashingmagazine.com/2014/05/responsive-images-done-right-guide-picture-srcset/"><code>&lt;picture&gt;</code> element</a> is brilliant for responsive images, but it only works with raster formats (like JPEG or WebP) and external SVG files treated as images. That means that you can’t animate or style internal elements using CSS.</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h2 id="showing-and-hiding-svg">Showing And Hiding SVG</h2>

<p>The most obvious choice was to include two different SVGs in my markup, one for small screens, the other for larger ones, then show or hide them using <a href="https://www.smashingmagazine.com/2018/02/media-queries-responsive-design-2018/">CSS and Media Queries</a>:</p>

<pre><code class="language-svg">&lt;svg id="svg-small" viewBox="0 0 1080 1440"&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;

&lt;svg id="svg-large" viewBox="0 0 1920 1080"&gt;
  &lt;!--... --&gt;
&lt;/svg&gt;


#svg-small { display: block; }
#svg-large { display: none; }

@media (min-width: 64rem) {
  #svg-small { display: none; }
  #svg-mobile { display: block; }
}
</code></pre>

<p>But using this method, both SVG versions are loaded, which, when the graphics are complex, means downloading lots and lots and lots of unnecessary code.</p>

<h2 id="replacing-svgs-using-javascript">Replacing SVGs Using JavaScript</h2>

<p>I thought about using JavaScript to swap in the larger SVG at a specified breakpoint:</p>

<pre><code class="language-javascript">if (window.matchMedia('(min-width: 64rem)').matches) {
  svgContainer.innerHTML = desktopSVG; 
} else {
  svgContainer.innerHTML = mobileSVG;
}
</code></pre>

<p>Leaving aside the fact that JavaScript would now be critical to how the design is displayed, both SVGs would usually be loaded anyway, which adds DOM complexity and unnecessary weight. Plus, maintenance becomes a problem as there are now two versions of the artwork to maintain, doubling the time it would take to update something as small as the shape of Quick Draw’s tail.</p>

<h2 id="the-solution-one-svg-symbol-library-and-multiple-uses">The Solution: One SVG Symbol Library And Multiple Uses</h2>

<p>Remember, my goal is to:</p>

<ul>
<li>Serve one version of Bow Wow Bandit to smaller screens,</li>
<li>Serve a different version to larger screens,</li>
<li>Define my artwork just once (DRY), and</li>
<li>Be able to resize and reposition elements.</li>
</ul>

<p>I don’t read about it enough, but the <code>&lt;symbol&gt;</code> element lets you define reusable SVG elements that can be hidden and reused to improve maintainability and reduce code bloat. They’re like components for SVG: <a href="https://css-tricks.com/svg-symbol-good-choice-icons/">create once and use wherever you need them</a>:</p>

<pre><code class="language-svg">&lt;svg xmlns="http://www.w3.org/2000/svg" style="display: none;"&gt;
  &lt;symbol id="quick-draw-body" viewBox="0 0 620 700"&gt;
    &lt;g class="quick-draw-body"&gt;[…]&lt;/g&gt;
  &lt;/symbol&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;

&lt;use href="#quick-draw-body" /&gt;
</code></pre>

<p>A <code>&lt;symbol&gt;</code> is like storing a character in a library. I can reference it as many times as I need, to keep my code consistent and lightweight. Using <code>&lt;use&gt;</code> elements, I can insert the same symbol multiple times, at different positions or sizes, and even in different SVGs.</p>

<p>Each <code>&lt;symbol&gt;</code> must have its own <code>viewBox</code>, which defines its internal coordinate system. That means paying special attention to how SVG elements are exported from apps like Sketch.</p>

<div class="partners__lead-place"></div>

<h2 id="exporting-for-individual-viewboxes">Exporting For Individual Viewboxes</h2>

<p>I wrote before about <a href="https://www.smashingmagazine.com/2025/06/smashing-animations-part-4-optimising-svgs/">how I export elements</a> in layers to make working with them easier. That process is a little different when creating symbols.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png"
			
			sizes="100vw"
			alt=""
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      My usual process of exporting elements from Sketch. (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Ordinarily, I would export all my elements using the same <code>viewBox</code>size. But when I’m creating a <code>symbol</code>, I need it to have its own specific <code>viewBox</code>.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png"
			
			sizes="100vw"
			alt="Exporting elements from Sketch as individual SVG files."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Exporting elements from Sketch as individual SVG files. (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>So I export each element as an individually sized SVG, which gives me the dimensions I need to convert its content into a <code>symbol</code>. Let’s take the SVG of Quick Draw McGraw’s hat, which has a <code>viewBox</code> size of 294×182:</p>

<pre><code class="language-svg">&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 294 182"&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;
</code></pre>

<p>I swap the SVG tags for <code>&lt;symbol&gt;</code> and add its artwork to my SVG library:</p>

<pre><code class="language-svg">&lt;svg xmlns="http://www.w3.org/2000/svg" style="display: none;"&gt;
  &lt;symbol id="quick-draw-hat" viewBox="0 0 294 182"&gt;
    &lt;g class="quick-draw-hat"&gt;[…]&lt;/g&gt;
  &lt;/symbol&gt;
&lt;/svg&gt;
</code></pre>

<p>Then, I repeat the process for all the remaining elements in my artwork. Now, if I ever need to update any of my symbols, the changes will be automatically applied to every instance it’s used.</p>

<h2 id="using-a-symbol-in-multiple-svgs">Using A <code>&lt;symbol&gt;</code> In Multiple SVGs</h2>

<p>I wanted my elements to appear in both versions of Bow Wow Bandit, one arrangement for smaller screens and an alternative arrangement for larger ones. So, I create both SVGs:</p>

<pre><code class="language-svg">&lt;svg class="svg-small" viewBox="0 0 1080 1440"&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;

&lt;svg class="svg-large" viewBox="0 0 1920 1080"&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;
</code></pre>

<p>…and insert links to my symbols in both:</p>

<pre><code class="language-svg">&lt;svg class="svg-small" viewBox="0 0 1080 1440"&gt;
  &lt;use href="#quick-draw-hat" /&gt;
&lt;/svg&gt;

&lt;svg class="svg-large" viewBox="0 0 1920 1080"&gt;
  &lt;use href="#quick-draw-hat" /&gt;
&lt;/svg&gt;
</code></pre>

<h2 id="positioning-symbols">Positioning Symbols</h2>

<p>Once I’ve placed symbols into my layout using <code>&lt;use&gt;</code>, my next step is to position them, which is especially important if I want alternative layouts for different screen sizes. Symbols behave like <code>&lt;g&gt;</code> groups, so I can scale and move them using attributes like <code>width</code>, <code>height</code>, and <code>transform</code>:</p>

<div class="break-out">
<pre><code class="language-svg">&lt;svg class="svg-small" viewBox="0 0 1080 1440"&gt;
  &lt;use href="#quick-draw-hat" width="294" height="182" transform="translate(-30,610)"/&gt;
&lt;/svg&gt;

&lt;svg class="svg-large" viewBox="0 0 1920 1080"&gt;
  &lt;use href="#quick-draw-hat" width="294" height="182" transform="translate(350,270)"/&gt;
&lt;/svg&gt;
</code></pre>
</div>

<p>I can place each <code>&lt;use&gt;</code> element independently using <code>transform</code>. This is powerful because rather than repositioning elements inside my SVGs, I move the <code>&lt;use&gt;</code> references. My internal layout stays clean, and the file size remains small because I’m not duplicating artwork. A browser only loads it once, which reduces bandwidth and speeds up page rendering. And because I’m always referencing the same <code>symbol</code>, their appearance stays consistent, whatever the screen size.</p>

<h2 id="animating-use-elements">Animating <code>&lt;use&gt;</code> Elements</h2>

<p>Here’s where things got tricky. I wanted to animate parts of my characters &mdash; like Quick Draw’s hat tilting and his legs kicking. But when I added CSS animations targeting internal elements inside a <code>&lt;symbol&gt;</code>, nothing happened.</p>

<p><strong>Tip:</strong> You can animate the <code>&lt;use&gt;</code> element itself, but not elements inside the <code>&lt;symbol&gt;</code>. If you want individual parts to move, make them their own symbols and animate each <code>&lt;use&gt;</code>.</p>

<p>Turns out, you can’t style or animate a <code>&lt;symbol&gt;</code>, because <code>&lt;use&gt;</code> creates shadow DOM clones that aren’t easily targetable. So, I had to get sneaky. Inside each <code>&lt;symbol&gt;</code> in my library SVG, I added a <code>&lt;g&gt;</code> element around the part I wanted to animate:</p>

<pre><code class="language-svg">&lt;symbol id="quick-draw-hat" viewBox="0 0 294 182"&gt;
  &lt;g class="quick-draw-hat"&gt;
    &lt;!-- ... --&gt;
  &lt;/g&gt;
&lt;/symbol&gt;
</code></pre>

<p>…and animated it using an attribute substring selector, targeting the <code>href</code> attribute of the <code>use</code> element:</p>

<pre><code class="language-css">use[href="#quick-draw-hat"] {
  animation-delay: 0.5s;
  animation-direction: alternate;
  animation-duration: 1s;
  animation-iteration-count: infinite;
  animation-name: hat-rock;
  animation-timing-function: ease-in-out;
  transform-origin: center bottom;
}

@keyframes hat-rock {
from { transform: rotate(-2deg); }
to   { transform: rotate(2deg); } }
</code></pre>

<div class="partners__lead-place"></div>

<h2 id="media-queries-for-display-control">Media Queries For Display Control</h2>

<p>Once I’ve created my two visible SVGs &mdash; one for small screens and one for larger ones &mdash; the final step is deciding which version to show at which screen size. I use CSS Media Queries to hide one SVG and show the other. I start by showing the small-screen SVG by default:</p>

<pre><code class="language-css">.svg-small { display: block; }
.svg-large { display: none; }
</code></pre>

<p>Then I use a <code>min-width</code> media query to switch to the large-screen SVG at <code>64rem</code> and above:</p>

<pre><code class="language-css">@media (min-width: 64rem) {
  .svg-small { display: none; }
  .svg-large { display: block; }
}
</code></pre>

<p>This ensures there’s only ever one SVG visible at a time, keeping my layout simple and the DOM free from unnecessary clutter. And because both visible SVGs reference the same hidden <code>&lt;symbol&gt;</code> library, the browser only downloads the artwork once, regardless of how many <code>&lt;use&gt;</code> elements appear across the two layouts.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png"
			
			sizes="100vw"
			alt="The final adaptive SVG."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      View the final adaptive SVG on my <a href='https://stuffandnonsense.co.uk/toon-titles/quick-draw-3.html'>Toon Titles website</a>. (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>By combining <code>&lt;symbol&gt;</code>, <code>&lt;use&gt;</code>, CSS Media Queries, and specific transforms, I can build <strong>adaptive SVGs</strong> that reposition their elements without duplicating content, loading extra assets, or relying on JavaScript. I need to define each graphic only once in a hidden symbol library. Then I can reuse those graphics, as needed, inside several visible SVGs. With CSS doing the layout switching, the <strong>result is fast and flexible</strong>.</p>

<p>It’s a reminder that some of the most powerful techniques on the web don’t need big frameworks or complex tooling &mdash; just a bit of SVG know-how and a clever use of the basics.</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(gg, yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Andy Clarke</author><title>Ambient Animations In Web Design: Principles And Implementation (Part 1)</title><link>https://www.smashingmagazine.com/2025/09/ambient-animations-web-design-principles-implementation/</link><pubDate>Mon, 22 Sep 2025 13:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/09/ambient-animations-web-design-principles-implementation/</guid><description>Creating motion can be tricky. Too much and it’s distracting. Too little and a design feels flat. Ambient animations are the middle ground &amp;mdash; subtle, slow-moving details that add atmosphere without stealing the show. In this article, web design pioneer &lt;a href="https://stuffandnonsense.co.uk">Andy Clarke&lt;/a> introduces the concept of ambient animations and explains how to implement them.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/09/ambient-animations-web-design-principles-implementation/" />
              <title>Ambient Animations In Web Design: Principles And Implementation (Part 1)</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Ambient Animations In Web Design: Principles And Implementation (Part 1)</h1>
                  
                    
                    <address>Andy Clarke</address>
                  
                  <time datetime="2025-09-22T13:00:00&#43;00:00" class="op-published">2025-09-22T13:00:00+00:00</time>
                  <time datetime="2025-09-22T13:00:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                
                

<p>Unlike <em>timeline-based</em> animations, which tell stories across a sequence of events, or <em>interaction</em> animations that are triggered when someone touches something, <strong>ambient animations</strong> are the kind of passive movements you might not notice at first. But, they make a design look alive in subtle ways.</p>

<p>In an ambient animation, elements might subtly transition between colours, move slowly, or gradually shift position. Elements can appear and disappear, change size, or they could rotate slowly.</p>

<p>Ambient animations aren’t intrusive; they don’t demand attention, aren’t distracting, and don’t interfere with what someone’s trying to achieve when they use a product or website. They can be playful, too, making someone smile when they catch sight of them. That way, ambient animations <strong>add depth to a brand’s personality</strong>.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/1-quick-draw-mcgraw-comic-book.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="399"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/1-quick-draw-mcgraw-comic-book.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/1-quick-draw-mcgraw-comic-book.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/1-quick-draw-mcgraw-comic-book.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/1-quick-draw-mcgraw-comic-book.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/1-quick-draw-mcgraw-comic-book.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/1-quick-draw-mcgraw-comic-book.png"
			
			sizes="100vw"
			alt="A three-page spread of a Quick Draw McGraw comic book including the animated cover and first two pages."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Hanna-Barbera’s Quick Draw McGraw © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/1-quick-draw-mcgraw-comic-book.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p class="c-pre-sidenote--left">To illustrate the concept of ambient animations, I’ve recreated the cover of a <a href="https://en.wikipedia.org/wiki/Quick_Draw_McGraw"><em>Quick Draw McGraw</em></a> <a href="https://dn720005.ca.archive.org/0/items/QuickDrawMcGrawCharlton/Quick%20Draw%20McGraw%20%233%20%28Charlton%201971%29.pdf">comic book</a> (PDF) as a CSS/SVG animation. The comic was published by Charlton Comics in 1971, and, being printed, these characters didn’t move, making them ideal candidates to transform into ambient animations.</p>
<p class="c-sidenote c-sidenote--right"><strong>FYI</strong>: Original cover artist <a href="https://www.lambiek.net/artists/d/dirgo_ray.htm">Ray Dirgo</a> was best known for his work drawing Hanna-Barbera characters for Charlton Comics during the 1970s. Ray passed away in 2000 at the age of 92. He outlived Charlton Comics, which went out of business in 1986, and DC Comics acquired its characters.</p>

<p><strong>Tip</strong>: You can view the complete ambient animation <a href="https://codepen.io/malarkey/pen/NPGrWVy">code on CodePen</a>.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/2-quick-draw-mcgraw-ambient-animations.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="484"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/2-quick-draw-mcgraw-ambient-animations.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/2-quick-draw-mcgraw-ambient-animations.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/2-quick-draw-mcgraw-ambient-animations.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/2-quick-draw-mcgraw-ambient-animations.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/2-quick-draw-mcgraw-ambient-animations.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/2-quick-draw-mcgraw-ambient-animations.png"
			
			sizes="100vw"
			alt="Quick Draw McGraw ambient animations."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Quick Draw McGraw ambient animations. (<a href='https://codepen.io/malarkey/pen/NPGrWVy'>Live Demo</a>) (<a href='https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/2-quick-draw-mcgraw-ambient-animations.png'>Large preview</a>)
    </figcaption>
  
</figure>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h2 id="choosing-elements-to-animate">Choosing Elements To Animate</h2>

<p>Not everything on a page or in a graphic needs to move, and part of designing an ambient animation is <strong>knowing when to stop</strong>. The trick is to pick elements that lend themselves naturally to subtle movement, rather than forcing motion into places where it doesn’t belong.</p>

<h3 id="natural-motion-cues">Natural Motion Cues</h3>

<p>When I’m deciding what to animate, I look for natural motion cues and think about when something would move naturally in the real world. I ask myself: <em>“Does this thing have weight?”</em>, <em>“Is it flexible?”</em>, and <em>“Would it move in real life?”</em> If the answer’s <em>“yes,”</em> it’ll probably feel right if it moves. There are several motion cues in Ray Dirgo’s cover artwork.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/3-pipe-feathers-toon-title-card.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="484"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/3-pipe-feathers-toon-title-card.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/3-pipe-feathers-toon-title-card.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/3-pipe-feathers-toon-title-card.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/3-pipe-feathers-toon-title-card.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/3-pipe-feathers-toon-title-card.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/3-pipe-feathers-toon-title-card.png"
			
			sizes="100vw"
			alt="Vibrantly illustrated pipe adorned with two feathers on the end against a silhouetted toon title card."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Pipe and feathers swing slightly. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/3-pipe-feathers-toon-title-card.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>For example, the peace pipe Quick Draw’s puffing on has two feathers hanging from it. They swing slightly left and right by three degrees as the pipe moves, just like real feathers would.</p>

<div class="break-out">
<pre><code class="language-css">&#35;quick-draw-pipe {
  animation: quick-draw-pipe-rotate 6s ease-in-out infinite alternate;
}

@keyframes quick-draw-pipe-rotate {
  0% { transform: rotate(3deg); }
  100% { transform: rotate(-3deg); }
}

&#35;quick-draw-feather-1 {
  animation: quick-draw-feather-1-rotate 3s ease-in-out infinite alternate;
}

&#35;quick-draw-feather-2 {
  animation: quick-draw-feather-2-rotate 3s ease-in-out infinite alternate;
}

@keyframes quick-draw-feather-1-rotate {
  0% { transform: rotate(3deg); }
  100% { transform: rotate(-3deg); }
}

@keyframes quick-draw-feather-2-rotate {
  0% { transform: rotate(-3deg); }
  100% { transform: rotate(3deg); }
}
</code></pre>
</div>

<h3 id="atmosphere-not-action">Atmosphere, Not Action</h3>

<p>I often choose elements or decorative details that add to the vibe but don’t fight for attention.</p>

<blockquote class="pull-quote">
  <p>
    <a class="pull-quote__link" aria-label="Share on Twitter" href="https://twitter.com/share?text=%0aAmbient%20animations%20aren%e2%80%99t%20about%20signalling%20to%20someone%20where%20they%20should%20look;%20they%e2%80%99re%20about%20creating%20a%20mood.%20%0a&url=https://smashingmagazine.com%2f2025%2f09%2fambient-animations-web-design-principles-implementation%2f">
      
Ambient animations aren’t about signalling to someone where they should look; they’re about creating a mood. 

    </a>
  </p>
  <div class="pull-quote__quotation">
    <div class="pull-quote__bg">
      <span class="pull-quote__symbol">“</span></div>
  </div>
</blockquote>

<p>Here, the chief slowly and subtly rises and falls as he puffs on his pipe.</p>

<pre><code class="language-css">&#35;chief {
  animation: chief-rise-fall 3s ease-in-out infinite alternate;
}

@keyframes chief-group-rise-fall {
  0% { transform: translateY(0); }
  100% { transform: translateY(-20px); }
}
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/4-chief-toon-title-card.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="484"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/4-chief-toon-title-card.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/4-chief-toon-title-card.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/4-chief-toon-title-card.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/4-chief-toon-title-card.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/4-chief-toon-title-card.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/4-chief-toon-title-card.png"
			
			sizes="100vw"
			alt="An illustrated Indian chief seated and puffing on a pipe against a silhouetted toon title card."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The chief rises and falls as he puffs on his pipe. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/4-chief-toon-title-card.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>For added effect, the feather on his head also moves in time with his rise and fall:</p>

<div class="break-out">
<pre><code class="language-css">&#35;chief-feather-1 {
  animation: chief-feather-1-rotate 3s ease-in-out infinite alternate;
}

&#35;chief-feather-2 {
  animation: chief-feather-2-rotate 3s ease-in-out infinite alternate;
}

@keyframes chief-feather-1-rotate {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(-9deg); }
}

@keyframes chief-feather-2-rotate {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(9deg); }
}
</code></pre>
</div>

<h3 id="playfulness-and-fun">Playfulness And Fun</h3>

<p>One of the things I love most about ambient animations is how they bring fun into a design. They’re an opportunity to <strong>demonstrate personality</strong> through playful details that make people smile when they notice them.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/5-closeup-illustrated-chief-head.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="484"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/5-closeup-illustrated-chief-head.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/5-closeup-illustrated-chief-head.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/5-closeup-illustrated-chief-head.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/5-closeup-illustrated-chief-head.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/5-closeup-illustrated-chief-head.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/5-closeup-illustrated-chief-head.png"
			
			sizes="100vw"
			alt="Closeup of the illustrated chief’s head and face."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The chief’s eyebrows rise and fall, and his eyes cross. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/5-closeup-illustrated-chief-head.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Take a closer look at the chief, and you might spot his eyebrows raising and his eyes crossing as he puffs hard on his pipe. Quick Draw’s eyebrows also bounce at what look like random intervals.</p>

<pre><code class="language-css">&#35;quick-draw-eyebrow {
  animation: quick-draw-eyebrow-raise 5s ease-in-out infinite;
}

@keyframes quick-draw-eyebrow-raise {
  0%, 20%, 60%, 100% { transform: translateY(0); }
  10%, 50%, 80% { transform: translateY(-10px); }
}
</code></pre>

<div class="partners__lead-place"></div>

<h2 id="keep-hierarchy-in-mind">Keep Hierarchy In Mind</h2>

<p>Motion draws the eye, and even subtle movements have a visual weight. So, I reserve the most obvious animations for elements that I need to create the biggest impact.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/6-illustrated-duick-draw-mcgraw.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="484"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/6-illustrated-duick-draw-mcgraw.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/6-illustrated-duick-draw-mcgraw.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/6-illustrated-duick-draw-mcgraw.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/6-illustrated-duick-draw-mcgraw.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/6-illustrated-duick-draw-mcgraw.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/6-illustrated-duick-draw-mcgraw.png"
			
			sizes="100vw"
			alt="Illustrated Quick Draw McGraw holding the feather-adorned pipe with dizzy eyes veering right."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Quick Draw McGraw wobbles under the influence of his pipe. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/6-illustrated-duick-draw-mcgraw.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Smoking his pipe clearly has a big effect on Quick Draw McGraw, so to demonstrate this, I wrapped his elements &mdash; including his pipe and its feathers &mdash; within a new SVG group, and then I made that wobble.</p>

<pre><code class="language-css">&#35;quick-draw-group {
  animation: quick-draw-group-wobble 6s ease-in-out infinite;
}

@keyframes quick-draw-group-wobble {
  0% { transform: rotate(0deg); }
  15% { transform: rotate(2deg); }
  30% { transform: rotate(-2deg); }
  45% { transform: rotate(1deg); }
  60% { transform: rotate(-1deg); }
  75% { transform: rotate(0.5deg); }
  100% { transform: rotate(0deg); }
}
</code></pre>

<p>Then, to emphasise this motion, I mirrored those values to wobble his shadow:</p>

<pre><code class="language-css">&#35;quick-draw-shadow {
  animation: quick-draw-shadow-wobble 6s ease-in-out infinite;
}

@keyframes quick-draw-shadow-wobble {
  0% { transform: rotate(0deg); }
  15% { transform: rotate(-2deg); }
  30% { transform: rotate(2deg); }
  45% { transform: rotate(-1deg); }
  60% { transform: rotate(1deg); }
  75% { transform: rotate(-0.5deg); }
  100% { transform: rotate(0deg); }
}
</code></pre>

<h2 id="apply-restraint">Apply Restraint</h2>

<p>Just because something can be animated doesn’t mean it should be. When creating an ambient animation, I study the image and note the elements where subtle motion might add life. I keep in mind the questions: <em>“What’s the story I’m telling? Where does movement help, and when might it become distracting?”</em></p>

<p>Remember, restraint isn’t just about doing less; it’s about doing the right things less often.</p>

<h2 id="layering-svgs-for-export">Layering SVGs For Export</h2>

<p>In “<a href="https://www.smashingmagazine.com/2025/06/smashing-animations-part-4-optimising-svgs/">Smashing Animations Part 4: Optimising SVGs</a>,” I wrote about the process I rely on to <em>“prepare, optimise, and structure SVGs for animation.”</em> When elements are crammed into a single SVG file, they can be a nightmare to navigate. Locating a specific path or group can feel like searching for a needle in a haystack.</p>

<blockquote>That’s why I develop my SVGs in layers, exporting and optimising one set of elements at a time &mdash; always in the order they’ll appear in the final file. This lets me build the master SVG gradually by pasting it in each cleaned-up section.</blockquote>

<p>I start by exporting background elements, optimising them, adding class and ID attributes, and pasting their code into my SVG file.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/7-toon-title-card.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="484"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/7-toon-title-card.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/7-toon-title-card.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/7-toon-title-card.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/7-toon-title-card.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/7-toon-title-card.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/7-toon-title-card.png"
			
			sizes="100vw"
			alt="The toon title card with the chief and Quick Draw characters cut out with their shapes remaining."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Exporting background elements. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/7-toon-title-card.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Then, I export elements that often stay static or move as groups, like the chief and Quick Draw McGraw.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/8-quick-draw-pasted-toon-title-card.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="484"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/8-quick-draw-pasted-toon-title-card.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/8-quick-draw-pasted-toon-title-card.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/8-quick-draw-pasted-toon-title-card.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/8-quick-draw-pasted-toon-title-card.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/8-quick-draw-pasted-toon-title-card.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/8-quick-draw-pasted-toon-title-card.png"
			
			sizes="100vw"
			alt="Showing Quick Draw pasted to the toon title card’s foreground, minus details including the pipe he is holding and his eyeballs."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Exporting larger groups. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/8-quick-draw-pasted-toon-title-card.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Before finally exporting, naming, and adding details, like Quick Draw’s pipe, eyes, and his stoned sparkles.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/9-quick-draw-toon-title-card-details.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="484"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/9-quick-draw-toon-title-card-details.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/9-quick-draw-toon-title-card-details.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/9-quick-draw-toon-title-card-details.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/9-quick-draw-toon-title-card-details.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/9-quick-draw-toon-title-card-details.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/9-quick-draw-toon-title-card-details.png"
			
			sizes="100vw"
			alt="Showing Quick Draw in the same toon title card but including the details that were left out before."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Adding details. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-principles-implementation/9-quick-draw-toon-title-card-details.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Since I export each layer from the same-sized artboard, I don’t need to worry about alignment or positioning issues as they all slot into place automatically.</p>

<h2 id="implementing-ambient-animations">Implementing Ambient Animations</h2>

<p>You don’t need an animation framework or library to add ambient animations to a project. Most of the time, all you’ll need is a well-prepared SVG and some thoughtful CSS.</p>

<p>But, let’s start with the SVG. The key is to group elements logically and give them meaningful class or ID attributes, which act as animation hooks in the CSS. For this animation, I gave every moving part its own identifier like <code>#quick-draw-tail</code> or <code>#chief-smoke-2</code>. That way, I could target exactly what I needed without digging through the DOM like a raccoon in a trash can.</p>

<p>Once the SVG is set up, CSS does most of the work. I can use <code>@keyframes</code> for more expressive movement, or <code>animation-delay</code> to simulate randomness and stagger timings. The trick is to keep everything subtle and remember I’m not animating for attention, I’m animating for atmosphere.</p>

<p>Remember that most ambient animations loop continuously, so they should be <strong>lightweight</strong> and <strong>performance-friendly</strong>. And of course, <a href="https://www.smashingmagazine.com/2021/10/respecting-users-motion-preferences/">it’s good practice to respect users who’ve asked for less motion</a>. You can wrap your animations in an <code>@media prefers-reduced-motion</code> query so they only run when they’re welcome.</p>

<div class="break-out">
<pre><code class="language-javascript">@media (prefers-reduced-motion: no-preference) {
  &#35;quick-draw-shadow {
    animation: quick-draw-shadow-wobble 6s ease-in-out infinite;
  }
}
</code></pre>
</div>

<p>It’s a small touch that’s easy to implement, and it makes your designs more inclusive.</p>

<div class="partners__lead-place"></div>

<h2 id="ambient-animation-design-principles">Ambient Animation Design Principles</h2>

<p>If you want your animations to feel ambient, more like atmosphere than action, it helps to follow a few principles. These aren’t hard and fast rules, but rather things I’ve learned while animating smoke, sparkles, eyeballs, and eyebrows.</p>

<h3 id="keep-animations-slow-and-smooth">Keep Animations Slow And Smooth</h3>

<p>Ambient animations should feel relaxed, so use <strong>longer durations</strong> and choose <strong>easing curves that feel organic</strong>. I often use <code>ease-in-out</code>, but <a href="https://www.smashingmagazine.com/2022/10/advanced-animations-css/">cubic Bézier curves</a> can also be helpful when you want a more relaxed feel and the kind of movements you might find in nature.</p>

<h3 id="loop-seamlessly-and-avoid-abrupt-changes">Loop Seamlessly And Avoid Abrupt Changes</h3>

<p>Hard resets or sudden jumps can ruin the mood, so if an animation loops, ensure it cycles smoothly. You can do this by <strong>matching start and end keyframes</strong>, or by setting the <code>animation-direction</code> to <code>alternate</code> the value so the animation plays forward, then back.</p>

<h3 id="use-layering-to-build-complexity">Use Layering To Build Complexity</h3>

<p>A single animation might be boring. Five subtle animations, each on separate layers, can feel rich and alive. Think of it like building a sound mix &mdash; you want <strong>variation in rhythm, tone, and timing</strong>. In my animation, sparkles twinkle at varying intervals, smoke curls upward, feathers sway, and eyes boggle. Nothing dominates, and each motion plays its small part in the scene.</p>

<h3 id="avoid-distractions">Avoid Distractions</h3>

<p>The point of an ambient animation is that it doesn’t dominate. It’s a <strong>background element</strong> and not a call to action. If someone’s eyes are drawn to a raised eyebrow, it’s probably too much, so dial back the animation until it feels like something you’d only catch if you’re really looking.</p>

<h3 id="consider-accessibility-and-performance">Consider Accessibility And Performance</h3>

<p>Check <code>prefers-reduced-motion</code>, and don’t assume everyone’s device can handle complex animations. SVG and CSS are light, but things like blur filters and drop shadows, and complex CSS animations can still tax lower-powered devices. When an animation is purely decorative, consider adding <code>aria-hidden=&quot;true&quot;</code> to keep it from cluttering up the accessibility tree.</p>

<h2 id="quick-on-the-draw">Quick On The Draw</h2>

<p>Ambient animation is like seasoning on a great dish. It’s the pinch of salt you barely notice, but you’d miss when it’s gone. It doesn’t shout, it whispers. It doesn’t lead, it lingers. It’s floating smoke, swaying feathers, and sparkles you catch in the corner of your eye. And when it’s done well, ambient animation <strong>adds personality to a design without asking for applause</strong>.</p>

<p>Now, I realise that not everyone needs to animate cartoon characters. So, in part two, I’ll share how I created animations for several recent client projects. Until next time, if you’re crafting an illustration or working with SVG, ask yourself: <strong>What would move if this were real?</strong> Then animate just that. Make it slow and soft. Keep it ambient.</p>

<p>You can view the complete ambient animation <a href="https://codepen.io/malarkey/pen/NPGrWVy">code on CodePen</a>.</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(gg, yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Marius Sarca</author><title>Creating Elastic And Bounce Effects With Expressive Animator</title><link>https://www.smashingmagazine.com/2025/09/creating-elastic-bounce-effects-expressive-animator/</link><pubDate>Mon, 15 Sep 2025 10:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/09/creating-elastic-bounce-effects-expressive-animator/</guid><description>Elastic and bounce effects have long been among the most desirable but time-consuming techniques in motion design. Expressive Animator streamlines the process, making it possible to produce lively animations in seconds, bypassing the tedious work of manual keyframe editing.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/09/creating-elastic-bounce-effects-expressive-animator/" />
              <title>Creating Elastic And Bounce Effects With Expressive Animator</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Creating Elastic And Bounce Effects With Expressive Animator</h1>
                  
                    
                    <address>Marius Sarca</address>
                  
                  <time datetime="2025-09-15T10:00:00&#43;00:00" class="op-published">2025-09-15T10:00:00+00:00</time>
                  <time datetime="2025-09-15T10:00:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                <p>This article is sponsored by <b>Expressive</b></p>
                

<p>In the world of modern web design, SVG images are used everywhere, from illustrations to icons to background effects, and are universally prized for their crispness and lightweight size. While static SVG images play an important role in web design, most of the time their true potential is unlocked only when they are combined with motion.</p>

<p>Few things add more life and personality to a website than a well-executed SVG animation. But not all animations have the same impact in terms of digital experience. For example, <strong>elastic and bounce effects</strong> have a unique appeal in motion design because they bring a <strong>sense of realism into movement</strong>, making animations more engaging and memorable.</p>

<figure><a href="https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/grumpy-egg.gif"><img src="https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/grumpy-egg-800.gif" width="800" height="800" alt="Grumpy Egg" /></a><figcaption>(<a href="https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/grumpy-egg.gif">Large preview</a>)</figcaption></figure>

<p>However, anyone who has dived into animating SVGs knows <a href="https://www.smashingmagazine.com/2023/02/putting-gears-motion-animating-cars-with-html-svg/">the technical hurdles involved</a>. Creating a convincing elastic or bounce effect traditionally requires handling complex CSS keyframes or wrestling with JavaScript animation libraries. Even when using an SVG animation editor, it will most likely require you to manually add the keyframes and adjust the easing functions between them, which can become a time-consuming process of trial and error, no matter the level of experience you have.</p>

<p>This is where Expressive Animator shines. It allows creators to apply elastic and bounce effects <strong>in seconds</strong>, bypassing the tedious work of manual keyframe editing. And the result is always exceptional: animations that feel <em>alive</em>, produced with a fraction of the effort.</p>

<h2 id="using-expressive-animator-to-create-an-elastic-effect">Using Expressive Animator To Create An Elastic Effect</h2>

<p>Creating an elastic effect in Expressive Animator is remarkably simple, fast, and intuitive, since the effect is built right into the software as an easing function. This means you only need two keyframes (start and end) to make the effect, and the software will automatically handle the springy motion in between. Even better, the elastic easing can be applied to <strong>any animatable property</strong> (e.g., position, scale, rotation, opacity, morph, etc.), giving you a consistent way to add it to your animations.</p>

<p>Before we dive into the tutorial, take a look at the video below to see what you will learn to create and the entire process from start to finish.</p>


<figure class="video-embed-container break-out">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/1116135653"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
</figure>

<p>First things first, let’s set the scene. For this, we’ll <a href="https://expressive.app/expressive-animator/docs/v1/projects/create/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">create a new project</a> by pressing <kbd>Ctrl</kbd>/<kbd>Cmd</kbd> + <kbd>P</kbd> and configuring it in the “Create New Project” dialog that pops up. For frame size, we’ll choose 1080×1080, for a duration of 00:01:30, and we’ll let the frame rate remain unchanged at 60 frames per second (fps).</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/01-create-dialog.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="467"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/01-create-dialog.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/01-create-dialog.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/01-create-dialog.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/01-create-dialog.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/01-create-dialog.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/01-create-dialog.png"
			
			sizes="100vw"
			alt="“Create New Project” dialog"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/01-create-dialog.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Once you hit the “Create project” button, you can use the <a href="https://expressive.app/expressive-animator/docs/v1/tools/pen-tool/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">Pen</a> and <a href="https://expressive.app/expressive-animator/docs/v1/tools/ellipse-tool/">Ellipse</a> tools to create the artwork that will be animated, or you can simply copy and paste the artwork below.</p>

<figure class="break-out">
	<p data-height="600"
	data-theme-id="light"
	data-slug-hash="pvjmwxv"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Effects With Expressive Animator - Artwork for Animation](https://codepen.io/smashingmag/pen/pvjmwxv).</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/pvjmwxv">Effects With Expressive Animator - Artwork for Animation</a>.</figcaption>
</figure>

<p>Now that everything has been set up, let’s create the animation. Make sure that snapping and auto-record are enabled, then move the playhead to 01:00f. By <a href="https://expressive.app/expressive-animator/docs/v1/canvas/snapping/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">enabling snapping</a>, you will be able to perfectly align nodes and graphic objects on the canvas. On the other hand, as the name suggests, auto-record tracks every change you make to the artwork and adds the appropriate keyframes on the timeline.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/02-prepare-scene.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="467"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/02-prepare-scene.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/02-prepare-scene.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/02-prepare-scene.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/02-prepare-scene.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/02-prepare-scene.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/02-prepare-scene.png"
			
			sizes="100vw"
			alt="Screenshot with snapping and auto-record are enabled and the playhead moved to 01:00f"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/02-prepare-scene.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Press the <kbd>A</kbd> key on your keyboard to switch to the <a href="https://expressive.app/expressive-animator/docs/v1/tools/node-tool/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">Node tool</a>, then select the String object and move its handle to the center-right point of the artboard. Don’t worry about precision, as the snapping will do all the heavy lifting for you. This will bend the shape and add keyframes for the Morph animator.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/03-string.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="467"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/03-string.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/03-string.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/03-string.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/03-string.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/03-string.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/03-string.png"
			
			sizes="100vw"
			alt="Screenshot with the String object and its handle moved to the center-right point of the artboard"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/03-string.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Next, press the <kbd>V</kbd> key on your keyboard to switch to the <a href="https://expressive.app/expressive-animator/docs/v1/tools/selection-tool/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">Selection tool</a>. With this tool enabled, select the Ball, move it to the right, and place it in the middle of the string. Once again, snapping will do all the hard work, allowing you to position the ball exactly where you want to, while auto-recording automatically adds the appropriate keyframes.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/04-ball.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="467"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/04-ball.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/04-ball.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/04-ball.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/04-ball.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/04-ball.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/04-ball.png"
			
			sizes="100vw"
			alt="Screenshot with the Ball selected and moved to the middle of the string"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/04-ball.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>You can now replay the animation and disable auto-recording by clicking on the Auto-Record button again.</p>

<p>As you can see when replaying, the direction in which the String and Ball objects are moving is wrong. Fortunately, we can fix this extremely easily just by reversing the keyframes. To do this, select the keyframes in the timeline and right-click to open the context menu and choose Reverse. This will reverse the keyframes, and if you replay the animation, you will see that the direction is now correct.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/05-reverse.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="467"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/05-reverse.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/05-reverse.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/05-reverse.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/05-reverse.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/05-reverse.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/05-reverse.png"
			
			sizes="100vw"
			alt="Screenshot with the context menu where you can choose Reverse"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/05-reverse.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>With this out of the way, we can finally add the elastic effect. Select all the keyframes in the timeline and click on the Custom easing button to open a dialog with easing options. From the dialog, choose Elastic and set the oscillations to 4 and the stiffness to 2.5.</p>

<p>That’s it! Click anywhere outside the easing dialog to close it and replay the animation to see the result.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/06-effect.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="467"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/06-effect.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/06-effect.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/06-effect.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/06-effect.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/06-effect.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/06-effect.png"
			
			sizes="100vw"
			alt="Selected custom easing button that opened a dialog with easing options"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/06-effect.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p><a href="https://expressive.app/expressive-animator/docs/v1/export/svg/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">The animation can be exported as well.</a> Press <kbd>Cmd</kbd>/<kbd>Ctrl</kbd> + <kbd>E</kbd> on your keyboard to open the export dialog and choose from various export options, ranging from vectorized formats, such as <a href="https://expressive.app/expressive-animator/docs/v1/export/svg/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">SVG</a> and <a href="https://expressive.app/expressive-animator/docs/v1/export/lottie/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">Lottie</a>, to rasterized formats, such as <a href="https://expressive.app/expressive-animator/docs/v1/export/image/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">GIF</a> and <a href="https://expressive.app/expressive-animator/docs/v1/export/video/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">video</a>.</p>

<p>For this specific animation, we’re going to choose the SVG export format. Expressive Animator allows you to choose between three different types of SVG, depending on the technology used for animation: <a href="https://expressive.app/expressive-animator/docs/v1/export/svg/smil/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">SMIL</a>, <a href="https://expressive.app/expressive-animator/docs/v1/export/svg/css/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">CSS</a>, or <a href="https://expressive.app/expressive-animator/docs/v1/export/svg/js/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">JavaScript</a>.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/07-export.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="467"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/07-export.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/07-export.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/07-export.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/07-export.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/07-export.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/07-export.png"
			
			sizes="100vw"
			alt="Export settings in the Expressive Animator"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/07-export.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Each of these technologies has different strengths and weaknesses, but for this tutorial, we are going to choose SMIL. This is because SMIL-based animations are widely supported, even on Safari browsers, and can be used as background images or embedded in HTML pages using the <code>&lt;img&gt;</code>  tag. In fact, <a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-3-smil-not-dead/">Andy Clarke recently wrote all about SMIL animations here at Smashing Magazine</a> if you want a full explanation of how it works.</p>

<p>You can visualize the exported SVG in the following CodePen demo:</p>

<figure class="break-out">
	<p data-height="600"
	data-theme-id="light"
	data-slug-hash="GgpaEyG"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Expressive Animator - Exported SVG](https://codepen.io/smashingmag/pen/GgpaEyG).</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/GgpaEyG">Expressive Animator - Exported SVG</a>.</figcaption>
</figure>

<h2 id="expressive-animator-for-bounce-and-other-effects">Expressive Animator For Bounce And Other Effects</h2>

<p>Adding a bounce effect to an animation is very similar to the process we just covered for creating an elastic effect, since both are built into Expressive Animator as easing functions. Just like elastic, bounce easing can be applied to any animatable property, giving you quick ways to create realistic motion.</p>

<p>Beyond these two effects, Expressive Animator also offers other easing options that can shape the personality of your animation, like Back, Steps, Sinc, just to name a few.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/08-easing-functions.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="757"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/08-easing-functions.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/08-easing-functions.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/08-easing-functions.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/08-easing-functions.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/08-easing-functions.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/08-easing-functions.png"
			
			sizes="100vw"
			alt="Easing functions in the Expressive Animator"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/08-easing-functions.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h2 id="conclusion">Conclusion</h2>

<p>Elastic and bounce effects have long been among the most desirable but time-consuming techniques in motion design. By integrating them directly into its easing functions, Expressive Animator removes the complexity of manual keyframe manipulation and transforms what used to be a technical challenge into a creative opportunity.</p>

<p>The best part is that getting started with Expressive Animator comes with zero risk. The software offers a full 7&ndash;day <strong>free trial without requiring an account</strong>, so you can download it instantly and begin experimenting with your own designs right away. After the trial ends, you can buy Expressive Animator with a one-time payment, <strong>no subscription required</strong>. This will give you a perpetual license covering both Windows and macOS.</p>

<p>To help you get started even faster, I’ve prepared some extra resources for you. You’ll find the source files for the animations created in this tutorial, along with a curated list of useful links that will guide you further in exploring Expressive Animator and SVG animation. These materials are meant to give you a solid starting point so you can learn, experiment, and build on your own with confidence.</p>

<ul>
<li>Grumpy Egg: The <a href="https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/grumpy-egg.eaf" download><code>.eaf</code></a> source file for the sample animation presented at the beginning of this article.</li>
<li>Elastic Effect: Another <a href="https://files.smashing.media/articles/creating-elastic-bounce-effects-expressive-animator/elastic-effect.eaf" download><code>.eaf</code></a> file, this time for the animation we made in this tutorial.</li>
<li><a href="https://expressive.app/expressive-animator/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">Get started with Expressive Animator</a></li>
<li>Expressive Animator <a href="https://expressive.app/expressive-animator/docs/v1/?utm_source=smashingmagazine&amp;utm_medium=blog&amp;utm_campaign=elastic_effect">Documentation</a></li>
</ul>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(gg, yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Myriam Frisano</author><title>Decoding The SVG &lt;code>path&lt;/code> Element: Line Commands</title><link>https://www.smashingmagazine.com/2025/06/decoding-svg-path-element-line-commands/</link><pubDate>Mon, 09 Jun 2025 08:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/06/decoding-svg-path-element-line-commands/</guid><description>SVG is easy — until you meet &lt;code>path&lt;/code>. However, it’s not as confusing as it initially looks. In this first installment of a pair of articles, Myriam Frisano aims to teach you the basics of &lt;code>&amp;lt;path&amp;gt;&lt;/code> and its sometimes mystifying commands. With simple examples and visualizations, she’ll help you understand the easy syntax and underlying rules of SVG’s most powerful element so that by the end, you’re fully able to translate SVG semantic tags into a language &lt;code>path&lt;/code> understands.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/06/decoding-svg-path-element-line-commands/" />
              <title>Decoding The SVG &lt;code&gt;path&lt;/code&gt; Element: Line Commands</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Decoding The SVG &lt;code&gt;path&lt;/code&gt; Element: Line Commands</h1>
                  
                    
                    <address>Myriam Frisano</address>
                  
                  <time datetime="2025-06-09T08:00:00&#43;00:00" class="op-published">2025-06-09T08:00:00+00:00</time>
                  <time datetime="2025-06-09T08:00:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                
                

<p>In a previous article, we looked at some <a href="https://www.smashingmagazine.com/2024/09/svg-coding-examples-recipes-writing-vectors-by-hand/">practical examples of how to code SVG by hand</a>. In that guide, we covered the basics of the SVG elements <code>rect</code>, <code>circle</code>, <code>ellipse</code>, <code>line</code>, <code>polyline</code>, and <code>polygon</code> (and also <code>g</code>).</p>

<p>This time around, we are going to tackle a more advanced topic, the absolute powerhouse of SVG elements: <code>path</code>. Don’t get me wrong; I still stand by my point that image paths are better drawn in vector programs than coded (unless you’re the type of creative who makes non-logical visual art in code &mdash; then go forth and create awe-inspiring wonders; you’re probably not the audience of this article). But when it comes to <strong>technical drawings</strong> and <strong>data visualizations</strong>, the <code>path</code> element unlocks a wide array of possibilities and opens up the world of hand-coded SVGs.</p>

<p>The path syntax can be really complex. We’re going to tackle it in two separate parts. In this first installment, we’re learning all about <strong>straight and angular paths</strong>. In the second part, we’ll make lines bend, twist, and turn.</p>

<h2 id="required-knowledge-and-guide-structure">Required Knowledge And Guide Structure</h2>

<p><strong>Note</strong>: <em>If you are unfamiliar with the basics of SVG, such as the subject of <code>viewBox</code> and the basic syntax of the simple elements (<code>rect</code>, <code>line</code>, <code>g</code>, and so on), I recommend reading <a href="https://www.smashingmagazine.com/2024/09/svg-coding-examples-recipes-writing-vectors-by-hand/">my guide</a> before diving into this one. You should also <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/text">familiarize yourself with <code>&lt;text&gt;</code></a> if you want to understand each line of code in the examples.</em></p>

<p>Before we get started, I want to quickly recap how I code SVG using JavaScript. I don’t like dealing with numbers and math, and reading SVG Code with numbers filled into every attribute makes me lose all understanding of it. By giving coordinates names and having all my math easy to parse and write out, I have a much better time with this type of code, and I think you will, too.</p>

<p>The goal of this article is more about <strong>understanding <code>path</code> syntax</strong> than it is about doing placement or how to leverage loops and other more basic things. So, I will not run you through the entire setup of each example. I’ll instead share snippets of the code, but they may be slightly adjusted from the CodePen or simplified to make this article easier to read. However, if there are specific questions about code that are not part of the text in the CodePen demos, the comment section is open.</p>

<p>To keep this all framework-agnostic, the code is written in vanilla JavaScript (though, really, TypeScript is your friend the more complicated your SVG becomes, and I missed it when writing some of these).</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p><strong>Web forms</strong> are at the center of every meaningful interaction. Meet Adam Silver&rsquo;s <strong><a href="https://www.smashingmagazine.com/printed-books/form-design-patterns/">Form Design Patterns</a></strong>, a practical guide to <strong>designing and building forms</strong> for the web.</p>
<a data-instant href="https://www.smashingmagazine.com/printed-books/form-design-patterns/" class="btn btn--green btn--large" style="">Jump to table of contents&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="https://www.smashingmagazine.com/printed-books/form-design-patterns/" class="feature-panel-image-link">
<div class="feature-panel-image"><picture><source type="image/avif" srcSet="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/64e57b41-b7f1-4ae3-886a-806cce580ef9/form-design-patterns-shop-image-1-1.avif" />
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/51e0f837-d85d-4b28-bfab-1c9a47f0ce33/form-design-patterns-shop-image.png"
    alt="Feature Panel"
    width="481"
    height="698"
/>
</picture>
</div>
</a>
</div>
</aside>
</div>

<h2 id="setting-up-for-success">Setting Up For Success</h2>

<p>As the <code>path</code> element relies on our understanding of some of the coordinates we plug into the commands, I think it is a lot easier if we have a bit of visual orientation. So, all of the examples will be coded on top of a visual representation of a traditional <code>viewBox</code> setup with the origin in the top-left corner (so, values in the shape of <code>0 0 ${width} ${height}</code>.</p>

<p>I added text labels as well to make it easier to point you to specific areas within the grid.</p>

<blockquote>Please note that I recommend being careful when adding text within the <code>&lt;text&gt;</code> element in SVG if you want your text to be accessible. If the graphic relies on text scaling like the rest of your website, it would be better to have it rendered through HTML. But for our examples here, it should be sufficient.</blockquote>

<p>So, this is what we’ll be plotting on top of:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="MYwEdVN"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG Viewbox Grid Visual [forked]](https://codepen.io/smashingmag/pen/MYwEdVN) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/MYwEdVN">SVG Viewbox Grid Visual [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<p>Alright, we now have a ViewBox Visualizing Grid. I think we’re ready for our first session with the beast.</p>

<h2 id="enter-path-and-the-all-powerful-d-attribute">Enter <code>path</code> And The All-Powerful <code>d</code> Attribute</h2>

<p>The <code>&lt;path&gt;</code> element has a <code>d</code> attribute, which speaks its own language. So, within <code>d</code>, you’re talking in terms of “commands”.</p>

<p>When I think of <code>non-path</code> versus <code>path</code> elements, I like to think that the reason why we have to write much more complex drawing instructions is this: <strong>All non-path elements are just dumber paths.</strong> In the background, they have one pre-drawn path shape that they will always render based on a few parameters you pass in. But <code>path</code> has no default shape. The shape logic has to be exposed to you, while it can be neatly hidden away for all other elements.</p>

<p>Let’s learn about those commands.</p>

<h2 id="where-it-all-begins-m">Where It All Begins: <code>M</code></h2>

<p>The first, which is where each path begins, is the <code>M</code> command, which moves the pen to a point. This command places your starting point, but it <strong>does not draw a single thing</strong>. A path with just an <code>M</code> command is an <code>auto-delete</code> when cleaning up SVG files.</p>

<p>It takes two arguments: the <code>x</code> and <code>y</code> coordinates of your start position.</p>

<pre><code class="language-javascript">const uselessPathCommand = `M${start.x} ${start.y}`;
</code></pre>

<h2 id="basic-line-commands-m-l-h-v">Basic Line Commands: <code>M</code> , <code>L</code>, <code>H</code>, <code>V</code></h2>

<p>These are fun and easy: <code>L</code>, <code>H</code>, and <code>V</code>, all draw a line from the current point to the point specified.</p>

<p><code>L</code> takes <strong>two arguments</strong>, the <code>x</code> and <code>y</code> positions of the point you want to draw to.</p>

<pre><code class="language-javascript">const pathCommandL = `M${start.x} ${start.y} L${end.x} ${end.y}`;
</code></pre>

<p><code>H</code> and <code>V</code>, on the other hand, only take <strong>one argument</strong> because they are only drawing a line in one direction. For <code>H</code>, you specify the <code>x</code> position, and for <code>V</code>, you specify the <code>y</code> position. The other value is implied.</p>

<pre><code class="language-javascript">const pathCommandH = `M${start.x} ${start.y} H${end.x}`;
const pathCommandV = `M${start.x} ${start.y} V${end.y}`;
</code></pre>

<p>To visualize how this works, I created a function that draws the path, as well as points with labels on them, so we can see what happens.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="azOLrjZ"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Simple Lines with path [forked]](https://codepen.io/smashingmag/pen/azOLrjZ) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/azOLrjZ">Simple Lines with path [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<p>We have three lines in that image. The <code>L</code> command is used for the red path. It starts with <code>M</code> at <code>(10,10)</code>, then moves diagonally down to <code>(100,100)</code>. The command is: <code>M10 10 L100 100</code>.</p>

<p>The blue line is horizontal. It starts at <code>(10,55)</code> and should end at <code>(100, 55)</code>. We could use the <code>L</code> command, but we’d have to write <code>55</code> again. So, instead, we write <code>M10 55 H100</code>, and then SVG knows to look back at the <code>y</code> value of <code>M</code> for the <code>y</code> value of <code>H</code>.</p>

<p>It’s the same thing for the green line, but when we use the <code>V</code> command, SVG knows to refer back to the <code>x</code> value of <code>M</code> for the <code>x</code> value of <code>V</code>.</p>

<p>If we compare the resulting horizontal path with the same implementation in a <code>&lt;line&gt;</code> element, we may</p>

<ol>
<li>Notice how much more efficient <code>path</code> can be, and</li>
<li>Remove quite a bit of meaning for anyone who doesn’t speak <code>path</code>.</li>
</ol>

<p>Because, as we look at these strings, one of them is called “line”. And while the rest doesn’t mean anything out of context, the line definitely conjures a specific image in our heads.</p>

<pre><code class="language-svg">&lt;path d="M 10 55 H 100" /&gt;
&lt;line x1="10" y1="55" x2="100" y2="55" /&gt;
</code></pre>

<div class="partners__lead-place"></div>

<h2 id="making-polygons-and-polylines-with-z">Making Polygons And Polylines With <code>Z</code></h2>

<p>In the previous section, we learned how <code>path</code> can behave like <code>&lt;line&gt;</code>, which is pretty cool. But it can do more. It can also act like <code>polyline</code> and <code>polygon</code>.</p>

<p>Remember, how those two basically work the same, but <code>polygon</code> connects the first and last point, while <code>polyline</code> does not? The <code>path</code> element can do the same thing. There is a separate command to close the path with a line, which is the <code>Z</code> command.</p>

<div class="break-out">
<pre><code class="language-javascript">const polyline2Points = `M${start.x} ${start.y} L${p1.x} ${p1.y} L${p2.x} ${p2.y}`;
const polygon2Points  = `M${start.x} ${start.y} L${p1.x} ${p1.y} L${p2.x} ${p2.y} Z`;
</code></pre>
</div>

<p>So, let’s see this in action and create a repeating triangle shape. Every odd time, it’s open, and every even time, it’s closed. Pretty neat!</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="emNGaPm"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Alternating Triangles [forked]](https://codepen.io/smashingmag/pen/emNGaPm) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/emNGaPm">Alternating Triangles [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<p>When it comes to comparing <code>path</code> versus <code>polygon</code> and <code>polyline</code>, the other tags tell us about their names, but I would argue that fewer people know what a polygon is versus what a line is (and probably even fewer know what a polyline is. Heck, even the program I’m writing this article in tells me polyline is not a valid word). The argument to use these two tags over <code>path</code> for legibility is weak, in my opinion, and I guess you’d probably agree that this looks like equal levels of meaningless string given to an SVG element.</p>

<pre><code class="language-svg">&lt;path d="M0 0 L86.6 50 L0 100 Z" /&gt;
&lt;polygon points="0,0 86.6,50 0,100" /&gt;

&lt;path d="M0 0 L86.6 50 L0 100" /&gt;
&lt;polyline points="0,0 86.6,50 0,100" /&gt;
</code></pre>

<h2 id="relative-commands-m-l-h-v">Relative Commands: <code>m</code>, <code>l</code>, <code>h</code>, <code>v</code></h2>

<p>All of the line commands exist in absolute and relative versions. The difference is that the relative commands are lowercase, e.g., <code>m</code>, <code>l</code>, <code>h</code>, and <code>v</code>. The relative commands are always relative to the last point, so instead of declaring an <code>x</code> value, you’re declaring a <code>dx</code> value, saying this is how many units you’re moving.</p>

<p>Before we look at the example visually, I want you to look at the following three-line commands. Try not to look at the CodePen beforehand.</p>

<pre><code class="language-javascript">const lines = [
  { d: `M10 10 L 10 30 L 30 30`, color: "var(--_red)" },
  { d: `M40 10 l 0 20 l 20 0`, color: "var(--_blue)" },
  { d: `M70 10 l 0 20 L 90 30`, color: "var(--_green)" }
];
</code></pre>

<p>As I mentioned, I hate looking at numbers without meaning, but there is one number whose meaning is pretty constant in most contexts: <code>0</code>. Seeing a <code>0</code> in combination with a command I just learned means <em>relative</em> manages to instantly tell me that nothing is happening. Seeing <code>l 0 20</code> by itself tells me that this line only moves along one axis instead of two.</p>

<p>And looking at that entire blue path command, the repeated <code>20</code> value gives me a sense that the shape might have some regularity to it. The first path does a bit of that by repeating <code>10</code> and <code>30</code>. But the third? As someone who can’t do math in my head, that third string gives me <em>nothing</em>.</p>

<p>Now, you might be surprised, but they all draw the same shape, just in different places.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="vEOewQp"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG Compound Paths [forked]](https://codepen.io/smashingmag/pen/vEOewQp) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/vEOewQp">SVG Compound Paths [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<p>So, how valuable is it that we can recognize the regularity in the blue path? Not very, in my opinion. In some cases, going with the relative value is easier than an absolute one. In other cases, the absolute is king. Neither is better nor worse.</p>

<blockquote>And, in all cases, that previous example would be much more efficient if it were set up with a variable for the gap, a variable for the shape size, and a function to generate the path definition that’s called from within a loop so it can take in the index to properly calculate the start point.</blockquote>

<div class="partners__lead-place"></div>

<h2 id="jumping-points-how-to-make-compound-paths">Jumping Points: How To Make Compound Paths</h2>

<p>Another very useful thing is something you don’t see visually in the previous CodePen, but it relates to the grid and its code.</p>

<p>I snuck in a grid drawing update.</p>

<p>With the method used in earlier examples, using <code>line</code> to draw the grid, the above CodePen would’ve rendered the grid with 14 separate elements. If you go and inspect the final code of that last CodePen, you’ll notice that there is just a single path element within the <code>.grid</code> group.</p>

<p>It looks like this, which is not fun to look at but holds the secret to how it’s possible:</p>

<div class="break-out">
<pre><code class="language-svg">&lt;path d="M0 0 H110 M0 10 H110 M0 20 H110 M0 30 H110 M0 0 V45 M10 0 V45 M20 0 V45 M30 0 V45 M40 0 V45 M50 0 V45 M60 0 V45 M70 0 V45 M80 0 V45 M90 0 V45" stroke="currentColor" stroke-width="0.2" fill="none"&gt;&lt;/path&gt;
</code></pre>
</div>

<p>If we take a close look, we may notice that there are multiple <code>M</code> commands. This is the magic of compound paths.</p>

<blockquote>Since the <code>M/m</code> commands don’t actually draw and just place the cursor, a <code>path</code> can have jumps.</blockquote>

<p>So, whenever we have multiple paths that share common styling and don’t need to have separate interactions, we can just chain them together to make our code shorter.</p>

<h2 id="coming-up-next">Coming Up Next</h2>

<p>Armed with this knowledge, we’re now able to replace <code>line</code>, <code>polyline</code>, and <code>polygon</code> with <code>path</code> commands and combine them in compound paths. But there is so much more to uncover because <code>path</code> doesn’t just offer foreign-language versions of lines but also gives us the option to code <code>circles</code> and <code>ellipses</code> that have open space and can sometimes also bend, twist, and turn. We’ll refer to those as <em>curves</em> and <em>arcs</em>, and discuss them more explicitly in the next article.</p>

<h3 id="further-reading-on-smashingmag">Further Reading On SmashingMag</h3>

<ul>
<li>“<a href="https://www.smashingmagazine.com/2024/12/mastering-svg-arcs/">Mastering SVG Arcs</a>,” Akshay Gupta</li>
<li>“<a href="https://www.smashingmagazine.com/2021/05/accessible-svg-patterns-comparison/">Accessible SVGs: Perfect Patterns For Screen Reader Users</a>,” Carie Fisher</li>
<li>“<a href="https://www.smashingmagazine.com/2023/01/svg-customization-animation-practical-guide/">Easy SVG Customization And Animation: A Practical Guide</a>,” Adrian Bece</li>
<li>“<a href="https://www.smashingmagazine.com/2022/05/magical-svg-techniques/">Magical SVG Techniques</a>,” Cosima Mielke</li>
</ul>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(gg, yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Andy Clarke</author><title>Smashing Animations Part 4: Optimising SVGs</title><link>https://www.smashingmagazine.com/2025/06/smashing-animations-part-4-optimising-svgs/</link><pubDate>Wed, 04 Jun 2025 08:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/06/smashing-animations-part-4-optimising-svgs/</guid><description>What’s the best way to make your SVGs faster, simpler, and more manageable? In this article, pioneering author and web designer &lt;a href="https://stuffandnonsense.co.uk/">Andy Clarke&lt;/a> explains the process he relies on &lt;em>to&lt;/em> prepare, optimise, and structure SVGs for animation and beyond.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/06/smashing-animations-part-4-optimising-svgs/" />
              <title>Smashing Animations Part 4: Optimising SVGs</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Smashing Animations Part 4: Optimising SVGs</h1>
                  
                    
                    <address>Andy Clarke</address>
                  
                  <time datetime="2025-06-04T08:00:00&#43;00:00" class="op-published">2025-06-04T08:00:00+00:00</time>
                  <time datetime="2025-06-04T08:00:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                
                

<p>SVG animations take me back to the Hanna-Barbera cartoons I watched as a kid. Shows like <em>Wacky Races</em>, <em>The Perils of Penelope Pitstop</em>, and, of course, <a href="https://en.wikipedia.org/wiki/Yogi_Bear"><em>Yogi Bear</em></a>. They inspired me to lovingly recreate some classic <a href="https://stuffandnonsense.co.uk/toon-titles">Toon Titles</a> using CSS, SVG, and SMIL animations.</p>

<p>But getting animations to load quickly and work smoothly needs more than nostalgia. It takes clean design, lean code, and a process that makes complex SVGs easier to animate. Here’s how I do it.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://stuffandnonsense.co.uk/toon-titles">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/1-toon-titles.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/1-toon-titles.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/1-toon-titles.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/1-toon-titles.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/1-toon-titles.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/1-toon-titles.png"
			
			sizes="100vw"
			alt="An example of Toon Titles from the website"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      There’s now a website where you can see all my <a href='https://stuffandnonsense.co.uk/toon-titles'>Toon Titles</a>. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/1-toon-titles.png'>Large preview</a>)
    </figcaption>
  
</figure>

<div class="refs">
  <ul><li><a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-1-classic-cartoons-inspire-css/">Smashing Animations Part 1: How Classic Cartoons Inspire Modern CSS</a></li><li><a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-2-css-masking-add-extra-dimension/">Smashing Animations Part 2: How CSS Masking Can Add An Extra Dimension</a></li><li><a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-3-smil-not-dead/">Smashing Animations Part 3: SMIL’s Not Dead Baby, SMIL’s Not Dead</a></li></ul>
</div>

<p>Whether for personal projects or commercial work, preparing SVGs well ensures they’re accessible. Optimising them ensures they load quickly, especially on mobile, and thinking carefully about how they’re structured makes maintaining them easier. I’ve developed a <strong>process that balances visuals with accessibility and performance</strong> and makes complex SVGs easier to work with.</p>

<p>So, to explain my process, I’ve chosen an episode of <em>The Yogi Bear Show</em> called “Bewitched Bear,” first broadcast in January 1960. In this story, Yogi steals a witch’s broom to help him grab “pic-a-nic” baskets.</p>

<p>“Hey, hey, hey!”</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/2-yogi-bear-bewitched-bear.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="540"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/2-yogi-bear-bewitched-bear.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/2-yogi-bear-bewitched-bear.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/2-yogi-bear-bewitched-bear.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/2-yogi-bear-bewitched-bear.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/2-yogi-bear-bewitched-bear.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/2-yogi-bear-bewitched-bear.png"
			
			sizes="100vw"
			alt="An illustration from the “Bewitched Bear” episode of The Yogi Bear Show where bear is on a witch’s broom"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The Yogi Bear Show © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/2-yogi-bear-bewitched-bear.png'>Large preview</a>)
    </figcaption>
  
</figure>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h2 id="start-clean-and-design-with-optimisation-in-mind">Start Clean And Design With Optimisation In Mind</h2>

<p>Keeping things simple is key to making SVGs that are optimised and ready to animate. Tools like Adobe Illustrator convert bitmap images to vectors, but the output often contains too many extraneous groups, layers, and masks. Instead, I start cleaning in Sketch, work from a reference image, and use the Pen tool to create paths.</p>

<blockquote><strong>Tip</strong>: <a href="https://affinity.serif.com/en-gb/designer/">Affinity Designer</a> (UK) and <a href="https://www.sketch.com">Sketch</a> (Netherlands) are alternatives to Adobe Illustrator and Figma. Both are independent and based in Europe. Sketch has been my default design app since Adobe killed Fireworks.</blockquote>

<h2 id="beginning-with-outlines">Beginning With Outlines</h2>

<p>For these Toon Titles illustrations, I first use the Pen tool to draw black outlines with as few anchor points as possible. The more points a shape has, the bigger a file becomes, so simplifying paths and reducing the number of points makes an SVG much smaller, often with no discernible visual difference.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/3-outlines-anchor-points.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/3-outlines-anchor-points.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/3-outlines-anchor-points.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/3-outlines-anchor-points.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/3-outlines-anchor-points.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/3-outlines-anchor-points.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/3-outlines-anchor-points.png"
			
			sizes="100vw"
			alt="Two outlines with different anchor points"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      <strong>Left</strong>: 160 anchor points. <strong>Right</strong>: 80 points. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/3-outlines-anchor-points.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Bearing in mind that parts of this Yogi illustration will ultimately be animated, I keep outlines for this Bewitched Bear’s body, head, collar, and tie separate so that I can move them independently. The head might nod, the tie could flap, and, like in those classic cartoons, Yogi’s collar will hide the joins between them.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/4-separate-outlines.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/4-separate-outlines.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/4-separate-outlines.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/4-separate-outlines.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/4-separate-outlines.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/4-separate-outlines.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/4-separate-outlines.png"
			
			sizes="100vw"
			alt="Separate outlines for body, head, collar and tie, and broom."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Separate outlines for body, head, collar and tie, and broom. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/4-separate-outlines.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h2 id="drawing-simple-background-shapes">Drawing Simple Background Shapes</h2>

<p>With the outlines in place, I use the Pen tool again to draw new shapes, which fill the areas with colour. These colours sit behind the outlines, so they don’t need to match them exactly. The fewer anchor points, the smaller the file size.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/5-simple-background-shapes.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/5-simple-background-shapes.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/5-simple-background-shapes.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/5-simple-background-shapes.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/5-simple-background-shapes.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/5-simple-background-shapes.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/5-simple-background-shapes.png"
			
			sizes="100vw"
			alt="Original vector artwork and a simplified version with Adobe Illustrator"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      <strong>Left</strong>: Original vector artwork, 8 Kb. <strong>Right</strong>: Simplified using Adobe Illustrator, 2 Kb. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/5-simple-background-shapes.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Sadly, neither Affinity Designer nor Sketch has tools that can simplify paths, but if you have it, using Adobe Illustrator can shave a few extra kilobytes off these background shapes.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/6-adobe-illustrator-simplify-paths.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/6-adobe-illustrator-simplify-paths.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/6-adobe-illustrator-simplify-paths.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/6-adobe-illustrator-simplify-paths.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/6-adobe-illustrator-simplify-paths.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/6-adobe-illustrator-simplify-paths.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/6-adobe-illustrator-simplify-paths.png"
			
			sizes="100vw"
			alt="An illustration how to simplify paths with Adobe Illustrator"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Adobe Illustrator: Object → Path → Simplify. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/6-adobe-illustrator-simplify-paths.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h2 id="optimising-the-code">Optimising The Code</h2>

<p>It’s not just metadata that makes SVG bulkier. The way you export from your design app also affects file size.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/7-vector-artwork-ready-optimisation.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/7-vector-artwork-ready-optimisation.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/7-vector-artwork-ready-optimisation.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/7-vector-artwork-ready-optimisation.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/7-vector-artwork-ready-optimisation.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/7-vector-artwork-ready-optimisation.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/7-vector-artwork-ready-optimisation.png"
			
			sizes="100vw"
			alt="Vector artwork ready for optimisation."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Vector artwork ready for optimisation. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/7-vector-artwork-ready-optimisation.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Exporting just those simple background shapes from Adobe Illustrator includes unnecessary groups, masks, and bloated path data by default. Sketch’s code is barely any better, and there’s plenty of room for improvement, even in its SVGO Compressor code. I rely on Jake Archibald’s <a href="https://jakearchibald.github.io/svgomg/">SVGOMG</a>, which uses SVGO v3 and consistently delivers the best optimised SVGs.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/8-jake-archibald-svgomg-online-optimisation-tool.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="439"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/8-jake-archibald-svgomg-online-optimisation-tool.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/8-jake-archibald-svgomg-online-optimisation-tool.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/8-jake-archibald-svgomg-online-optimisation-tool.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/8-jake-archibald-svgomg-online-optimisation-tool.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/8-jake-archibald-svgomg-online-optimisation-tool.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/8-jake-archibald-svgomg-online-optimisation-tool.png"
			
			sizes="100vw"
			alt="Jake Archibald’s SVGOMG online optimisation tool."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Jake Archibald’s SVGOMG online optimisation tool. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/8-jake-archibald-svgomg-online-optimisation-tool.png'>Large preview</a>)
    </figcaption>
  
</figure>

<div class="partners__lead-place"></div>

<h2 id="layering-svg-elements">Layering SVG Elements</h2>

<p>My process for preparing SVGs for animation goes well beyond drawing vectors and optimising paths &mdash; it also includes how I <strong>structure the code</strong> itself. When every visual element is crammed into a single SVG file, even optimised code can be a nightmare to navigate. Locating a specific path or group often feels like searching for a needle in a haystack.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/9-yogi-bear-title-card-toon-titles-recreation.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/9-yogi-bear-title-card-toon-titles-recreation.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/9-yogi-bear-title-card-toon-titles-recreation.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/9-yogi-bear-title-card-toon-titles-recreation.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/9-yogi-bear-title-card-toon-titles-recreation.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/9-yogi-bear-title-card-toon-titles-recreation.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/9-yogi-bear-title-card-toon-titles-recreation.png"
			
			sizes="100vw"
			alt="Toon Titles recreation of the Yogi Bear title card"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Yogi Bear title card design by Lawrence Goble (1958). Toon Titles recreation. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/9-yogi-bear-title-card-toon-titles-recreation.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>That’s why I develop my SVGs in layers, exporting and optimising one set of elements at a time &mdash; always in the order they’ll appear in the final file. This lets me build the master SVG gradually by pasting it in each cleaned-up section. For example, I start with backgrounds like this gradient and title graphic.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/10-gradient-background-title-graphic.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/10-gradient-background-title-graphic.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/10-gradient-background-title-graphic.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/10-gradient-background-title-graphic.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/10-gradient-background-title-graphic.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/10-gradient-background-title-graphic.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/10-gradient-background-title-graphic.png"
			
			sizes="100vw"
			alt="Gradient background and title graphic."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Gradient background and title graphic. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/10-gradient-background-title-graphic.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Instead of facing a wall of SVG code, I can now easily identify the background gradient’s path and its associated <code>linearGradient</code>, and see the group containing the title graphic. I take this opportunity to add a comment to the code, which will make editing and adding animations to it easier in the future:</p>

<pre><code class="language-svg">&lt;svg ...&gt;
  &lt;defs&gt;
    &lt;!-- ... --&gt;
  &lt;/defs&gt;
  &lt;path fill="url(#grad)" d="…"/&gt;
  &lt;!-- TITLE GRAPHIC --&gt;
  &lt;g&gt;
    &lt;path … /&gt;
    &lt;!-- ... --&gt; 
  &lt;/g&gt;
&lt;/svg&gt;
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/11-trail-gaussian-blur.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/11-trail-gaussian-blur.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/11-trail-gaussian-blur.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/11-trail-gaussian-blur.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/11-trail-gaussian-blur.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/11-trail-gaussian-blur.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/11-trail-gaussian-blur.png"
			
			sizes="100vw"
			alt="Trail with Gaussian Blur."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Trail with Gaussian Blur. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/11-trail-gaussian-blur.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Next, I add the blurred trail from Yogi’s airborne broom. This includes defining a Gaussian Blur filter and placing its path between the background and title layers:</p>

<pre><code class="language-svg">&lt;svg ...&gt;
  &lt;defs&gt;
    &lt;linearGradient id="grad" …&gt;…&lt;/linearGradient&gt;
    &lt;filter id="trail" …&gt;…&lt;/filter&gt;
  &lt;/defs&gt;
  &lt;!-- GRADIENT --&gt;
  &lt;!-- TRAIL --&gt;
  &lt;path filter="url(#trail)" …/&gt;
  &lt;!-- TITLE GRAPHIC --&gt;
&lt;/svg&gt;
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/12-yogi-bear-magical-stars.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/12-yogi-bear-magical-stars.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/12-yogi-bear-magical-stars.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/12-yogi-bear-magical-stars.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/12-yogi-bear-magical-stars.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/12-yogi-bear-magical-stars.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/12-yogi-bear-magical-stars.png"
			
			sizes="100vw"
			alt="Yogi Bear’s magical stars."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Yogi Bear’s magical stars. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/12-yogi-bear-magical-stars.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Then come the magical stars, added in the same sequential fashion:</p>

<pre><code class="language-svg">&lt;svg ...&gt;
  &lt;!-- GRADIENT --&gt;
  &lt;!-- TRAIL --&gt;
  &lt;!-- STARS --&gt;
  &lt;!-- TITLE GRAPHIC --&gt;
&lt;/svg&gt;
</code></pre>

<p>To keep everything organised and animation-ready, I create an empty group that will hold all the parts of Yogi:</p>

<pre><code class="language-svg">&lt;g id="yogi"&gt;...&lt;/g&gt;
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/13-yogi-bear-component-parts.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/13-yogi-bear-component-parts.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/13-yogi-bear-component-parts.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/13-yogi-bear-component-parts.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/13-yogi-bear-component-parts.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/13-yogi-bear-component-parts.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/13-yogi-bear-component-parts.png"
			
			sizes="100vw"
			alt="Added Yogi Bear’s component parts"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Sequentially adding Yogi Bear’s component parts. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/13-yogi-bear-component-parts.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Then I build Yogi from the ground up &mdash; starting with background props, like his broom:</p>

<pre><code class="language-svg">&lt;g id="broom"&gt;...&lt;/g&gt;
</code></pre>

<p>Followed by grouped elements for his body, head, collar, and tie:</p>

<pre><code class="language-svg">&lt;g id="yogi"&gt;
  &lt;g id="broom"&gt;…&lt;/g&gt;
  &lt;g id="body"&gt;…&lt;/g&gt;
  &lt;g id="head"&gt;…&lt;/g&gt;
  &lt;g id="collar"&gt;…&lt;/g&gt;
  &lt;g id="tie"&gt;…&lt;/g&gt;
&lt;/g&gt;
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/14-yogi-bear-title-card-toon-titles-recreation.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/14-yogi-bear-title-card-toon-titles-recreation.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/14-yogi-bear-title-card-toon-titles-recreation.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/14-yogi-bear-title-card-toon-titles-recreation.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/14-yogi-bear-title-card-toon-titles-recreation.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/14-yogi-bear-title-card-toon-titles-recreation.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/14-yogi-bear-title-card-toon-titles-recreation.png"
			
			sizes="100vw"
			alt="Toon Titles recreation of the Yogi Bear title card"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Yogi Bear title card design by Lawrence Goble (1958). Toon Titles recreation. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/14-yogi-bear-title-card-toon-titles-recreation.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Since I export each layer from the same-sized artboard, I don’t need to worry about alignment or positioning issues later on &mdash; they’ll all slot into place automatically. I keep my code <strong>clean</strong>, <strong>readable</strong>, and <strong>ordered logically</strong> by layering elements this way. It also makes animating smoother, as each component is easier to identify.</p>

<h2 id="reusing-elements-with-use">Reusing Elements With <code>&lt;use&gt;</code></h2>

<p>When duplicate shapes get reused repeatedly, SVG files can get bulky fast. My recreation of the “Bewitched Bear” title card contains 80 stars in three sizes. Combining all those shapes into one optimised path would bring the file size down to 3KB. But I want to animate individual stars, which would almost double that to 5KB:</p>

<pre><code class="language-svg">&lt;g id="stars"&gt;
 &lt;path class="star-small" fill="#eae3da" d="..."/&gt;
 &lt;path class="star-medium" fill="#eae3da" d="..."/&gt;
 &lt;path class="star-large" fill="#eae3da" d="..."/&gt;
 &lt;!-- ... --&gt;
&lt;/g&gt;
</code></pre>

<p>Moving the stars’ <code>fill</code> attribute values to their parent group reduces the overall weight a little:</p>

<pre><code class="language-svg">&lt;g id="stars" fill="#eae3da"&gt;
 &lt;path class="star-small" d="…"/&gt;
 &lt;path class="star-medium" d="…"/&gt;
 &lt;path class="star-large" d="…"/&gt;
 &lt;!-- ... --&gt;
&lt;/g&gt;
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/15-yogi-bear-sparkling-stars.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/15-yogi-bear-sparkling-stars.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/15-yogi-bear-sparkling-stars.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/15-yogi-bear-sparkling-stars.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/15-yogi-bear-sparkling-stars.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/15-yogi-bear-sparkling-stars.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/15-yogi-bear-sparkling-stars.png"
			
			sizes="100vw"
			alt="Yogi Bear’s sparkling stars."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Yogi Bear’s sparkling stars. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/15-yogi-bear-sparkling-stars.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>But a more efficient and manageable option is to define each star size as a reusable template:</p>

<div class="break-out">
<pre><code class="language-svg">&lt;defs&gt;
  &lt;path id="star-large" fill="#eae3da" fill-rule="evenodd" d="…"/&gt;
  &lt;path id="star-medium" fill="#eae3da" fill-rule="evenodd" d="…"/&gt;
  &lt;path id="star-small" fill="#eae3da" fill-rule="evenodd" d="…"/&gt;
&lt;/defs&gt;
</code></pre>
</div>

<p>With this setup, changing a star’s design only means updating its template once, and every instance updates automatically. Then, I reference each one using <code>&lt;use&gt;</code> and position them with <code>x</code> and <code>y</code> attributes:</p>

<pre><code class="language-svg">&lt;g id="stars"&gt;
  &lt;!-- Large stars --&gt;
  &lt;use href="#star-large" x="1575" y="495"/&gt;
  &lt;!-- ... --&gt;
  &lt;!-- Medium stars --&gt;
  &lt;use href="#star-medium" x="1453" y="696"/&gt;
  &lt;!-- ... --&gt;
  &lt;!-- Small stars --&gt;
  &lt;use href="#star-small" x="1287" y="741"/&gt;
  &lt;!-- ... --&gt;
&lt;/g&gt;
</code></pre>

<p>This approach makes the SVG easier to manage, lighter to load, and faster to iterate on, especially when working with dozens of repeating elements. Best of all, it keeps the markup clean <strong>without compromising on flexibility or performance</strong>.</p>

<div class="partners__lead-place"></div>

<h2 id="adding-animations">Adding Animations</h2>

<p>The stars trailing behind Yogi’s stolen broom bring so much personality to the animation. I wanted them to sparkle in a seemingly random pattern against the dark blue background, so I started by defining a keyframe animation that cycles through different <code>opacity</code> levels:</p>

<pre><code class="language-css">@keyframes sparkle {
  0%, 100% { opacity: .1; }
  50% { opacity: 1; }
}
</code></pre>

<p>Next, I applied this looping animation to every <code>use</code> element inside my stars group:</p>

<pre><code class="language-css">&#35;stars use {
  animation: sparkle 10s ease-in-out infinite;
}
</code></pre>

<p>The secret to creating a convincing twinkle lies in <strong>variation</strong>. I staggered animation delays and durations across the stars using <code>nth-child</code> selectors, starting with the quickest and most frequent sparkle effects:</p>

<pre><code class="language-css">/&#42; Fast, frequent &#42;/
&#35;stars use:nth-child(n + 1):nth-child(-n + 10) {
  animation-delay: .1s;
  animation-duration: 2s;
}
</code></pre>

<p>From there, I layered in additional timings to mix things up. Some stars sparkle slowly and dramatically, others more randomly, with a variety of rhythms and pauses:</p>

<pre><code class="language-css">/&#42; Medium &#42;/
&#35;stars use:nth-child(n + 11):nth-child(-n + 20) { ... }

/&#42; Slow, dramatic &#42;/
&#35;stars use:nth-child(n + 21):nth-child(-n + 30) { ... }

/&#42; Random &#42;/
&#35;stars use:nth-child(3n + 2) { ... }

/&#42; Alternating &#42;/
&#35;stars use:nth-child(4n + 1) { ... }

/&#42; Scattered &#42;/
&#35;stars use:nth-child(n + 31) { ... }
</code></pre>

<p>By thoughtfully structuring the SVG and reusing elements, I can build complex-looking animations without bloated code, making even a simple effect like changing <code>opacity</code> sparkle.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/16-subtle-movements-yogi-bear.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/16-subtle-movements-yogi-bear.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/16-subtle-movements-yogi-bear.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/16-subtle-movements-yogi-bear.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/16-subtle-movements-yogi-bear.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/16-subtle-movements-yogi-bear.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/16-subtle-movements-yogi-bear.png"
			
			sizes="100vw"
			alt="Yogi Bear"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Subtle movements bring Yogi Bear to life. (<a href='https://files.smashing.media/articles/smashing-animations-part-4-optimising-svgs/16-subtle-movements-yogi-bear.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Then, for added realism, I make Yogi’s head wobble:</p>

<div class="break-out">
<pre><code class="language-css">@keyframes headWobble {
  0% { transform: rotate(-0.8deg) translateY(-0.5px); }
  100% { transform: rotate(0.9deg) translateY(0.3px); }
}

&#35;head {
  animation: headWobble 0.8s cubic-bezier(0.5, 0.15, 0.5, 0.85) infinite alternate;
}
</code></pre>
</div>

<p>His tie waves:</p>

<div class="break-out">
<pre><code class="language-css">@keyframes tieWave {
  0%, 100% { transform: rotateZ(-4deg) rotateY(15deg) scaleX(0.96); }
  33% { transform: rotateZ(5deg) rotateY(-10deg) scaleX(1.05); }
  66% { transform: rotateZ(-2deg) rotateY(5deg) scaleX(0.98); }
}

&#35;tie {
  transform-style: preserve-3d;
  animation: tieWave 10s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite;
}
</code></pre>
</div>

<p>His broom swings:</p>

<div class="break-out">
<pre><code class="language-css">@keyframes broomSwing {
  0%, 20% { transform: rotate(-5deg); }
  30% { transform: rotate(-4deg); }
  50%, 70% { transform: rotate(5deg); }
  80% { transform: rotate(4deg); }
  100% { transform: rotate(-5deg); }
}

&#35;broom {
  animation: broomSwing 4s cubic-bezier(0.5, 0.05, 0.5, 0.95) infinite;
}
</code></pre>
</div>

<p>And, finally, Yogi himself gently rotates as he flies on his magical broom:</p>

<div class="break-out">
<pre><code class="language-css">@keyframes yogiWobble {
  0% { transform: rotate(-2.8deg) translateY(-0.8px) scale(0.998); }
  30% { transform: rotate(1.5deg) translateY(0.3px); }
  100% { transform: rotate(3.2deg) translateY(1.2px) scale(1.002); }
}

&#35;yogi {
  animation: yogiWobble 3.5s cubic-bezier(.37, .14, .3, .86) infinite alternate;
}
</code></pre>
</div>

<p>All these subtle movements bring Yogi to life. By developing structured SVGs, I can create animations that feel full of character without writing a single line of JavaScript.</p>

<p>Try this yourself:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="bNdwJBN"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Bewitched Bear CSS/SVG animation [forked]](https://codepen.io/smashingmag/pen/bNdwJBN) by <a href="https://codepen.io/malarkey">Andy Clarke</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/bNdwJBN">Bewitched Bear CSS/SVG animation [forked]</a> by <a href="https://codepen.io/malarkey">Andy Clarke</a>.</figcaption>
</figure>

<h2 id="conclusion">Conclusion</h2>

<p>Whether you’re recreating a classic title card or animating icons for an interface, the principles are the same:</p>

<ol>
<li>Start clean,</li>
<li>Optimise early, and</li>
<li>Structure everything with animation in mind.</li>
</ol>

<p>SVGs offer incredible creative freedom, but only if kept <strong>lean</strong> and <strong>manageable</strong>. When you plan your process like a production cell &mdash; layer by layer, element by element &mdash; you’ll spend less time untangling code and more time bringing your work to life.</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(gg, yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Andy Clarke</author><title>Smashing Animations Part 3: SMIL’s Not Dead Baby, SMIL’s Not Dead</title><link>https://www.smashingmagazine.com/2025/05/smashing-animations-part-3-smil-not-dead/</link><pubDate>Wed, 21 May 2025 08:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/05/smashing-animations-part-3-smil-not-dead/</guid><description>While there are plenty of ways that CSS animations can bring designs to life, adding simple SMIL (Synchronized Multimedia Integration Language) animations in SVG can help them do much more. Andy Clarke explains where SMIL animations in SVG take over where CSS leaves off.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-3-smil-not-dead/" />
              <title>Smashing Animations Part 3: SMIL’s Not Dead Baby, SMIL’s Not Dead</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Smashing Animations Part 3: SMIL’s Not Dead Baby, SMIL’s Not Dead</h1>
                  
                    
                    <address>Andy Clarke</address>
                  
                  <time datetime="2025-05-21T08:00:00&#43;00:00" class="op-published">2025-05-21T08:00:00+00:00</time>
                  <time datetime="2025-05-21T08:00:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                
                

<p>The SMIL specification was introduced by the W3C in 1998 for synchronizing multimedia. This was long before CSS animations or JavaScript-based animation libraries were available. It was built into SVG 1.1, which is why we can still use it there today.</p>

<p>Now, you might’ve heard that <a href="https://css-tricks.com/smil-is-dead-long-live-smil-a-guide-to-alternatives-to-smil-features">SMIL is dead</a>. However, it’s alive and well since Google reversed a decision to deprecate the technology almost a decade ago. It remains a terrific choice for designers and developers who want simple, semantic ways to add animations to their designs.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/1-yogi-bear-show.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="540"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/1-yogi-bear-show.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/1-yogi-bear-show.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/1-yogi-bear-show.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/1-yogi-bear-show.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/1-yogi-bear-show.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/1-yogi-bear-show.png"
			
			sizes="100vw"
			alt="The Yogi Bear Show illustration"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The Yogi Bear Show © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/1-yogi-bear-show.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p><strong>Tip</strong>: <em>There’s now a website where you can see all my <a href="https://stuffandnonsense.co.uk/toon-titles">Toon Titles</a>.</em></p>

<div class="refs">
  <ul><li><a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-1-classic-cartoons-inspire-css/">Smashing Animations Part 1: How Classic Cartoons Inspire Modern CSS</a></li><li><a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-2-css-masking-add-extra-dimension/">Smashing Animations Part 2: How CSS Masking Can Add An Extra Dimension</a></li></ul>
</div>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h2 id="introducing-mike-worth">Introducing Mike Worth</h2>

<p>I’ve recently been working on a new website for Emmy-award-winning game composer Mike Worth. He hired me to create a bold, retro-style design that showcases his work. I used animations throughout to delight and surprise his audience as they move through his website.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/2-mike-worth-website.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="550"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/2-mike-worth-website.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/2-mike-worth-website.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/2-mike-worth-website.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/2-mike-worth-website.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/2-mike-worth-website.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/2-mike-worth-website.png"
			
			sizes="100vw"
			alt="Illustrations from Mike Worth’s website"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Design by <a href='https://stuffandnonsense.co.uk/'>Andy Clarke, Stuff & Nonsense</a>. Mike Worth’s website will launch in June 2025, but you can see <a href='https://codepen.io/collection/YwMKPb'>examples from this article on CodePen</a>. (<a href='https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/2-mike-worth-website.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Mike loves ’90s animation &mdash; especially <a href="https://en.wikipedia.org/wiki/DuckTales_(1987_TV_series)">Disney’s</a> <a href="https://en.wikipedia.org/wiki/DuckTales_(1987_TV_series)"><em>Duck Tales</em></a>. Unsurprisingly, my taste in cartoons stretches back a little further to <a href="https://en.wikipedia.org/wiki/Hanna-Barbera">Hanna-Barbera</a> shows like Dastardly and Muttley in <em>Their Flying Machines</em>, <em>Scooby-Doo</em>, <em>The Perils of Penelope Pitstop</em>, <em>Wacky Races</em>, and, of course, <a href="https://en.wikipedia.org/wiki/Yogi_Bear"><em>The Yogi Bear Show</em></a>. So, to explain how this era of animation relates to SVG, I’ll be adding SMIL animations in SVG to title cards from some classic Yogi Bear cartoons.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/3-yogi-bear-show.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="300"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/3-yogi-bear-show.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/3-yogi-bear-show.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/3-yogi-bear-show.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/3-yogi-bear-show.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/3-yogi-bear-show.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/3-yogi-bear-show.png"
			
			sizes="100vw"
			alt="The Yogi Bear Show illustrations"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The Yogi Bear Show © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/3-yogi-bear-show.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Fundamentally, animation changes how an element looks and where it appears over time using a few basic techniques. That might be simply shifting an element up or down, left or right, to create the appearance of motion, like Yogi Bear moving across the screen.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/4-yogi-bear-title-card.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/4-yogi-bear-title-card.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/4-yogi-bear-title-card.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/4-yogi-bear-title-card.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/4-yogi-bear-title-card.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/4-yogi-bear-title-card.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/4-yogi-bear-title-card.png"
			
			sizes="100vw"
			alt="Yogi Bear title card design recreated by Andy Clarke"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Yogi Bear title card design by Lawrence Goble (1958). Author’s recreation. (<a href='https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/4-yogi-bear-title-card.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Rotating objects around a fixed point can create everything, from simple spinning effects to natural-looking movements of totally normal things, like a bear under a parachute falling from the sky.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/5-yogi-bear-title-card-design.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/5-yogi-bear-title-card-design.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/5-yogi-bear-title-card-design.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/5-yogi-bear-title-card-design.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/5-yogi-bear-title-card-design.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/5-yogi-bear-title-card-design.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/5-yogi-bear-title-card-design.png"
			
			sizes="100vw"
			alt="Yogi Bear title card recreated by Andy Clarke"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Yogi Bear title card design by Lawrence Goble (1958). Author’s recreation. (<a href='https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/5-yogi-bear-title-card-design.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Scaling makes an element grow, shrink, or stretch, which can add drama, create perspective, or simulate depth.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/6-yogi-bear-title-card.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/6-yogi-bear-title-card.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/6-yogi-bear-title-card.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/6-yogi-bear-title-card.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/6-yogi-bear-title-card.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/6-yogi-bear-title-card.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/6-yogi-bear-title-card.png"
			
			sizes="100vw"
			alt="Yogi Bear title card recreated by Andy Clarke"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Yogi Bear title card design by Lawrence Goble (1958). Author’s recreation. (<a href='https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/6-yogi-bear-title-card.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Changing colour and transitioning opacity can add atmosphere, create a mood, and enhance visual storytelling. Just these basic principles can create animations that attract attention and improve someone’s experience using a design.</p>

<p>These results are all achievable using CSS animations, but some SVG properties can’t be animated using CSS. Luckily, we can do more &mdash; and have much more fun &mdash; using SMIL animations in SVG. We can combine complex animations, move objects along paths, and control when they start, stop, and everything in between.</p>

<p>Animations can be embedded within any SVG element, including <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorials/SVG_from_scratch/Basic_shapes">primitive shapes</a> like circles, ellipses, and rectangles. They can also be encapsulated into groups, paths, and polygons:</p>

<pre><code class="language-svg">&lt;circle ...&gt;
  &lt;animate&gt;...&lt;/animate&gt;
&lt;/circle&gt;
</code></pre>

<p>Animations can also be defined outside an element, elsewhere in an SVG, and connected to it using an <code>xlink</code> attribute:</p>

<pre><code class="language-svg">&lt;g id="yogi"&gt;...&lt;/g&gt;
  ...
&lt;animate xlink:href="#yogi"&gt;…&lt;/animate&gt;
</code></pre>

<h2 id="building-an-animation">Building An Animation</h2>

<p><code>&lt;animate&gt;</code> is just one of several animation elements in SVG. Together with an <code>attributeName</code> value, it enables animations based on one or more of an element’s attributes.</p>

<p>Most animation explanations start by moving a primitive shape, like this exciting circle:</p>

<pre><code class="language-svg">&lt;circle
  r="50"
  cx="50" 
  cy="50" 
  fill="#062326" 
  opacity="1"
/&gt;
</code></pre>

<p>Using this <code>attributeName</code> property, I can define which of this circle’s attributes I want to animate, which, in this example, is its <code>cx</code> (x-axis center point) position:</p>

<pre><code class="language-svg">&lt;circle ... &gt;
  &lt;animate attributename="cx"&gt;&lt;/animate&gt;
&lt;/circle&gt;
</code></pre>

<p>On its own, this does precisely nothing until I define three more values. The <code>from</code> keyword specifies the circle’s initial position, <code>to</code>, its final position, and the <code>dur</code>-ation between those two positions:</p>

<pre><code class="language-svg">&lt;circle ... &gt;
  &lt;animate 
  attributename="cx"
  from="50" 
  to="500"
  dur="1s"&gt;
  &lt;/animate&gt;
&lt;/circle&gt;
</code></pre>

<p>If I want more precise control, I can replace <code>from</code> and <code>to</code> with a set of <code>values</code> separated by semicolons:</p>

<pre><code class="language-svg">&lt;circle ... &gt;
  &lt;animate 
  attributename="cx"
  values="50; 250; 500; 250;"
  dur="1s"&gt;
  &lt;/animate&gt;
&lt;/circle&gt;
</code></pre>

<p>Finally, I can define how many times the animation repeats (<code>repeatcount</code>) and even after what period that repeating should stop (<code>repeatdur</code>):</p>

<pre><code class="language-svg">&lt;circle ... &gt;
  &lt;animate 
  attributename="cx"
  values="50; 250; 500; 250;"
  dur="1s"
  repeatcount="indefinite"
  repeatdur="180s"&gt;
&lt;/circle&gt;
</code></pre>

<p>Most SVG elements have attributes that can be animated. This title card from 1959’s <a href="https://yogibear.fandom.com/wiki/Brainy_Bear">“Brainy Bear” episode</a> shows Yogi in a crazy scientist‘s brain experiment. Yogi’s head is under the dome, and energy radiates around him.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/7-yogi-bear-show.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="540"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/7-yogi-bear-show.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/7-yogi-bear-show.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/7-yogi-bear-show.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/7-yogi-bear-show.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/7-yogi-bear-show.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/7-yogi-bear-show.png"
			
			sizes="100vw"
			alt="The Yogi Bear Show illustration where Yogi’s head is under the dome, and energy radiates around him"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The Yogi Bear Show © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/7-yogi-bear-show.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>To create the buzz around Yogi, my SVG includes three <code>path</code> elements, each with <code>opacity</code>, <code>stroke</code>, and <code>stroke-width</code> attributes, which can all be animated:</p>

<pre><code class="language-svg">&lt;path opacity="1" stroke="#fff" stroke-width="5" ... /&gt;
</code></pre>

<p>I animated each path’s <code>opacity</code>, changing its value from <code>1</code> to <code>.5</code> and back again:</p>

<pre><code class="language-svg">&lt;path opacity="1" ... &gt;
  &lt;animate 
    attributename="opacity"
    values="1; .25; 1;"
    dur="1s"
    repeatcount="indefinite"&gt;
  &lt;/animate&gt;
&lt;/path&gt;
</code></pre>

<p>Then, to radiate energy from Yogi, I specified when each animation should <code>begin</code>, using a different value for each <code>path</code>:</p>

<pre><code class="language-svg">&lt;path ... &gt;
  &lt;animate begin="0" … &gt;
&lt;/path&gt;

&lt;path ... &gt;
  &lt;animate begin=".5s" … &gt;
&lt;/path&gt;

&lt;path ... &gt;
  &lt;animate begin="1s" … &gt;
&lt;/path&gt;
</code></pre>

<p>I’ll explain more about the <code>begin</code> property and how to start animations after this short commercial break.</p>

<p>Try this yourself:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="qEEzYgG"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Brainy Bear SVG animation [forked]](https://codepen.io/smashingmag/pen/qEEzYgG) by <a href="https://codepen.io/malarkey">Andy Clarke</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/qEEzYgG">Brainy Bear SVG animation [forked]</a> by <a href="https://codepen.io/malarkey">Andy Clarke</a>.</figcaption>
</figure>

<p>To make animations appear more natural, I can apply more than one <code>animate</code> element and give each one a different <code>attributename</code> value. Those paths also contain a <code>stroke-width</code> attribute, which I can also animate by changing the stroke widths between <code>5</code> and <code>7</code>:</p>

<pre><code class="language-svg">&lt;path ... &gt;
  &lt;animate attributename="opacity" ... &gt;&lt;/animate&gt;
  &lt;animate attributename="stroke-width" ... &gt;&lt;/animate&gt;
&lt;/path&gt;
</code></pre>

<p>Finally, I can animate the dome over Yogi’s head, changing its <code>fill</code> colour between two values over five seconds to create the impression that the crazy scientist’s machine is heating up:</p>

<pre><code class="language-svg">&lt;path fill="#50D9E0" ... &gt;
  &lt;animate
    attributename="fill"
    values="#50D9E0; #E18C50;"
    dur="5s"
    begin="2s"
  &gt;
&lt;/path&gt;
</code></pre>

<p>Implement that code, and you’ll soon notice that the dome returns to its original state after the animation is complete. To retain its colour at the end of the animation, I can add the &mdash; confusingly named &mdash; <code>fill</code> property and a value of <code>freeze.</code> This stops the animation in its final state and prevents it from returning to the original colour:</p>

<pre><code class="language-svg">&lt;path fill="#50D9E0" ... &gt;
  &lt;animate fill="freeze"&gt;
&lt;/path&gt;
</code></pre>

<p>Animating attributes brings these title card designs to life, whether by adjusting the position of a primitive shape, its opacity, and stroke width or by creating complex sequences with staggered timing. But there’s still more I can do, starting with the next animation element, <code>animateTransform</code>.</p>

<div class="partners__lead-place"></div>

<h2 id="animatetransform"><code>animateTransform</code></h2>

<p>If <code>&lt;animate&gt;</code> controls attributes, then <code>animateTransform</code> animates transformations, including rotations, scaling, skewing, and translations. It works by changing the values of a transform property, like this <code>translate</code>:</p>

<pre><code class="language-svg">&lt;path transform="translate(0,0)"/&gt;
</code></pre>

<p>Then, the animation works the same way as <code>&lt;animate&gt;</code>, adding an <code>attributename</code> and specifying the type of transform, in this example, <code>rotate:</code></p>

<pre><code class="language-svg">&lt;animatetransform 
  attributename="transform"
  type="rotate"&gt;
&lt;/animatetransform&gt;
</code></pre>

<p>I can use either <code>from</code> and <code>to</code> or the <code>values</code> attribute to define how an element is transformed.</p>

<ul>
<li><strong>Scale</strong> uses <code>x</code> and <code>y</code> values  (<code>.5</code>, <code>1</code>).</li>
<li><strong>Rotate</strong> uses degrees (<code>0</code>–<code>360</code>) plus optional <code>x</code> and <code>y</code> (<code>360</code>, <code>0</code>, <code>0</code>).</li>
<li><strong>Translate</strong> also uses <code>x</code> and <code>y</code> values  (<code>50</code>, <code>100</code>).</li>
<li><strong>Skew</strong> uses <code>x</code> and <code>y</code> values, too (<code>50</code>, <code>100</code>).</li>
</ul>

<p>What’s interesting about those values is that they can be added to an element’s existing values instead of replacing them. For example, when an attribute contains a <code>translate</code> value of <code>100, 0</code>:</p>

<pre><code class="language-svg">&lt;path transform="translate(100, 0)"/&gt;
</code></pre>

<p>And then I animate that translation horizontally by <code>100</code>:</p>

<pre><code class="language-svg">&lt;animatetransform
  attributename="transform"
  type="translate"
  from="0, 0"
  to="100, 0"
  additive="sum"&gt;
&lt;/animatetransform&gt;
</code></pre>

<p>Using the <code>additive</code> property with a value of <code>sum</code>, the animation values are relative to the original, starting the animation at <code>100</code> and ending at <code>200</code> by adding <code>100</code> to <code>100</code>.</p>

<p>Similarly, if I give the <code>accumulate</code> property a value of <code>sum</code>, each instance of animation will build on the last. So, in an animation where an element is translated by <code>100</code> and repeats five times, each movement will be cumulative, moving the element by <code>500</code>:</p>

<pre><code class="language-svg">&lt;animatetransform
  attributename="transform"
  type="translate"
  from="0, 0"
  to="100, 0"
  additive="sum"
  accumulate="sum" 
/&gt;
</code></pre>

<p>This title card from 1958’s Yogi Bear’s <a href="https://yogibear.fandom.com/wiki/Yogi_Bear%27s_Big_Break">“Big Break” episode</a> shows Yogi floating from the sky under a parachute.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/8-yogi-bear-show.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="540"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/8-yogi-bear-show.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/8-yogi-bear-show.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/8-yogi-bear-show.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/8-yogi-bear-show.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/8-yogi-bear-show.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/8-yogi-bear-show.png"
			
			sizes="100vw"
			alt="The Yogi Bear Show illustration shows Yogi floating from the sky under a parachute"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The Yogi Bear Show © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/8-yogi-bear-show.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>I needed two types of transform animations to generate the effect of Yogi drifting gently downwards: <code>translate</code>, and <code>rotate</code>. I first added an <code>animatetransform</code> element to the group, which contains Yogi and his chute. I defined his initial vertical position &mdash; <code>1200</code> off the top of the <code>viewBox</code> &mdash; then translated his descent to <code>1000</code> over a 15-second duration:</p>

<pre><code class="language-svg">&lt;g transform="translate(1200, -1200)"&gt;
  ...
  &lt;animateTransform
    attributeName="transform"
    type="translate"
    values="500,-1200; 500,1000"
    dur="15s"
    repeatCount="1" 
  /&gt;
&lt;/g&gt;
</code></pre>

<p>Yogi appears to fall from the sky, but the movement looks unrealistic. So, I added a second <code>animatetransform</code> element, this time with an indefinitely repeating +/- 5-degree rotation to swing Yogi from side to side during his descent:</p>

<pre><code class="language-svg">&lt;animateTransform
  attributeName="transform"
  type="rotate"
  values="-5; 5; -5"
  dur="14s"
  repeatCount="indefinite"
  additive="sum" 
/&gt;
</code></pre>

<p>Try this yourself:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="PwwraNm"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Big Break SVG animation [forked]](https://codepen.io/smashingmag/pen/PwwraNm) by <a href="https://codepen.io/malarkey">Andy Clarke</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/PwwraNm">Big Break SVG animation [forked]</a> by <a href="https://codepen.io/malarkey">Andy Clarke</a>.</figcaption>
</figure>

<h2 id="starting-and-stopping">Starting And Stopping</h2>

<p>So far, every animation begins as soon as the page has loaded. But there are ways to not only delay the start of animation but define precisely where it begins, using the begin <code>property</code>:</p>

<p>In this title card from 1959’s <a href="https://yogibear.fandom.com/wiki/Robin_Hood_Yogi">“Robin Hood Yogi”</a>, Yogi shoots an arrow into an apple on Boo-Boo’s head.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/9-yogi-bear-show.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="540"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/9-yogi-bear-show.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/9-yogi-bear-show.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/9-yogi-bear-show.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/9-yogi-bear-show.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/9-yogi-bear-show.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/9-yogi-bear-show.png"
			
			sizes="100vw"
			alt="The Yogi Bear Show illustration where Yogi shoots an arrow into an apple on Boo-Boo’s head"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The Yogi Bear Show © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/9-yogi-bear-show.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>By default, the arrow is set loose when the page loads. Blink, and you might miss it. To build some anticipation, I can <code>begin</code> the animation two seconds later:</p>

<pre><code class="language-svg">&lt;animatetransform
  attributename="transform"
  type="translate"
  from="0 0"
  to="750 0"
  dur=".25s"
  begin="2s"
  fill="freeze"
/&gt;
</code></pre>

<p>Or, I can let the viewer take the shot when they click the arrow:</p>

<pre><code class="language-svg">&lt;animatetransform
  ...
  begin="click"
/&gt;
</code></pre>

<p>And I can combine the click event and a delay, all with no JavaScript, just a smattering of SMIL:</p>

<pre><code class="language-svg">&lt;animatetransform
  ...
  begin="click + .5s"
/&gt;
</code></pre>

<p>Try this yourself by clicking the arrow:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="OPPeERj"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Robin Hood Yogi CSS animation [forked]](https://codepen.io/smashingmag/pen/OPPeERj) by <a href="https://codepen.io/malarkey">Andy Clarke</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/OPPeERj">Robin Hood Yogi CSS animation [forked]</a> by <a href="https://codepen.io/malarkey">Andy Clarke</a>.</figcaption>
</figure>

<h2 id="synchronising-animations">Synchronising Animations</h2>

<p>In his 1958 <a href="https://yogibear.fandom.com/wiki/Pie-Pirates">“Pie-Pirates” episode</a>, Yogi Bear tries to steal a pie and has to outwit a bulldog. The title card &mdash; designed by Lawrence Goble &mdash; shows the chase but, alas, (spoiler alert) no stolen pie.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/10-yogi-bear-show.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="540"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/10-yogi-bear-show.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/10-yogi-bear-show.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/10-yogi-bear-show.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/10-yogi-bear-show.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/10-yogi-bear-show.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/10-yogi-bear-show.png"
			
			sizes="100vw"
			alt="The Yogi Bear Show illustration showing a bulldog chasing Yogi Bear"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The Yogi Bear Show © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/10-yogi-bear-show.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>To bring this title card to life, I needed two groups of paths: one for Yogi and the other for the dog. I translated them both off the left edge of the <code>viewBox</code>:</p>

<pre><code class="language-svg">&lt;g class="dog" transform="translate(-1000, 0)"&gt;
  ...
&lt;/g&gt;

&lt;g class="yogi" transform="translate(-1000, 0)"&gt;
  ...
&lt;/g&gt;
</code></pre>

<p>Then, I applied an <code>animatetransform</code> element to both groups, which moves them back into view:</p>

<pre><code class="language-svg">&lt;!-- yogi --&gt;
&lt;animateTransform
  attributeName="transform"
  type="translate"
  from="-1000,0"
  to="0,0"
  dur="2s"
  fill="freeze"
/&gt;

&lt;!-- dog --&gt;
&lt;animateTransform
  attributeName="transform"
  type="translate"
  from="-1000,0"
  to="0,0"
  dur=".5s"
  fill="freeze"
/&gt;
</code></pre>

<p>This sets up the action, but the effect feels flat, so I added another pair of animations that bounce both characters:</p>

<pre><code class="language-svg">&lt;!-- yogi --&gt;
&lt;animateTransform
  attributeName="transform"
  type="rotate"
  values="-1,0,450; 1,0,450; -1,0,450"
  dur=".25s"
  repeatCount="indefinite"
/&gt;

&lt;!-- dog --&gt;
&lt;animateTransform
  attributeName="transform"
  type="rotate"
  values="-1,0,450; 1,0,450; -1,0,450"
  dur="0.5s"
  repeatCount="indefinite"
/&gt;
</code></pre>

<p>Animations can begin when a page loads, after a specified time, or when clicked. And by naming them, they can also synchronise with other animations.</p>

<p>I wanted Yogi to enter the frame first to build anticipation, with a short pause before other animations begin, synchronising to the moment he’s arrived. First, I added an ID to Yogi’s <code>translate</code> animation:</p>

<pre><code class="language-svg">&lt;animateTransform
  id="yogi"
  type="translate"
  ...
/&gt;
</code></pre>

<blockquote><strong>Watch out</strong>: For a reason, I can’t, for the life of me, explain why Firefox won’t begin animations with an ID when the ID contains a hyphen. This isn’t smarter than the average browser, but replacing hyphens with underscores fixes the problem.</blockquote>

<p>Then, I applied a <code>begin</code> to his <code>rotate</code> animation, which starts playing a half-second after the <code>#yogi</code> animation ends:</p>

<pre><code class="language-svg">&lt;animateTransform
  type="rotate"
  begin="yogi.end + .5s"
  ...
/&gt;
</code></pre>

<p>I can build sophisticated sets of synchronised animations using the <code>begin</code> property and whether a named animation begins or ends. The bulldog chasing Yogi enters the frame two seconds after Yogi begins his entrance:</p>

<pre><code class="language-svg">&lt;animateTransform
  id="dog"
  type="translate"
  begin="yogi.begin + 2s"
  fill="freeze"
  ...
/&gt;
</code></pre>

<p>One second after the dog has caught up with Yogi, a <code>rotate</code> transformation makes him bounce, too:</p>

<pre><code class="language-svg">&lt;animateTransform
  type="rotate"
  ...
  begin="dog.begin + 1s"
  repeatCount="indefinite" 
/&gt;
</code></pre>

<p>The background rectangles whizzing past are also synchronised, this time to one second before the bulldog ends his run:</p>

<pre><code class="language-svg">&lt;rect ...&gt;
  &lt;animateTransform
    begin="dog.end + -1s"
  /&gt;
&lt;/rect&gt;
</code></pre>

<p>Try this yourself:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="LEEKryp"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Pie-Pirates SVG animation [forked]](https://codepen.io/smashingmag/pen/LEEKryp) by <a href="https://codepen.io/malarkey">Andy Clarke</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/LEEKryp">Pie-Pirates SVG animation [forked]</a> by <a href="https://codepen.io/malarkey">Andy Clarke</a>.</figcaption>
</figure>

<p>The timing of this background movement is synchronised with the dog arriving, which, in turn, is relative to Yogi’s arrival, building a sequence of animations that all feel connected.</p>

<div class="partners__lead-place"></div>

<h2 id="animating-along-motion-paths">Animating Along Motion Paths</h2>

<p>Until now, all the animations in these title cards have been up, down, left, right, or one combination or another. But there’s one more aspect of SMIL in SVG, which can add an extra dimension to animations: animating along motion paths using the <code>animatemotion</code> element.</p>

<p><code>animatemotion</code> accepts all the same properties and values as <code>animate</code> and <code>animateTransform</code>, but adds a few more for finer control over direction and timing. <code>animatemotion</code> uses the <code>path</code> property to enable elements to move along a motion path. It also uses the <code>d</code> value for coordinate data in the same way as any conventional path.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/11-yogi-bear-show.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="540"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/11-yogi-bear-show.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/11-yogi-bear-show.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/11-yogi-bear-show.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/11-yogi-bear-show.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/11-yogi-bear-show.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/11-yogi-bear-show.png"
			
			sizes="100vw"
			alt="The Yogi Bear Show illustration of the Runaway Bear"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The Yogi Bear Show © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/11-yogi-bear-show.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>In <a href="https://yogibear.fandom.com/wiki/The_Runaway_Bear">“The Runaway Bear”</a> from 1959, Yogi must avoid a hunter turning his head into a trophy. I wanted Yogi to leap in and out of the screen by making him follow a path. I also wanted to vary the speed of his dash: speeding up as he enters and exits, and slowing down as he passes the title text.</p>

<p>I first added a <code>path</code> property, using its coordinate data to give Yogi a route to follow, and specified a two-second duration for my animation:</p>

<pre><code class="language-svg">&lt;g&gt;
  &lt;animateMotion
    dur="2s"
    path="..."
  &gt;
  &lt;/animateMotion&gt;
&lt;/g&gt;
</code></pre>

<p>Alternatively, I could add a <code>path</code> element, leave it visible, or prevent it from being rendered by placing it inside a <code>defs</code> element:</p>

<pre><code class="language-svg">&lt;defs&gt;
  &lt;path id="yogi" d="..." /&gt;
&lt;/defs&gt;
</code></pre>

<p>I can then reference that by using a <code>mpath</code> element inside my <code>animateMotion</code>:</p>

<pre><code class="language-svg">&lt;animateMotion
  ...
  &lt;mpath href="#yogi" /&gt;
&lt;/animateMotion&gt;
</code></pre>

<p>I experimented with several paths before settling on the one that delivered the movement shape I was looking for:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/12-yogi-bear-title-card-recreations.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/12-yogi-bear-title-card-recreations.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/12-yogi-bear-title-card-recreations.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/12-yogi-bear-title-card-recreations.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/12-yogi-bear-title-card-recreations.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/12-yogi-bear-title-card-recreations.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/12-yogi-bear-title-card-recreations.png"
			
			sizes="100vw"
			alt="Several variants of the Yogi Bear title card recreated by Andy Clarke"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Yogi Bear title card design by Lawrence Goble (1959.) Author’s recreation. (<a href='https://files.smashing.media/articles/smashing-animations-part-3-smil-not-dead/12-yogi-bear-title-card-recreations.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>One was too bouncy, one was too flat, but the third motion path was just right. Almost, as I also wanted to vary the speed of Yogi’s dash: speeding him up as he enters and exits and slowing him down as he passes the title text.</p>

<p>The <code>keyPoints</code> property enabled me to specify points along the motion path and then adjust the duration Yogi spends between them. To keep things simple, I defined five points between <code>0</code> and <code>1</code>:</p>

<pre><code class="language-svg">&lt;animateMotion
  ...
  keyPoints="0; .35; .5; .65; 1;"
&gt;
&lt;/animateMotion&gt;
</code></pre>

<p>Then I added the same number of <code>keyTimes</code> values, separated by semicolons, to control the pacing of this animation:</p>

<pre><code class="language-svg">&lt;animateMotion
  ...
  keyTimes="0; .1; .5; .95; 1;"
&gt;
&lt;/animateMotion&gt;
</code></pre>

<p>Now, Yogi rushes through the first three <code>keyPoints</code>, slows down as he passes the title text, then speeds up again as he exits the <code>viewBox</code>.</p>

<p>Try this yourself:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="oggryox"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Runaway Bear SVG animation [forked]](https://codepen.io/smashingmag/pen/oggryox) by <a href="https://codepen.io/malarkey">Andy Clarke</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/oggryox">Runaway Bear SVG animation [forked]</a> by <a href="https://codepen.io/malarkey">Andy Clarke</a>.</figcaption>
</figure>

<h2 id="smil-s-not-dead-baby-smil-s-not-dead">SMIL’s Not Dead, Baby. SMIL’s Not Dead</h2>

<p>With their ability to control transformations, animate complex motion paths, and synchronise multiple animations, SMIL animations in SVG are still powerful tools. They can bring design to life without needing a framework or relying on JavaScript. It’s compact, which makes it great for small SVG effects.</p>

<p>SMIL includes the <code>begin</code> attribute, which makes chaining animations far more intuitive than with CSS. Plus, SMIL lives inside the SVG file, making it perfect for animations that travel with an asset. So, while SMIL is not modern by today’s standards and may be a little bit niche, it can still be magical.</p>

<blockquote class="pull-quote">
  <p>
    <a class="pull-quote__link" aria-label="Share on Twitter" href="https://twitter.com/share?text=%0aDon%e2%80%99t%20let%20the%20misconception%20that%20SMIL%20is%20%e2%80%9cdead%e2%80%9d%20stop%20you%20from%20using%20this%20fantastic%20tool.%0a&url=https://smashingmagazine.com%2f2025%2f05%2fsmashing-animations-part-3-smil-not-dead%2f">
      
Don’t let the misconception that SMIL is “dead” stop you from using this fantastic tool.

    </a>
  </p>
  <div class="pull-quote__quotation">
    <div class="pull-quote__bg">
      <span class="pull-quote__symbol">“</span></div>
  </div>
</blockquote>

<p>Google reversed its decision to deprecate SMIL almost a decade ago, so it remains a terrific choice for designers and developers who want <strong>simple</strong>, <strong>semantic ways</strong> to add animations to their designs.</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Akshay Gupta</author><title>Mastering SVG Arcs</title><link>https://www.smashingmagazine.com/2024/12/mastering-svg-arcs/</link><pubDate>Mon, 09 Dec 2024 09:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2024/12/mastering-svg-arcs/</guid><description>SVG arcs demystified! Akshay Gupta explains how to master radii, rotation, and arc direction to create stunning curves. Make arcs a powerful part of your SVG toolkit for creating more dynamic, intricate designs with confidence.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2024/12/mastering-svg-arcs/" />
              <title>Mastering SVG Arcs</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Mastering SVG Arcs</h1>
                  
                    
                    <address>Akshay Gupta</address>
                  
                  <time datetime="2024-12-09T09:00:00&#43;00:00" class="op-published">2024-12-09T09:00:00+00:00</time>
                  <time datetime="2024-12-09T09:00:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                
                

<p>So, <strong>I love drawing birds with code.</strong> Inspired by my brother’s love for birdwatching, I admire the uniqueness of their feathers, colors, and sounds. But what I notice most is the way their bodies curve and different birds can have dramatically different curves! So, I took my love for drawing with SVG graphics and used it to experiment with bird shapes. Over time, I’ve drawn enough to become incredibly adept at working with arc shapes.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/mastering-svg-arcs/1-birds-with-code-svg.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="800"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/mastering-svg-arcs/1-birds-with-code-svg.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/mastering-svg-arcs/1-birds-with-code-svg.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/mastering-svg-arcs/1-birds-with-code-svg.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/mastering-svg-arcs/1-birds-with-code-svg.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/mastering-svg-arcs/1-birds-with-code-svg.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/mastering-svg-arcs/1-birds-with-code-svg.jpg"
			
			sizes="100vw"
			alt="Five examples of birds drawn in SVG, including a peacock, robin, parakeet, parrot, and toucan."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/mastering-svg-arcs/1-birds-with-code-svg.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Here are a few of my recent works. Inspired by designs I came across on <a href="https://dribbble.com">Dribbble</a>, I created my versions with code. You can browse through the code for each on my <a href="https://codepen.io/akshaygpt">CodePen</a>.</p>

<p>But before we dive into creating curves with arcs, please pause here and check out Myriam Frisano’s recent article, “<a href="https://www.smashingmagazine.com/2024/09/svg-coding-examples-recipes-writing-vectors-by-hand/">SVG Coding Examples: Useful Recipes For Writing Vectors By Hand</a>.” It’s an excellent primer to the SVG syntax and it will give you solid context heading into the concepts we’re covering here when it comes to mastering <strong>SVG arcs</strong>.</p>

<h2 id="a-quick-svg-refresher">A Quick SVG Refresher</h2>

<p>You probably know that SVGs are crisp, infinitely scalable illustrations without pixelated degradation &mdash; vectors for the win! What you might not know is that <strong>few developers write SVG code.</strong> Why? Well, the syntax looks complicated and unfamiliar compared to, say, HTML. But trust me, once you break it down, it’s not only possible to hand-code SVG but also quite a bit of fun.</p>

<p>Let’s make sure you’re up to speed on the SVG <code>viewBox</code> because it’s a key concept when it comes to the <em>scalable</em> part of *SVG. We’ll use the analogy of a camera, lens, and canvas to explain this concept. Think of your browser window as a camera and the SVG <code>viewBox</code> as the camera lens focusing on the painting of a bird you’ve created (the SVG). Imagine the painting on a large canvas that may stretch far beyond what the camera captures. The <code>viewBox</code> defines which part of this canvas is visible through the camera.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/mastering-svg-arcs/2-svg-viewbox.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="555"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/mastering-svg-arcs/2-svg-viewbox.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/mastering-svg-arcs/2-svg-viewbox.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/mastering-svg-arcs/2-svg-viewbox.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/mastering-svg-arcs/2-svg-viewbox.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/mastering-svg-arcs/2-svg-viewbox.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/mastering-svg-arcs/2-svg-viewbox.png"
			
			sizes="100vw"
			alt="Illustrating the viewBox in green, like a camera lens."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/mastering-svg-arcs/2-svg-viewbox.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Let’s say we have an SVG element that we’re sizing at <code>600px</code> square with <code>width</code> and <code>height</code> attributes directly on the <code>&lt;svg&gt;</code> element.</p>

<pre><code class="language-svg">&lt;svg width="600px" height="600px"&gt;
</code></pre>
 

<p>Let’s turn our attention to the <code>viewBox</code> attribute:</p>

<pre><code class="language-svg">&lt;svg width="600px" height="600px" viewBox="-300 -300 600 600"&gt;
</code></pre>

<p>The <code>viewBox</code> attribute defines the internal coordinate system for the SVG, with four values mapping to the SVG’s x, y, width, and height in that order.</p>

<p>Here’s how this relates to our analogy:</p>

<ul>
<li><strong>Camera Position and Size</strong><br />
The <code>-300, -300</code> represents the camera lens’ left and top edge position. Meanwhile, <code>600 x 600</code> is like the camera’s frame size, showing a specific portion of that space.</li>
<li><strong>Unchanging Canvas Size</strong><br />
Changing the <code>x</code> and <code>y</code> values adjusts where the camera points, and <code>width</code> and <code>height</code> govern how much of the canvas it frames. It doesn’t resize the actual canvas (the SVG element itself, which remains at <code>600</code>×<code>600</code> pixels). No matter where the camera is positioned or zoomed, the canvas itself remains fixed.</li>
</ul>

<p>So, when you adjust the <code>viewBox</code> coordinates, you’re simply choosing a new area of the canvas to focus on without resizing the canvas itself. This lets you control the visible area without changing the SVG’s actual display dimensions.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/mastering-svg-arcs/3-coordinates-viewbox.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="663"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/mastering-svg-arcs/3-coordinates-viewbox.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/mastering-svg-arcs/3-coordinates-viewbox.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/mastering-svg-arcs/3-coordinates-viewbox.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/mastering-svg-arcs/3-coordinates-viewbox.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/mastering-svg-arcs/3-coordinates-viewbox.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/mastering-svg-arcs/3-coordinates-viewbox.png"
			
			sizes="100vw"
			alt="Demonstrating coordinates for the top-left corner (-300,-300), center (0,0), and the direction of x and y axis (left to right and top to bottom respectively) of the viewBox."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/mastering-svg-arcs/3-coordinates-viewbox.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>You now have the context you need to learn how to work with <code>&lt;path&gt;</code> elements in SVG, which is where we start working with arcs!</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="/printed-books/touch-design-for-mobile-interfaces/">Touch Design for Mobile Interfaces</a></strong>, Steven Hoober’s brand-new guide on <strong>designing for mobile</strong> with proven, universal, human-centric guidelines. <strong>400 pages</strong>, jam-packed with in-depth user research and <strong>best practices</strong>.</p>
<a data-instant href="https://www.smashingmagazine.com/printed-books/touch-design-for-mobile-interfaces/" class="btn btn--green btn--large" style="">Jump to table of contents&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="https://www.smashingmagazine.com/printed-books/touch-design-for-mobile-interfaces/" class="feature-panel-image-link">
<div class="feature-panel-image"><picture><source type="image/avif" srcSet="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/14bcab88-b622-47f6-a51d-76b0aa003597/touch-design-book-shop-opt.avif" />
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b14658fc-bb2d-41a6-8d1a-70eaaf1b8ec8/touch-design-book-shop-opt.png"
    alt="Feature Panel"
    width="480"
    height="697"
/>
</picture>
</div>
</a>
</div>
</aside>
</div>

<h2 id="the-path-element">The <code>&lt;path&gt;</code> Element</h2>

<p>We have an <code>&lt;svg&gt;</code> element. And we’re viewing the element’s contents through the “lens” of a <code>viewBox</code>.</p>

<p>A <code>&lt;path&gt;</code> allows us to draw shapes. We have other elements for drawing shapes &mdash; namely <code>&lt;circle&gt;</code>, <code>&lt;line&gt;</code>, and <code>&lt;polygon&gt;</code> &mdash; but imagine being restricted to strict geometrical shapes as an artist. That’s where the custom <code>&lt;path&gt;</code> element comes in. It’s used to draw complex shapes that cannot be created with the basic ones. Think of <code>&lt;path&gt;</code> as a flexible container that lets you mix and match different drawing commands.</p>

<p>With a single <code>&lt;path&gt;</code>, you can combine multiple drawing commands into one smooth, elegant design. Today, we’re focusing on a super specific path command: <strong>arcs.</strong> In other words, what we’re doing is drawing arc shapes with <code>&lt;path&gt;</code>.</p>

<p>Here’s a quick, no-frills example that places a <code>&lt;path&gt;</code> inside the <code>&lt;svg&gt;</code> example we looked at earlier:</p>

<div class="break-out">
<pre><code class="language-svg">&lt;svg width="600px" height="600px" viewBox="-300 -300 600 600"&gt;</code>
  <code style="font-weight: bold;">&lt;path d="M 0 0 A 100 100 0 1 1 200 0"</code> 
    <code class="language-svg">fill="transparent"
    stroke="black"
    stroke-width="24"
  /&gt;
&lt;/svg&gt;
</code></pre>
</div>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="bNbeQQy"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Arc path animation [forked]](https://codepen.io/smashingmag/pen/bNbeQQy) by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/bNbeQQy">Arc path animation [forked]</a> by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</figcaption>
</figure>

<p>Now, I get it. Looking at that string of numbers for the first time is like staring into the Matrix, right? But once you get the hang of it, you’ll see that arcs aren’t as scary as they look.</p>

<p>Let’s break down the <code>&lt;path&gt;</code> in that example. We’ll break it down even further in the next section, but for now:</p>

<ul>
<li><code>M 0 0</code> moves the path to the center of the <code>viewBox</code> but doesn’t actually “draw” anything just yet.</li>
<li><code>A 100 100 0 1 1 200 0</code> draws an arc with a radius of <code>100</code> in both the X and Y axes, ending at <code>(200, 0)</code>.</li>
</ul>

<p>You can visualize the coordinate positions in red resulting from different <code>M</code> commands in the following demo:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="EaYyOGW"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Arc Possibilities b/w 2 points [forked]](https://codepen.io/smashingmag/pen/EaYyOGW) by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/EaYyOGW">Arc Possibilities b/w 2 points [forked]</a> by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</figcaption>
</figure>

<p>See that? We have two points along the X-axis that are relative to the <code>viewBox</code>&rsquo;s center, and a curved line connects them. Now, know that the numbers in an <code>M</code> command are setting coordinates, and the numbers in an <code>A</code> command draw a line along the SVG’s axes. You just drew a curve in SVG!</p>

<div class="partners__lead-place"></div>

<h2 id="dissecting-an-arc">Dissecting An Arc</h2>

<p>We can zoom into the <code>M</code> and <code>A</code> commands even further to better understand what’s happening.</p>

<pre><code class="language-svg">&lt;path d="M 0 0 A 100 100 0 1 1 200 0" /&gt;
</code></pre>

<p>First off, we’re working with an arc, or more accurately, an <strong>elliptical arc</strong>, which is a curved line. We know that a perfect circle is merely an <em>ellipse</em> with equal radii in both the X and Y directions. We can change the shape of the circle by giving it different, unmatching radii values.</p>

<p>This is what we know so far:</p>

<ul>
<li><code>M</code>

<ul>
<li><strong><code>0</code>:</strong>  Coordinate along the X-axis.</li>
<li><strong><code>0</code>:</strong> Coordinate along the Y-axis.</li>
</ul></li>
<li><code>A</code>

<ul>
<li><strong><code>100</code>:</strong> Radius value in the X direction.</li>
<li><strong><code>100</code>:</strong> Radius value in the Y direction.</li>
<li><strong><code>200</code>:</strong> The arc’s endpoint in the X-direction.</li>
<li><strong><code>0</code>:</strong> The arc’s endpoint in the Y-direction.</li>
</ul></li>
</ul>

<p>There are three values in the <code>A</code> command that we sort of skipped. These are like “switches” in the sense that they are Boolean values that enable or disable certain things about the arc.</p>

<ul>
<li><strong><code>0</code>:</strong> Rotates the arc along the X-axis.</li>
<li><strong><code>1</code>:</strong> Determines whether this is a “small” arc (<code>0</code>) with a span greater than 180° or a “large” arc (<code>1</code>) with a span greater than 180°.</li>
<li><strong><code>1</code>:</strong> Sets whether the arc &ldquo;sweeps” in a clockwise direction or a counter-clockwise direction, where <code>0</code> equals clockwise and <code>1</code> equals counter-clockwise.</li>
</ul>

<p>If we take this information and re-write the <code>&lt;path&gt;</code> with these definitions, then it starts to come together more clearly:</p>

<div class="break-out">
<pre><code class="language-svg">&lt;path d="
  M &lt;x-coordinate&gt; &lt;y-coordinate&gt; 
  A &lt;radius-x&gt; &lt;radius-y&gt; &lt;rotation-x&gt; &lt;large-arc-flag&gt; &lt;sweep-flag&gt; &lt;endpoint-x&gt; &lt;endpoint-y&gt;
" /&gt;
</code></pre>
</div>

<p>Maybe we can simplify that a bit using abbreviations:</p>

<pre><code class="language-svg">&lt;path d="
  M &lt;x&gt; &lt;y&gt; 
  A &lt;rx&gt; &lt;ry&gt; &lt;rotation&gt; &lt;arc&gt; &lt;sweep&gt; &lt;ex&gt; &lt;ey&gt;
" /&gt;
</code></pre>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/mastering-svg-arcs/4-illustrating-arc-x-y-radii.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="754"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/mastering-svg-arcs/4-illustrating-arc-x-y-radii.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/mastering-svg-arcs/4-illustrating-arc-x-y-radii.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/mastering-svg-arcs/4-illustrating-arc-x-y-radii.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/mastering-svg-arcs/4-illustrating-arc-x-y-radii.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/mastering-svg-arcs/4-illustrating-arc-x-y-radii.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/mastering-svg-arcs/4-illustrating-arc-x-y-radii.png"
			
			sizes="100vw"
			alt="Illustrating an arc’s X and Y radii, as well as the amount of its rotation."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/mastering-svg-arcs/4-illustrating-arc-x-y-radii.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Let’s take this information and start playing with values to see how it behaves.</p>

<div class="partners__lead-place"></div>

<h2 id="visualizing-the-possibilities">Visualizing The Possibilities</h2>

<p>Again, if this is the <code>&lt;path&gt;</code> we’re starting with:</p>

<pre><code class="language-svg">&lt;path d="M 0 0 A 100 100 0 1 1 200 0"/&gt;
</code></pre>

<p>Then, we can manipulate it in myriad ways. Mathematically speaking, you can create an infinite number of arcs between any two points by adjusting the parameters. Here are a few variations of an arc that we get when all we do is change the arc’s endpoints in the X (<code>&lt;ex&gt;</code>) and Y (<code>&lt;ey&gt;</code>) directions.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="WbexYLV"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Arc Possibilities b/w 2 points [forked]](https://codepen.io/smashingmag/pen/WbexYLV) by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/WbexYLV">Arc Possibilities b/w 2 points [forked]</a> by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</figcaption>
</figure>

<p>Or, let’s control the arc’s width and height by updating its radius in the X direction (<code>&lt;rx&gt;</code>) and the Y direction (<code>&lt;ry&gt;</code>). If we play around with the <code>&lt;rx&gt;</code> value, we can manipulate the arc’s height:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="xbKOQMr"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Rx [forked]](https://codepen.io/smashingmag/pen/xbKOQMr) by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/xbKOQMr">Rx [forked]</a> by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</figcaption>
</figure>

<p>Similarly, we can manipulate the arc’s width by updating the <code>&lt;ry&gt;</code> value:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="mybEQvG"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Ry [forked]](https://codepen.io/smashingmag/pen/mybEQvG) by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/mybEQvG">Ry [forked]</a> by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</figcaption>
</figure>

<p>Let’s see what happens when we rotate the arc along its X-axis (<code>&lt;rotation&gt;</code>). This parameter rotates the arc’s ellipse around its center. It won’t affect circles, but it’s a game-changer for ellipses.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="VYZjVRx"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [x-axis-rotation [forked]](https://codepen.io/smashingmag/pen/VYZjVRx) by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/VYZjVRx">x-axis-rotation [forked]</a> by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</figcaption>
</figure>

<p>Even with a fixed set of endpoints and radii (<code>&lt;rx&gt;</code> and <code>&lt;ry&gt;</code>), and a given angle of rotation, four distinct arcs can connect them. That’s because we have the <code>&lt;arc&gt;</code> flag value that can be one of two values, as well as the <code>&lt;sweep&gt;</code> flag that is also one of two values. Two boolean values, each with two arguments, give us four distinct possibilities.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="wBwWQOb"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [4 cases [forked]](https://codepen.io/smashingmag/pen/wBwWQOb) by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/wBwWQOb">4 cases [forked]</a> by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</figcaption>
</figure>

<p>And lastly, adjusting the arc’s endpoint along the X (<code>&lt;ex&gt;</code>) and Y (<code>&lt;ey&gt;</code>) directions shifts the arc’s location without changing the overall shape.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="xbKOQeL"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [endx, endy [forked]](https://codepen.io/smashingmag/pen/xbKOQeL) by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/xbKOQeL">endx, endy [forked]</a> by <a href="https://codepen.io/akshaygpt">akshaygpt</a>.</figcaption>
</figure>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>And there you have it, SVG arcs demystified! Whether you’re manipulating radii, rotation, or arc direction, you now have all the tools to master these beautiful curves. With practice, arcs will become just another part of your SVG toolkit, one that gives you the power to create <strong>more dynamic, intricate designs</strong> with confidence.</p>

<p>So keep playing, keep experimenting, and soon you’ll be bending arcs like a pro &mdash; making your SVGs not just functional but beautifully artistic. If you enjoyed this dive into arcs, drop a like or share it with your friends. Let’s keep pushing the boundaries of what SVG can do!</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(gg, yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Myriam Frisano</author><title>SVG Coding Examples: Useful Recipes For Writing Vectors By Hand</title><link>https://www.smashingmagazine.com/2024/09/svg-coding-examples-recipes-writing-vectors-by-hand/</link><pubDate>Wed, 18 Sep 2024 09:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2024/09/svg-coding-examples-recipes-writing-vectors-by-hand/</guid><description>Myriam Frisano explores the basics of hand-coding SVGs with practical examples to demystify the inner workings of common SVG elements. In this guide, you’ll learn about asking the right questions to solve common positioning problems and how to leverage JavaScript so that, by the end, you can add “SVG coding” to your toolbox. You’ll also be able to declare proudly, “I know how to draw literal pictures with words!”</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2024/09/svg-coding-examples-recipes-writing-vectors-by-hand/" />
              <title>SVG Coding Examples: Useful Recipes For Writing Vectors By Hand</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>SVG Coding Examples: Useful Recipes For Writing Vectors By Hand</h1>
                  
                    
                    <address>Myriam Frisano</address>
                  
                  <time datetime="2024-09-18T09:00:00&#43;00:00" class="op-published">2024-09-18T09:00:00+00:00</time>
                  <time datetime="2024-09-18T09:00:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                
                

<p>Even though I am the kind of front-end engineer who manually cleans up SVG files when they are a mess, I never expected to become one of <em>those</em> people. You know, those crazy people that <em>draw with code.</em></p>

<p>But here we are.</p>

<p>I dove deep into SVG specs last winter when I created a project to <a href="https://code.halfapx.com/guideline-generator/">draw Calligraphy Grids</a>, and even though I knew the basic structures and rules of SVG, it was only then that I fully tried to figure out and understand what all of those numbers meant and how they interacted with each other.</p>

<p>And, once you get the hang of it, it is actually very interesting and quite fun to code SVG by hand.</p>

<blockquote><strong>No &lt;path&gt; ahead</strong><br /><br />We won’t go into more complex SVG shapes like <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths">paths</a> in this article, this is more about practical information for simple SVGs. When it comes to drawing curves, I still recommend using a tool like Illustrator or Affinity. However, if you are super into compounding your lines, a path is useful. Maybe we’ll do that in Part 2.<br /><br />Also, this guide focuses mostly on practical examples that illustrate some of the math involved when drawing SVGs. There is a wonderful article here that goes a bit deeper into the specs, which I recommend reading if you’re more interested in that: “<a href="https://www.smashingmagazine.com/2019/05/svg-design-tools-practical-guide/#comments-svg-design-tools-practical-guide">A Practical Guide To SVG And Design Tools</a>.”</blockquote>

<h2 id="drawing-with-math-remember-coordinate-systems">Drawing With Math. Remember Coordinate Systems?</h2>

<p>Illustrator, Affinity, and all other vector programs are basically just helping you draw on a coordinate system, and then those paths and shapes are stored in SVG files.</p>

<p>If you open up these files in an editor, you’ll see that they are just a bunch of paths that contain lots of numbers, which are coordinates in that coordinate system that make up the lines.</p>

<p>But, there is a difference between the all-powerful <code>&lt;path&gt;</code> and the other, more semantic elements like <code>&lt;rect&gt;</code>, <code>&lt;circle&gt;</code>, <code>&lt;line&gt;</code>, <code>&lt;ellipse&gt;</code>, <code>&lt;polygon&gt;</code>, and <code>&lt;polyline&gt;</code>.</p>

<p>These elements are not that hard to read and write by hand, and they open up a lot of possibilities to add animation and other fun stuff. So, while most people might only think of SVGs as never-pixelated, infinitely scaling images, they can also be quite comprehensive pieces of code.</p>

<h3 id="how-does-svg-work-unit-unit">How Does SVG Work? <code>unit != unit</code></h3>

<p>Before we get started on how SVG elements are drawn, let’s talk about the ways units work in SVG because they might be a bit confusing when you first get started.</p>

<p>The beauty of SVG is that it’s a vector format, which means that the units are somewhat detached from the browser and are instead just relative to the coordinate system you’re working in.</p>

<p>That means you would <strong>not</strong> use a unit within SVG but rather just use numbers and then define the size of the document you’re working with.</p>

<p>So, your <code>width</code> and <code>height</code> might be using CSS <code>rem</code> units, but in your <code>viewBox</code>, units become just a concept that helps you in establishing sizing relationships.</p>

<h3 id="what-is-the-viewbox">What Is The <code>viewBox</code>?</h3>

<p>The <code>viewBox</code> works a little bit like the CSS <code>aspect-ratio</code> property. It helps you establish a relationship between the width and the height of your coordinate system and sets up the box you’re working in. I tend to think of the <code>viewBox</code> as my “document” size.</p>

<p>Any element that is placed within the SVG with bigger dimensions than the <code>viewBox</code> will not be visible. So, the <code>viewBox</code> is the cutout of the coordinate system we’re looking through. The <code>width</code> and <code>height</code> attributes are unnecessary if there is a <code>viewBox</code> attribute.</p>

<p>So, in short, having an SVG with a <code>viewBox</code> makes it behave a lot like a regular image. And just like with images, it’s usually easiest to just set either a <code>width</code> or a <code>height</code> and let the other dimension be automatically sized based on the intrinsic aspect ratio dimensions.</p>

<p>So, if we were to create a function that draws an SVG, we might store three separate variables and fill them in like this:</p>

<pre><code class="language-html">`&lt;svg 
  width="${svgWidth}" 
  viewBox="0 0 ${documentWidth} ${documentHeight}" 
  xmlns="http://www.w3.org/2000/svg"
&gt;`;
</code></pre>

<h3 id="svg-things-of-note">SVG Things Of Note</h3>

<p>There is a lot to know about SVG: When you want to reuse an image a lot, you may want to turn it into a <code>symbol</code> that can then be referenced with a <code>use</code> tag, you can create sprites, and there are some best practices when using them for icons, and so on.</p>

<p>Unfortunately, this is a bit out of the scope of this article. Here, we’re mainly focusing on designing SVG files and not on how we can optimize and use them.</p>

<p>However, one thing of note that is easier to implement from the start is <strong>accessibility</strong>.</p>

<p>SVGs can be used in an <code>&lt;img&gt;</code> tag, where <code>alt</code> tags are available, but then you lose the ability to interact with your SVG code, so inlining might be your preference.</p>

<p>When inlining, it’s easiest to declare <code>role=&quot;img&quot;</code> and then add a <code>&lt;title&gt;</code> tag with your image title.</p>

<p><strong>Note</strong>: <em>You can check out <a href="https://www.smashingmagazine.com/2021/05/accessible-svg-patterns-comparison/">this article for SVG and Accessibility recommendations</a>.</em></p>

<pre><code class="language-javascript">&lt;svg
  role="img"
  [...attr]
&gt;
  &lt;title&gt;An accessible title&lt;/title&gt;
  &lt;!-- design code --&gt;
&lt;/svg&gt;
</code></pre>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="/printed-books/typescript-in-50-lessons/">“TypeScript in 50 Lessons”</a></strong>, our shiny new guide to TypeScript. With detailed <strong>code walkthroughs</strong>, hands-on examples and common gotchas. For developers who know enough <strong>JavaScript</strong> to be dangerous.</p>
<a data-instant href="/printed-books/typescript-in-50-lessons/" class="btn btn--green btn--large" style="">Jump to table of contents&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="/printed-books/typescript-in-50-lessons/" class="feature-panel-image-link">
<div class="feature-panel-image"><picture><source type="image/avif" srcSet="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/2732dfe9-e1ee-41c3-871a-6252aeda741c/typescript-panel.avif" />
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/c2f2c6d6-4e85-449a-99f5-58bd053bc846/typescript-shop-cover-opt.png"
    alt="Feature Panel"
    width="481"
    height="698"
/>
</picture>
</div>
</a>
</div>
</aside>
</div>

<h2 id="drawing-svg-with-javascript">Drawing SVG With JavaScript</h2>

<p>There is usually some mathematics involved when drawing SVGs. It’s usually fairly simple arithmetic (except, you know, in case you draw calligraphy grids and then have to dig out trigonometry…), but I think even for simple math, most people don’t write their SVGs in pure HTML and thus would like to use algebra.</p>

<p>At least for me, I find it much easier to understand SVG Code when giving meaning to numbers, so I always stick to JavaScript, and by giving my coordinates names, I like them immeasurable times more.</p>

<p>So, for the upcoming examples, we’ll look at the list of variables with the simple math and then JSX-style templates for interpolation, as that gives more legible syntax highlighting than string interpolations, and then each example will be available as a CodePen.</p>

<p>To keep this Guide framework-agnostic, I wanted to quickly go over drawing SVG elements with just good old vanilla JavaScript.</p>

<p>We’ll create a container element in HTML that we can put our SVG into and grab that element with JavaScript.</p>

<pre><code class="language-html">&lt;div data-svg-container&gt;&lt;/div&gt;
&lt;script src="template.js"&gt;&lt;/script&gt;
</code></pre>

<p>To make it simple, we’ll draw a rectangle <code>&lt;rect&gt;</code> that covers the entire <code>viewBox</code> and uses a fill.</p>

<p><strong>Note</strong>: <em>You can add all valid CSS values as fills, so a fixed color, or something like <code>currentColor</code> to access the site’s text color or a CSS variable would work here if you’re inlining your SVG and want it to interact with the page it’s placed in.</em></p>

<p>Let’s first start with our variable setup.</p>

<div class="break-out">
<pre><code class="language-javascript">// vars
const container = document.querySelector("[data-svg-container]");
const svgWidth = "30rem"; // use any value with units here
const documentWidth = 100;
const documentHeight = 100;
const rectWidth = documentWidth;
const rectHeight = documentHeight;
const rectFill = "currentColor"; // use any color value here
const title = "A simple square box";
</code></pre>
</div>

<h3 id="method-1-create-element-and-set-attributes">Method 1: Create Element and Set Attributes</h3>

<p>This method is easier to keep type-safe (if using TypeScript) &mdash; uses proper SVG elements and attributes, and so on &mdash; but it is less performant and may take a long time if you have many elements.</p>

<div class="break-out">
<pre><code class="language-javascript">const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
const titleElement = document.createElementNS("http://www.w3.org/2000/svg", "title");
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");

svg.setAttribute("width", svgWidth);
svg.setAttribute("viewBox", `0 0 ${documentWidth} ${documentHeight}`);
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
svg.setAttribute("role", "img");

titleElement.textContent = title;

rect.setAttribute("width", rectWidth);
rect.setAttribute("height", rectHeight);
rect.setAttribute("fill", rectFill);

svg.appendChild(titleElement);
svg.appendChild(rect);

container.appendChild(svg);
</code></pre>
</div>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="LYKKVzg"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG Rectangle (JS Method 1) [forked]](https://codepen.io/smashingmag/pen/LYKKVzg) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/LYKKVzg">SVG Rectangle (JS Method 1) [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<h3 id="method-2-create-an-svg-string">Method 2: Create An SVG String</h3>

<p>Alternatively, you can create an SVG string and set the <code>innerHTML</code> of the container to that string. This is more performant, but you lose type safety, and the elements aren’t properly created in the DOM.</p>

<pre><code class="language-javascript">container.innerHTML = `
&lt;svg 
  width="${svgWidth}" 
  viewBox="0 0 ${documentWidth} ${documentHeight}" 
  xmlns="http://www.w3.org/2000/svg" 
  role="img"
&gt;
  &lt;title&gt;${title}&lt;/title&gt;
  &lt;rect 
    width="${rectWidth}" 
    height="${rectHeight}" 
    fill="${rectFill}" 
  /&gt;
&lt;/svg&gt;`;
</code></pre>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="BaggNmN"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG Rectangle (JS Method 2) [forked]](https://codepen.io/smashingmag/pen/BaggNmN) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/BaggNmN">SVG Rectangle (JS Method 2) [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<h3 id="method-3-best-of-both-worlds">Method 3: Best Of Both Worlds</h3>

<p>The best of both worlds is to just create the SVG itself as a DOM element and then set the content of the SVG via <code>innerHTML</code>.</p>

<p>We’re appending a proper SVG element to the container and can type-check that and have access to it properly. You aren’t typically going to be changing the content of the SVG that much, so I feel like this is probably the best way to do it.</p>

<div class="break-out">
<pre><code class="language-javascript">const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");

svg.setAttribute("width", svgWidth);
svg.setAttribute("viewBox", `0 0 ${documentWidth} ${documentHeight}`);
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
svg.setAttribute("role", "img");

svg.innerHTML = `
  &lt;title&gt;${title}&lt;/title&gt;
  &lt;rect 
    width="${rectWidth}" 
    height="${rectHeight}" 
    fill="${rectFill}" 
  /&gt;
`;

container.appendChild(svg);
</code></pre>
</div>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="RwzzPjz"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG Rectangle (JS Method 3) [forked]](https://codepen.io/smashingmag/pen/RwzzPjz) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/RwzzPjz">SVG Rectangle (JS Method 3) [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<h2 id="drawing-basic-elements">Drawing Basic Elements</h2>

<p>Okay, so now that we have the basics of the SVG setup, let’s look into how the most common elements are drawn.</p>

<h3 id="drawing-boxes">Drawing Boxes</h3>

<p><code>&lt;rect&gt;</code> creates a box, as we&rsquo;ve learned in the previous example. It has <code>y</code> and <code>x</code> attributes, which define the position of the top left corner. They are optional, and if not set, the box will be drawn at the origin <code>(0,0)</code> like in that previous example.</p>

<p>There are also <code>rx</code> and <code>ry</code> attributes. Those are radii. If you define <code>rx</code>, <code>ry</code> will automatically be set to the same value unless you redeclare it, then you’d use an elliptical corner-radius instead of a circular one.</p>

<p>Let’s draw four different rectangles in our next SVG, one in each quadrant:</p>

<ol>
<li>Top left: This is just a rectangle with a top and left offset and a width and height.</li>
<li>Top right: We will make use of a small corner radius to make it a rounded rectangle.</li>
<li>Bottom left: It uses such a large corner radius that it turns into a circle. It has a bit of a weird box origin, but it’s an option.</li>
<li>Bottom right: It uses an elliptical corner radius for this squoval shape.</li>
</ol>

<p>This is the implementation in JavaScript:</p>

<pre><code class="language-javascript">const rectDocWidth = 200;
const rectDocHeight = 200;
const rectFill = "currentColor";
const docOffset = 15;
const rectSize = rectDocWidth / 2 - docOffset &#42; 2;
const roundedCornerRadius = 10;
const circleLookRadius = rectSize / 2;
const ellipticalRy = roundedCornerRadius &#42; 2;
</code></pre>

<p>And to then set up the SVG, we’ll apply these variables to the template:</p>

<div class="break-out">
<pre><code class="language-jsx">&lt;svg
  width={svgWidth}
  viewBox={`0 0 ${rectDocWidth} ${rectDocHeight}`}
  xmlns="http://www.w3.org/2000/svg"
  role="img"
&gt;
  &lt;title&gt;Four Rectangles of different qualities placed in each quadrant&lt;/title&gt;
  &lt;rect
    x={docOffset}
    y={docOffset}
    width={rectSize}
    height={rectSize}
    fill={rectFill}
  /&gt;
  &lt;rect
    x={rectDocWidth - rectSize - docOffset}
    rx={roundedCornerRadius}
    y={docOffset}
    width={rectSize}
    height={rectSize}
    fill={rectFill}
  /&gt;
  &lt;rect
    x={docOffset}
    rx={circleLookRadius}
    y={rectDocHeight - rectSize - docOffset}
    width={rectSize}
    height={rectSize}
    fill={rectFill}
  /&gt;
  &lt;rect
    x={rectDocWidth - rectSize - docOffset}
    rx={roundedCornerRadius}
    ry={ellipticalRy}
    y={rectDocHeight - rectSize - docOffset}
    width={rectSize}
    height={rectSize}
    fill={rectFill}
  /&gt;
&lt;/svg&gt;
</code></pre>
</div>

<p>And this is the result:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="mdZZJpN"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG Rect [forked]](https://codepen.io/smashingmag/pen/mdZZJpN) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/mdZZJpN">SVG Rect [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<div class="partners__lead-place"></div>

<h2 id="drawing-lines">Drawing Lines</h2>

<p>There is a <code>&lt;line&gt;</code> element in SVG that takes an <code>x1</code>, <code>y1</code>, <code>x2</code>, and <code>y2</code> attribute, which are the coordinates of the start and end points of the line.</p>

<p>For me, knowing how to draw straight horizontal or vertical lines was fairly important.</p>

<p>The rules for that are simple: <strong>We’ll just have to make sure that the <code>y</code> values are the same for a horizontal line and the <code>x</code> values are the same for a vertical line.</strong></p>

<p>Let’s look at an example where we draw a horizontal and a vertical line through the center of our document. I purposefully used some weirder numbers here; you’ll see that the resulting SVG is still perfectly centered, though, since it’s totally fine to use floating point numbers in SVG, and we don’t really run into subpixel rendering issues as we do in some CSS cases, where we end up with fractional pixels.</p>

<p>These are the JavaScript variables we set up:</p>

<pre><code class="language-javascript">const lineDocWidth = 421;
const lineDocHeight = 391;
const lineStroke = "currentColor";
const lineStrokeWidth = 5;
const horizontalLineStart = 0;
const horizontalLineEnd = lineDocWidth;
const horizontalLineY = lineDocHeight / 2;
const verticalLineStart = 0;
const verticalLineEnd = lineDocHeight;
const verticalLineX = lineDocWidth / 2;
</code></pre>

<p>And this is how we can integrate these variables into the SVG element:</p>

<div class="break-out">
<pre><code class="language-jsx">&lt;svg
  width={svgWidth}
  viewBox={`0 0 ${lineDocWidth} ${lineDocHeight}`}
  xmlns="http://www.w3.org/2000/svg"
  role="img"
&gt;
  &lt;title&gt;Horizontal and Vertical Line through the middle of the document&lt;/title&gt;
  &lt;line
    x1={horizontalLineStart}
    x2={horizontalLineEnd}
    y1={horizontalLineY}
    y2={horizontalLineY}
    stroke={lineStroke}
    stroke-width={lineStrokeWidth}
  /&gt;
  &lt;line
    x1={verticalLineX}
    x2={verticalLineX}
    y1={verticalLineStart}
    y2={verticalLineEnd}
    stroke={lineStroke}
    stroke-width={lineStrokeWidth}
  /&gt;
&lt;/svg&gt;
</code></pre>
</div>

<p>And here’s our result:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="abggOqK"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG Line [forked]](https://codepen.io/smashingmag/pen/abggOqK) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/abggOqK">SVG Line [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<h3 id="drawing-circles">Drawing Circles</h3>

<p><code>&lt;circle&gt;</code> elements have <code>cx</code>, <code>cy</code>, and <code>r</code> as coordinates. The <code>x</code> and <code>y</code> values are relative to the circle center, and <code>r</code> describes the radius of the circle.</p>

<p>This is where things are less intuitive in my head because there will be times when I want the edge of the circle to be placed at a certain point and not the center, and I’ll usually also think in terms of diameters, not radii.</p>

<p>So, let’s say we want to draw a circle whose outer edge is offset from the bottom left corner by a certain amount and whose diameter is a certain size. We’d have to do some math again to calculate our coordinates.</p>

<p>These are the variables in JavaScript that we’re working with:</p>

<div class="break-out">
<pre><code class="language-javascript">const circleDocWidth = 100;
const circleDocHeight = 100;
const circleOffset = 10;
const circleDiameter = 20;
const circleRadius = circleDiameter / 2;
const circleX = circleOffset + circleRadius;
const circleY = circleDocHeight - circleOffset - circleRadius;
</code></pre>
</div>

<p>And, just like before, this is how we might integrate them into the SVG element:</p>

<pre><code class="language-jsx">&lt;svg
  width={svgWidth}
  viewBox={`0 0 ${circleDocWidth} ${circleDocHeight}`}
  xmlns="http://www.w3.org/2000/svg"
  role="img"
&gt;
  &lt;circle
    cx={circleX}
    cy={circleY}
    r={circleRadius}
    fill="red"
  /&gt;
&lt;/svg&gt;
</code></pre>

<p>And this is what it looks like:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="KKjjpoW"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG Circle [forked]](https://codepen.io/smashingmag/pen/KKjjpoW) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/KKjjpoW">SVG Circle [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<h3 id="drawing-ellipses">Drawing Ellipses</h3>

<p><code>&lt;ellipse&gt;</code> elements have <code>cx</code>, <code>cy</code>, <code>rx</code>, and <code>ry</code> as coordinates. The <code>x</code> and <code>y</code> values are relative to the ellipse center, and <code>rx</code> and <code>ry</code> describe the radius of the ellipse.</p>

<p>Let’s draw an ellipse that is offset from the top right corner by a certain amount, whose horizontal radius is a certain size, and whose vertical radius is half of that.</p>

<p>For that we need to define our variables in JavaScript:</p>

<div class="break-out">
<pre><code class="language-javascript">const ellipseSVGWidth = 100;
const ellipseDocWidth = 100;
const ellipseDocHeight = 100;
const ellipseOffset = 10;
const ellipseHorizontalRadius = ellipseDocWidth / 2 - ellipseOffset;
const ellipseVerticalRadius = ellipseHorizontalRadius / 2;
const ellipseX = ellipseDocWidth - ellipseOffset - ellipseHorizontalRadius;
const ellipseY = ellipseOffset + ellipseVerticalRadius;
</code></pre>
</div>

<p>…and integrate them into the SVG element:</p>

<div class="break-out">
<pre><code class="language-jsx">&lt;svg
  width={svgWidth}
  viewBox={`0 0 ${ellipseDocWidth} ${ellipseDocHeight}`}
  xmlns="http://www.w3.org/2000/svg"
  role="img"
&gt;
  &lt;title&gt;Ellipse offset from the top right corner&lt;/title&gt;
  &lt;ellipse
    cx={ellipseX}
    cy={ellipseY}
    rx={ellipseHorizontalRadius}
    ry={ellipseVerticalRadius}
    fill="hotpink"
  /&gt;
&lt;/svg&gt;
</code></pre>
</div>

<p>Here’s the result:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="NWZZqMR"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG Ellipse [forked]](https://codepen.io/smashingmag/pen/NWZZqMR) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/NWZZqMR">SVG Ellipse [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<h3 id="connecting-dots-with-polyline-and-polygon">Connecting Dots With <code>&lt;polyline&gt;</code> And <code>&lt;polygon&gt;</code></h3>

<p>Let’s say we want to have a line that has multiple points but doesn’t make a rectangle or a circle.</p>

<p>This is where we can use <code>polyline</code> and <code>polygon</code>, which share the same attributes and only differ in the way that a <code>polygon</code> will connect the first and last point, while a <code>polyline</code> won’t.</p>

<p>They take a <code>points</code> attribute, which is a list of <code>x</code> and <code>y</code> values separated by a space, and, by default, both of them have a <code>fill</code>, which can be a bit strange. That’s especially true for a <code>polyline</code>, so you might want to set that value to <code>none</code>.</p>

<p>Let’s say we have three circles, and we want to have lines connecting their centers. We can just take the <code>cx</code> and <code>cy</code> values of those circles and chain them together in the <code>points</code> attribute.</p>

<p>SVG is drawn from background to foreground, so the circles are drawn first, then the lines so they are stacked on top of each other.</p>

<p>To notice the differences between the polyline and the polygon, we’ll draw our composite four times, like we did before with the circles.</p>

<p>This time, we have more than one element, though. To make it quicker to scan which set belongs together, we can make use of the <code>g</code> element, which groups multiple elements together. It allows us to apply certain attributes to all children at the same time.</p>

<p>To see that in action and to save us a bit of time, in having to adjust <code>x</code> and <code>y</code> values for each separate element within the composite, we can apply a <code>transform</code> to that group element to push our composite into the different quadrants.</p>

<p><code>transform=&quot;translate(x,y)</code>&rdquo; is how we do that. The transform attribute works a lot like CSS transforms, with slight differences in syntax. But in most simple cases, we can assume the same thing to happen. The translate attribute will take the original position and then move the elements contained within the group along the <code>x</code> and <code>y</code> axis.</p>

<p>So, let’s have a look at our SVG:</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-coding-examples-recipes-writing-vectors-by-hand/polygon-polyline-composite-fixed.svg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="800"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-coding-examples-recipes-writing-vectors-by-hand/polygon-polyline-composite-fixed.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-coding-examples-recipes-writing-vectors-by-hand/polygon-polyline-composite-fixed.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-coding-examples-recipes-writing-vectors-by-hand/polygon-polyline-composite-fixed.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-coding-examples-recipes-writing-vectors-by-hand/polygon-polyline-composite-fixed.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-coding-examples-recipes-writing-vectors-by-hand/polygon-polyline-composite-fixed.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-coding-examples-recipes-writing-vectors-by-hand/polygon-polyline-composite-fixed.png"
			
			sizes="100vw"
			alt="From left to right, bottom to top: Polyline with no fill applied, polyline with fill, polygon with no fill, polygon with fill"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      From left to right, bottom to top: Polyline with no fill applied, polyline with fill, polygon with no fill, polygon with fill. (<a href='https://files.smashing.media/articles/svg-coding-examples-recipes-writing-vectors-by-hand/polygon-polyline-composite-fixed.svg'>SVG preview</a>)
    </figcaption>
  
</figure>

<p>Here, you can see that with the same coordinates, a polyline won’t draw the line between the blue and the red dot, while a polygon will. However, when applying a fill, they take the exact same information as if the shape was closed, which is the right side of the graphic, where the polyline makes it look like a piece of a circle is missing.</p>

<p>This is the second time where we have dealt with quite a bit of repetition, and we can have a look at how we could leverage the power of JavaScript logic to render our template faster.</p>

<p>But first, we need a basic implementation like we’ve done before. We’re creating objects for the circles, and then we’re chaining the <code>cx</code> and <code>cy</code> values together to create the <code>points</code> attribute. We’re also storing our transforms in variables.</p>

<div class="break-out">
<pre><code class="language-javascript">const polyDocWidth = 200;
const polyDocHeight = 200;
const circleOne = { cx: 25, cy: 80, r: 10, fill: "red" };
const circleTwo = { cx: 40, cy: 20, r: 5, fill: "lime" };
const circleThree = { cx: 70, cy: 60, r: 8, fill: "cyan" };
const points = `${circleOne.cx},${circleOne.cy} ${circleTwo.cx},${circleTwo.cy} ${circleThree.cx},${circleThree.cy}`;
const moveToTopRight = `translate(${polyDocWidth / 2}, 0)`;
const moveToBottomRight = `translate(${polyDocWidth / 2}, ${polyDocHeight / 2})`;
const moveToBottomLeft = `translate(0, ${polyDocHeight / 2})`;
</code></pre>
</div>

<p>And then, we apply the variables to the template, using either a <code>polyline</code> or <code>polygon</code> element and a <code>fill</code> attribute that is either set to <code>none</code> or a color value.</p>

<pre><code class="language-jsx">
&lt;svg
  width={svgWidth}
  viewBox={`0 0 ${polyDocWidth} ${polyDocHeight}`}
  xmlns="http://www.w3.org/2000/svg"
  role="img"
&gt;
  &lt;title&gt;Composite shape comparison&lt;/title&gt;
  &lt;g&gt;
    &lt;circle
      cx={circleOne.cx}
      cy={circleOne.cy}
      r={circleOne.r}
      fill={circleOne.fill}
    /&gt;
    &lt;circle
      cx={circleTwo.cx}
      cy={circleTwo.cy}
      r={circleTwo.r}
      fill={circleTwo.fill}
    /&gt;
    &lt;circle
      cx={circleThree.cx}
      cy={circleThree.cy}
      r={circleThree.r}
      fill={circleThree.fill}
    /&gt;
    &lt;polyline
      points={points}
      fill="none"
      stroke="black"
    /&gt;
  &lt;/g&gt;
  &lt;g transform={moveToTopRight}&gt;
    &lt;circle
      cx={circleOne.cx}
      cy={circleOne.cy}
      r={circleOne.r}
      fill={circleOne.fill}
    /&gt;
    &lt;circle
      cx={circleTwo.cx}
      cy={circleTwo.cy}
      r={circleTwo.r}
      fill={circleTwo.fill}
    /&gt;
    &lt;circle
      cx={circleThree.cx}
      cy={circleThree.cy}
      r={circleThree.r}
      fill={circleThree.fill}
    /&gt;
    &lt;polyline
      points={points}
      fill="white"
      stroke="black"
    /&gt;
  &lt;/g&gt;
  &lt;g transform={moveToBottomLeft}&gt;
    &lt;circle
      cx={circleOne.cx}
      cy={circleOne.cy}
      r={circleOne.r}
      fill={circleOne.fill}
    /&gt;
    &lt;circle
      cx={circleTwo.cx}
      cy={circleTwo.cy}
      r={circleTwo.r}
      fill={circleTwo.fill}
    /&gt;
    &lt;circle
      cx={circleThree.cx}
      cy={circleThree.cy}
      r={circleThree.r}
      fill={circleThree.fill}
    /&gt;
    &lt;polygon
      points={points}
      fill="none"
      stroke="black"
    /&gt;
  &lt;/g&gt;
  &lt;g transform={moveToBottomRight}&gt;
    &lt;circle
      cx={circleOne.cx}
      cy={circleOne.cy}
      r={circleOne.r}
      fill={circleOne.fill}
    /&gt;
    &lt;circle
      cx={circleTwo.cx}
      cy={circleTwo.cy}
      r={circleTwo.r}
      fill={circleTwo.fill}
    /&gt;
    &lt;circle
      cx={circleThree.cx}
      cy={circleThree.cy}
      r={circleThree.r}
      fill={circleThree.fill}
    /&gt;
    &lt;polygon
      points={points}
      fill="white"
      stroke="black"
    /&gt;
  &lt;/g&gt;
&lt;/svg&gt;
</code></pre>

<p>And here’s a version of it to play with:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="OJeeVoM"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG Polygon / Polyline (simple) [forked]](https://codepen.io/smashingmag/pen/OJeeVoM) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/OJeeVoM">SVG Polygon / Polyline (simple) [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<div class="partners__lead-place"></div>

<h2 id="dealing-with-repetition">Dealing With Repetition</h2>

<p>When it comes to drawing SVGs, you may find that you’ll be repeating a lot of the same code over and over again. This is where JavaScript can come in handy, so let’s look at the composite example again and see how we could optimize it so that there is less repetition.</p>

<p><strong>Observations:</strong></p>

<ul>
<li>We have three circle elements, all following the same pattern.</li>
<li>We create one repetition to change the <code>fill</code> style for the element.</li>
<li>We repeat those two elements one more time, with either a <code>polyline</code> or a <code>polygon</code>.</li>
<li>We have four different <code>transforms</code> (technically, no transform is a transform in this case).</li>
</ul>

<p>This tells us that we can create nested loops.</p>

<p>Let’s go back to just a vanilla implementation for this since the way loops are done is quite different across frameworks.</p>

<p>You could make this more generic and write separate generator functions for each type of element, but this is just to give you an idea of what you could do in terms of logic. There are certainly still ways to optimize this.</p>

<p>I’ve opted to have arrays for each type of variation that we have and wrote a helper function that goes through the data and builds out an array of objects with all the necessary information for each group. In such a short array, it would certainly be a viable option to just have the data stored in one element, where the values are repeated, but we’re taking the DRY thing seriously in this one.</p>

<p>The group array can then be looped over to build our SVG HTML.</p>

<div class="break-out">
<pre><code class="language-javascript">const container = document.querySelector("[data-svg-container]");
const svgWidth = 200;
const documentWidth = 200;
const documentHeight = 200;
const halfWidth = documentWidth / 2;
const halfHeight = documentHeight / 2;
const circles = [
  { cx: 25, cy: 80, r: 10, fill: "red" },
  { cx: 40, cy: 20, r: 5, fill: "lime" },
  { cx: 70, cy: 60, r: 8, fill: "cyan" },
];
const points = circles.map(({ cx, cy }) =&gt; `${cx},${cy}`).join(" ");
const elements = ["polyline", "polygon"];
const fillOptions = ["none", "white"];
const transforms = [
  undefined,
  `translate(${halfWidth}, 0)`,
  `translate(0, ${halfHeight})`,
  `translate(${halfWidth}, ${halfHeight})`,
];
const makeGroupsDataObject = () =&gt; {
  let counter = 0;
  const g = [];
  elements.forEach((element) =&gt; {
    fillOptions.forEach((fill) =&gt; {
      const transform = transforms[counter++];
      g.push({ element, fill, transform });
    });
  });
  return g;
};
const groups = makeGroupsDataObject();
// result:
// [
//   {
//     element: "polyline",
//     fill: "none",
//   },
//   {
//     element: "polyline",
//     fill: "white",
//     transform: "translate(100, 0)",
//   },
//   {
//     element: "polygon",
//     fill: "none",
//     transform: "translate(0, 100)",
//   },
//   {
//     element: "polygon",
//     fill: "white",
//     transform: "translate(100, 100)",
//   }
// ]

const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width", svgWidth);
svg.setAttribute("viewBox", `0 0 ${documentWidth} ${documentHeight}`);
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
svg.setAttribute("role", "img");
svg.innerHTML = "&lt;title&gt;Composite shape comparison&lt;/title&gt;";
groups.forEach((groupData) =&gt; {
  const circlesHTML = circles
    .map((circle) =&gt; {
      return `
        &lt;circle 
          cx="${circle.cx}" 
          cy="${circle.cy}" 
          r="${circle.r}" 
          fill="${circle.fill}"
        /&gt;`;
    })
    .join("");
  const polyElementHTML = `
    &lt;${groupData.element} 
      points="${points}" 
      fill="${groupData.fill}" 
      stroke="black" 
    /&gt;`;
  const group = `
      &lt;g ${groupData.transform ? `transform="${groupData.transform}"` : ""}&gt;
        ${circlesHTML}
        ${polyElementHTML}
      &lt;/g&gt;
    `;
  svg.innerHTML += group;
});
container.appendChild(svg);
</code></pre>
</div>

<p>And here’s the Codepen of that:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="XWLLbPq"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG Polygon / Polyline (JS loop version) [forked]](https://codepen.io/smashingmag/pen/XWLLbPq) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/XWLLbPq">SVG Polygon / Polyline (JS loop version) [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<h2 id="more-fun-stuff">More Fun Stuff</h2>

<p>Now, that’s all the basics I wanted to cover, but there is so much more you can do with SVG. There is more you can do with <code>transform</code>; you can use a <code>mask</code>, you can use a <code>marker</code>, and so on.</p>

<p>We don’t have time to dive into all of them today, but since this started for me when making Calligraphy Grids, I wanted to show you the two most satisfying ones, which I, unfortunately, can’t use in the generator since I wanted to be able to open my generated SVGs in Affinity and it doesn’t support <code>pattern</code>.</p>

<p>Okay, so <code>pattern</code> is part of the <code>defs</code> section within the SVG, which is where you can define reusable elements that you can then reference in your SVG.</p>

<h3 id="graph-grid-with-pattern">Graph Grid with <code>pattern</code></h3>

<p>If you think about it, a graph is just a bunch of horizontal and vertical lines that repeat across the x- and y-axis.</p>

<p>So, <code>pattern</code> can help us with that. We can create a <code>&lt;rect&gt;</code> and then reference a <code>pattern</code> in the <code>fill</code> attribute of the <code>rect</code>. The pattern then has its own <code>width</code>, <code>height</code>, and <code>viewBox</code>, which defines how the pattern is repeated.</p>

<p>So, let’s say we want to perfectly center our graph grid in any given width or height, and we want to be able to define the size of our resulting squares (cells).</p>

<p>Once again, let’s start with the JavaScipt variables:</p>

<pre><code class="language-javascript">const graphDocWidth = 226;
const graphDocHeight = 101;
const cellSize = 5;
const strokeWidth = 0.3;
const strokeColor = "currentColor";
const patternHeight = (cellSize / graphDocHeight) &#42; 100;
const patternWidth = (cellSize / graphDocWidth) &#42; 100;
const gridYStart = (graphDocHeight % cellSize) / 2;
const gridXStart = (graphDocWidth % cellSize) / 2;
</code></pre>

<p>Now, we can apply them to the SVG element:</p>

<pre><code class="language-jsx">&lt;svg
  width={svgWidth}
  viewBox={`0 0 ${graphDocWidth} ${graphDocHeight}`}
  xmlns="http://www.w3.org/2000/svg"
  role="img"
&gt;
  &lt;defs&gt;
    &lt;pattern
      id="horizontal"
      viewBox={`0 0 ${graphDocWidth} ${strokeWidth}`}
      width="100%"
      height={`${patternHeight}%`}
    &gt;
      &lt;line
        x1="0"
        x2={graphDocWidth}
        y1={gridYStart}
        y2={gridYStart}
        stroke={strokeColor}
        stroke-width={strokeWidth}
      /&gt;
    &lt;/pattern&gt;
    &lt;pattern
      id="vertical"
      viewBox={`0 0 ${strokeWidth} ${graphDocHeight}`}
      width={`${patternWidth}%`}
      height="100%"
    &gt;
      &lt;line
        y1={0}
        y2={graphDocHeight}
        x1={gridXStart}
        x2={gridXStart}
        stroke={strokeColor}
        stroke-width={strokeWidth}
      /&gt;
    &lt;/pattern&gt;
  &lt;/defs&gt;
  &lt;title&gt;A graph grid&lt;/title&gt;
  &lt;rect
    width={graphDocWidth}
    height={graphDocHeight}
    fill="url(&#35;horizontal)"
  /&gt;
  &lt;rect
    width={graphDocWidth}
    height={graphDocHeight}
    fill="url(&#35;vertical)"
  /&gt;
&lt;/svg&gt;
</code></pre>

<p>And this is what that then looks like:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="XWLLbxq"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG Graph Grid [forked]](https://codepen.io/smashingmag/pen/XWLLbxq) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/XWLLbxq">SVG Graph Grid [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<h3 id="dot-grid-with-pattern">Dot Grid With <code>pattern</code></h3>

<p>If we wanted to draw a dot grid instead, we could simply repeat a circle. Or, we could alternatively use a line with a <code>stroke-dasharray</code> and <code>stroke-dashoffset</code> to create a dashed line. And we’d only need one line in this case.</p>

<p>Starting with our JavaScript variables:</p>

<pre><code class="language-javascript">const dotDocWidth = 219;
const dotDocHeight = 100;
const cellSize = 4;
const strokeColor = "black";
const gridYStart = (dotDocHeight % cellSize) / 2;
const gridXStart = (dotDocWidth % cellSize) / 2;
const dotSize = 0.5;
const patternHeight = (cellSize / dotDocHeight) &#42; 100;
</code></pre>

<p>And then adding them to the SVG element:</p>

<pre><code class="language-jsx">&lt;svg
  width={svgWidth}
  viewBox={`0 0 ${dotDocWidth} ${dotDocHeight}`}
  xmlns="http://www.w3.org/2000/svg"
  role="img"
&gt;
  &lt;defs&gt;
    &lt;pattern
      id="horizontal-dotted-line"
      viewBox={`0 0 ${dotDocWidth} ${dotSize}`}
      width="100%"
      height={`${patternHeight}%`}
    &gt;
      &lt;line
        x1={gridXStart}
        y1={gridYStart}
        x2={dotDocWidth}
        y2={gridYStart}
        stroke={strokeColor}
        stroke-width={dotSize}
        stroke-dasharray={`0,${cellSize}`}
        stroke-linecap="round"
      &gt;&lt;/line&gt;
    &lt;/pattern&gt;
  &lt;/defs&gt;
  &lt;title&gt;A Dot Grid&lt;/title&gt;
  &lt;rect
    x="0"
    y="0"
    width={dotDocWidth}
    height={dotDocHeight}
    fill="url(&#35;horizontal-dotted-line)"
  &gt;&lt;/rect&gt;
&lt;/svg&gt;
</code></pre>

<p>And this is what that looks like:</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="eYwwNQM"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG Dot Grid [forked]](https://codepen.io/smashingmag/pen/eYwwNQM) by <a href="https://codepen.io/mynimi">Myriam</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/eYwwNQM">SVG Dot Grid [forked]</a> by <a href="https://codepen.io/mynimi">Myriam</a>.</figcaption>
</figure>

<h2 id="conclusion">Conclusion</h2>

<p>This brings us to the end of our little introductory journey into SVG. As you can see, coding SVG by hand is not as scary as it seems. If you break it down into the basic elements, it becomes quite like any other coding task:</p>

<ul>
<li>We analyze the problem,</li>
<li>Break it down into smaller parts,</li>
<li>Examine each coordinate and its mathematical breakdown,</li>
<li>And then put it all together.</li>
</ul>

<p>I hope that this article has given you a starting point into the wonderful world of coded images and that it gives you the motivation to delve deeper into the specs and try drawing some yourself.</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Mihai Cora</author><title>Creating Custom Lottie Animations With SVGator</title><link>https://www.smashingmagazine.com/2024/09/creating-custom-lottie-animations-svgator/</link><pubDate>Tue, 17 Sep 2024 11:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2024/09/creating-custom-lottie-animations-svgator/</guid><description>Creating ready-to-implement Lottie animations with a single tool is now possible thanks to SVGator’s latest feature updates. In this article, you will learn how to create and animate a Lottie using SVGator, an online animation tool that has zero learning curve if you’re familiar with at least one design tool. Have a closer look at the tool’s main functionalities and the straightforward creation process.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2024/09/creating-custom-lottie-animations-svgator/" />
              <title>Creating Custom Lottie Animations With SVGator</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Creating Custom Lottie Animations With SVGator</h1>
                  
                    
                    <address>Mihai Cora</address>
                  
                  <time datetime="2024-09-17T11:00:00&#43;00:00" class="op-published">2024-09-17T11:00:00+00:00</time>
                  <time datetime="2024-09-17T11:00:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                <p>This article is sponsored by <b>SVGator</b></p>
                

<p>SVGator has gone through a series of updates since our last article, which was published in 2021, when it was already considered to be the most advanced web-based tool for vector animation. The first step toward more versatile software came with the mobile export feature that made it possible to implement the animations in iOS and Android applications.</p>

<p>The animation tool continued its upgrade with a series of new export options: video formats including MP4, AVI, MKV, MOV, and WebM, as well as image formats such as GIF, Animated PNG, WebP, and image sequence. By covering a larger area of users’ needs, the app now enables anyone to create animated stickers, social media, and newsletter animations, video assets, and many more types of visual content on demand.</p>

<p>The goal of becoming a “one tool for all” still lacked the last piece of the puzzle, namely full support for Lottie files. Lottie, just like SVG, is a vector-based format, but it has even <strong>better comprehensive multi-platform support</strong>, a fact that makes it super popular among developers and design professionals. It is built for use across various platforms, enabling <strong>smooth integration into both web and mobile applications</strong>. Its file size is minimal, it is <strong>infinitely scalable</strong>, and developers find it straightforward to implement once they get familiar with the format. Lottie can incorporate raster graphics and also supports interactivity.</p>

<p>SVGator’s latest version has everything you need for your various applications without the need for any third-party apps or plug-ins.</p>

<p><strong>Note</strong>: <em>You can test all of SVGator’s functionalities free of charge before committing to the Pro plan. However, you can export up to three watermarked files, with videos and GIFs limited to basic quality.</em></p>

<p>In this article, we will follow a creation process made of these steps:</p>

<ul>
<li>Importing an existent Lottie JSON and making some minor adjustments;</li>
<li>Importing new animated assets created with SVGator (using the library);</li>
<li>Creating and animating new elements from scratch;</li>
<li>Exporting the Lottie animation.</li>
</ul>

<h2 id="getting-started-with-svgator">Getting Started With SVGator</h2>

<p>The sign-up process is simple, fast, and straightforward, and no credit card is required. Sign up either with Google or Facebook or, alternatively, by providing your name, email address, and password. Start a project either with a Lottie animation or a static SVG. If you don’t have an existing file, you can design and animate everything starting from a blank canvas.</p>

<p>Now that you’ve created your account, let’s dive right into the fun part. Here’s a preview of how your animation is going to look by the time you’re done following this guide. Neat, right?</p>

<figure><a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/1-final-animation.gif"><img src="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/1-final-animation.gif" width="800" height="450" alt="Final animation" /></a><figcaption>(<a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/1-final-animation.gif">Large preview</a>)</figcaption></figure>

<h2 id="create-a-new-project">Create A New Project</h2>

<p>After logging in and clicking on the <strong>New Project</strong> option, you will be taken to the <strong>New Project Panel</strong>, where you can choose between starting from a blank project or uploading a file. Let’s start this project with an existing Lottie JSON.</p>

<ul>
<li><a href="https://cdn.svgator.com/assets/pub/fast-response.json">Download the Lottie demo</a></li>
</ul>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/2-upload-file-lottie.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/2-upload-file-lottie.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/2-upload-file-lottie.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/2-upload-file-lottie.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/2-upload-file-lottie.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/2-upload-file-lottie.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/2-upload-file-lottie.png"
			
			sizes="100vw"
			alt="A screenshot with the selected Upload button"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/2-upload-file-lottie.png'>Large preview</a>)
    </figcaption>
  
</figure>

<ol>
  <li>Click on the <strong>Upload file</strong> button and navigate to the directory where you have saved your Lottie file.<br />
<br />













<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/3-lottie-open-file.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/3-lottie-open-file.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/3-lottie-open-file.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/3-lottie-open-file.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/3-lottie-open-file.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/3-lottie-open-file.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/3-lottie-open-file.png"
			
			sizes="100vw"
			alt="A screenshot with the selected Fast response.json file and Open button"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/3-lottie-open-file.png'>Large preview</a>)
    </figcaption>
  
</figure>
  </li>
  <li>Select the “<strong>Fast response.json</strong>” file and click <strong>Open</strong>.<br /> 
<br />
Hit play in the editor, and the animation should look like this:<br />
<br />

<figure><a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/4-animation-svgator.gif"><img src="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/4-animation-svgator.gif" width="800" height="449" alt="An animation opened in SVGator" /></a><figcaption>(<a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/4-animation-svgator.gif"><em>Large preview</em></a>)</figcaption></figure>
  </li>
</ol>

<p><strong>Note</strong>: <em>Make sure to hit</em> <strong><em>Save</em></strong> <em>after each step to make sure you don’t lose any of your progress while working on this project alongside our guide.</em></p>

<h2 id="import-an-animated-asset">Import An Animated Asset</h2>

<p>In this step, you will learn how to use the <strong>Library</strong> to import new assets to your project. You can easily choose from a variety of ready-made SVGs stored in different categories, load new files from your computer (Lottie, static SVG, and images), or save animations from other SVGator projects and reuse them.</p>

<p>In this case, let’s use an animated message bubble previously created and saved to the <strong>Uploads</strong> section of the <strong>Library</strong>.</p>

<p>Learn how to <strong>create and save animated assets</strong> with this <a href="https://www.youtube.com/watch?v=R2Px90nfOEI">short video tutorial</a>.</p>

<ul>
<li><a href="https://cdn.svgator.com/assets/pub/message-bubble.json">Download the message bubble Lottie animation</a></li>
</ul>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/5-message-bubble-lottie-animation.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/5-message-bubble-lottie-animation.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/5-message-bubble-lottie-animation.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/5-message-bubble-lottie-animation.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/5-message-bubble-lottie-animation.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/5-message-bubble-lottie-animation.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/5-message-bubble-lottie-animation.png"
			
			sizes="100vw"
			alt="A screenshot with the message bubble Lottie animation."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/5-message-bubble-lottie-animation.png'>Large preview</a>)
    </figcaption>
  
</figure>

<ol>
  <li>Navigate to the left sidebar of the app and switch to the Library tab, then click the “+” icon to upload the message bubble asset that you downloaded earlier.</li>
  <li>After it is loaded in the uploads section, simply click on it to add it to your project.<br /><br />All the animated properties of the asset are now present in the timeline, and you can edit them if you want.<br /><br />
  <strong>Note</strong>: <em>Make sure the playhead is at the second “0” before adding the animated asset. When adding an animated asset, it will always start animating from the point where the playhead is placed.</em></li>
  <li>Freely adjust its position and size as you wish.</li>
  <li>With the playhead at the second 0, click on the <strong>Animate</strong> button, then choose <strong>Position</strong>.</li>
</ol>

<p>At this point, you should have the first Position keyframe automatically added at the second 0, and you are ready to start animating.</p>

<h2 id="animate-the-message-bubble">Animate The Message Bubble</h2>

<ol>
  <li>Start by dragging the playhead on the timeline at 0.2 seconds:<br />
<br />













<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/6-animate-message-bubble.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/6-animate-message-bubble.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/6-animate-message-bubble.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/6-animate-message-bubble.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/6-animate-message-bubble.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/6-animate-message-bubble.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/6-animate-message-bubble.png"
			
			sizes="100vw"
			alt="A screenshot showing steps how to animate the message bubble"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/6-animate-message-bubble.png'>Large preview</a>)
    </figcaption>
  
</figure>
  </li>
  <li>Then, drag the message bubble up a few pixels. The second keyframe will appear in the timeline, marking the element’s new position, thus creating the 2 milliseconds animation.<br /><br /><strong>Note</strong>: <em>You can hit Play at any moment to check how everything looks!</em><br /><br />Next, you can use the Scale animator to make the bubble disappear after the dots representing the typing are done animating by scaling it down to 0 for both the X and Y axes:<br /><br />














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/7-animate-message-bubble.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/7-animate-message-bubble.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/7-animate-message-bubble.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/7-animate-message-bubble.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/7-animate-message-bubble.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/7-animate-message-bubble.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/7-animate-message-bubble.png"
			
			sizes="100vw"
			alt="A screenshot showing steps how to animate the message bubble"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/7-animate-message-bubble.png'>Large preview</a>)
    </figcaption>
  
</figure>
  </li>
  <li>With the message bubble still selected, drag the playhead at 2.2 seconds, click on <strong>Animate</strong>, and select Scale (or just press <kbd>Shift</kbd> + <kbd>S</kbd> on the keyboard) to set the first Scale keyframe, then drag the playhead at 2.5 seconds.</li>
  <li>Set the scale properties to 0 for both the X and Y axes (in the right side panel). The bubble won’t be visible anymore at this point.<br /><br /><strong>Note</strong>: <em>To maintain the ratio while changing the scale values, make sure you have the Maintain proportions on (the link icon next to the scale inputs).</em><br /><br />To add an extra touch of interest to this scaling motion, add an easing function preset:<br /><br />














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/8-animate-message-bubble.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/8-animate-message-bubble.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/8-animate-message-bubble.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/8-animate-message-bubble.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/8-animate-message-bubble.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/8-animate-message-bubble.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/8-animate-message-bubble.png"
			
			sizes="100vw"
			alt="A screenshot showing steps how to animate the message bubble"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/8-animate-message-bubble.png'>Large preview</a>)
    </figcaption>
  
</figure>
</li>
  <li>First, jump back to the first Scale keyframe (you can also double-click the keyframe to jump the playhead right at it).</li>
  <li>Open the <strong>Easing Panel</strong> next to the time indicator and scroll down through the presets list, then select <strong>Ease in Back</strong>. Due to its bezier going out of the graph, this easing function will create a bounce-back effect for the scale animation.<br /><br /><strong>Note</strong>: <em>You can adjust the bezier of a selected easing preset and create a new custom function, which will appear at the top of the list.</em><br /><br /><em>Keep in mind that you need at least one keyframe selected if you intend to apply an easing. The easing function will apply from the selected keyframe toward the next keyframe at its right. Of course, you can apply a certain easing for multiple keyframes at once.</em><br /><br />To get a smoother transition when the message bubble disappears, add an Opacity animation of one millisecond at the end of the scaling:<br /><br />














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/9-animate-message-bubble.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/9-animate-message-bubble.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/9-animate-message-bubble.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/9-animate-message-bubble.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/9-animate-message-bubble.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/9-animate-message-bubble.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/9-animate-message-bubble.png"
			
			sizes="100vw"
			alt="A screenshot showing steps how to animate the message bubble"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/9-animate-message-bubble.png'>Large preview</a>)
    </figcaption>
  
</figure>
</li>
  <li>Choose <strong>Opacity</strong> from the animators’ list and set the first keyframe at 2.4 seconds, then drag the playhead at 2.5 seconds to match the ending keyframe from the scale animation above.</li>
  <li>From the <strong>Appearance panel</strong>, drag the <strong>Opacity slider</strong> all the way to the left, at 0%.</li>
</ol>

<h2 id="create-an-email-icon">Create An Email Icon</h2>

<p>For the concept behind this animation to be complete, let’s create (and later animate) a “new email” notification as a response to the character sending that message.</p>

<p>Once again, SVGator’s asset library comes in handy for this step:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/10-create-email-icon-animation.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/10-create-email-icon-animation.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/10-create-email-icon-animation.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/10-create-email-icon-animation.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/10-create-email-icon-animation.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/10-create-email-icon-animation.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/10-create-email-icon-animation.png"
			
			sizes="100vw"
			alt="A screenshot showing steps how to create an email icon."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/10-create-email-icon-animation.png'>Large preview</a>)
    </figcaption>
  
</figure>

<ol>
  <li>Go to the search bar from the <strong>Library</strong> and type in “<strong>mail</strong>,” then click on the mail asset from the results.</li>
  <li>Place it somewhere above the laptop. Edit the mail icon to better fit the style of the animation:<br /><br />














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/11-edit-mail-icon-animation.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/11-edit-mail-icon-animation.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/11-edit-mail-icon-animation.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/11-edit-mail-icon-animation.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/11-edit-mail-icon-animation.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/11-edit-mail-icon-animation.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/11-edit-mail-icon-animation.png"
			
			sizes="100vw"
			alt="A screenshot showing steps how to edit the mail icon."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/11-edit-mail-icon-animation.png'>Large preview</a>)
    </figcaption>
  
</figure>
</li>
  <li>Open the <strong>email</strong> group and select the rectangle from the back.</li>
  <li>Change its fill color to a dark purple.</li>
  <li>Round up the corners using the <strong>Radius</strong> slider.<br /><br />














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/12-edit-mail-icon-animation.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/12-edit-mail-icon-animation.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/12-edit-mail-icon-animation.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/12-edit-mail-icon-animation.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/12-edit-mail-icon-animation.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/12-edit-mail-icon-animation.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/12-edit-mail-icon-animation.png"
			
			sizes="100vw"
			alt="A screenshot showing steps how to edit the mail icon."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/12-edit-mail-icon-animation.png'>Large preview</a>)
    </figcaption>
  
</figure>
</li>
  <li>Make the element’s design minimal by deleting these two lines from the lower part of the envelope.<br /><br />














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/13-edit-mail-icon-animation.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/13-edit-mail-icon-animation.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/13-edit-mail-icon-animation.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/13-edit-mail-icon-animation.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/13-edit-mail-icon-animation.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/13-edit-mail-icon-animation.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/13-edit-mail-icon-animation.png"
			
			sizes="100vw"
			alt="A screenshot steps showing how to edit the mail icon."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/13-edit-mail-icon-animation.png'>Large preview</a>)
    </figcaption>
  
</figure>
</li>
    <li>Select the envelope seal flap, which is the <strong>Polyline</strong> element in the group, above the rectangle.</li>
    <li>Add a lighter purple for the fill, set the stroke to 2 px width, and also make it white.<br /><br />To make the animation even more interesting, create a notification alert in the top-right corner of the envelope:<br /><br />














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/14-edit-mail-icon-animation.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/14-edit-mail-icon-animation.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/14-edit-mail-icon-animation.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/14-edit-mail-icon-animation.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/14-edit-mail-icon-animation.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/14-edit-mail-icon-animation.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/14-edit-mail-icon-animation.png"
			
			sizes="100vw"
			alt="A screenshot showing steps how to edit the mail icon."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/14-edit-mail-icon-animation.png'>Large preview</a>)
    </figcaption>
  
</figure>
</li>
  <li>Use the <strong>Ellipse tool (O)</strong> from the toolbar on top and draw a circle in the top-right corner of the envelope.</li>
  <li>Choose a nice red color for the fill, and set the stroke to white with a 2 px width.<br /><br />














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/15-edit-mail-icon-animation.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/15-edit-mail-icon-animation.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/15-edit-mail-icon-animation.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/15-edit-mail-icon-animation.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/15-edit-mail-icon-animation.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/15-edit-mail-icon-animation.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/15-edit-mail-icon-animation.png"
			
			sizes="100vw"
			alt="A screenshot showing steps how to edit the mail icon."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/15-edit-mail-icon-animation.png'>Large preview</a>)
    </figcaption>
  
</figure>
</li>
  <li>Click on the “T” icon to select the <strong>Text</strong> tool.</li>
  <li>Click on the circle and type “1”.</li>
  <li>Set the color to white and click on the “B” icon to make it bold.<br /><br />














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/16-edit-mail-icon-animation.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/16-edit-mail-icon-animation.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/16-edit-mail-icon-animation.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/16-edit-mail-icon-animation.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/16-edit-mail-icon-animation.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/16-edit-mail-icon-animation.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/16-edit-mail-icon-animation.png"
			
			sizes="100vw"
			alt="A screenshot showing steps how to edit the mail icon."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/16-edit-mail-icon-animation.png'>Large preview</a>)
    </figcaption>
  
</figure>
</li>
  <li>Select both the red circle and the number, and group them: right-click, and hit <strong>Group</strong>.<br /><br /> 
You can also hit <kbd>Command</kbd> or <kbd>Ctrl</kbd> + <kbd>G</kbd> on your keyboard. Double-click on the newly created group to rename it to “Notification.”<br /><br />














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/17-edit-mail-icon-animation.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/17-edit-mail-icon-animation.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/17-edit-mail-icon-animation.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/17-edit-mail-icon-animation.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/17-edit-mail-icon-animation.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/17-edit-mail-icon-animation.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/17-edit-mail-icon-animation.png"
			
			sizes="100vw"
			alt="A screenshot showing steps how to edit the mail icon."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/17-edit-mail-icon-animation.png'>Large preview</a>)
    </figcaption>
  
</figure>
</li>
<li>Select both the notification group and email group below and create a new group, which you can name “new email.”</li>
</ol>

<h2 id="animate-the-new-email-group">Animate The New Email Group</h2>

<p>Let’s animate the new email popping out of the laptop right after the character has finished texting his message:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/18-animate-new-email-group.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/18-animate-new-email-group.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/18-animate-new-email-group.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/18-animate-new-email-group.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/18-animate-new-email-group.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/18-animate-new-email-group.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/18-animate-new-email-group.png"
			
			sizes="100vw"
			alt="A screenshot showing steps how to animate the new email group."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/18-animate-new-email-group.png'>Large preview</a>)
    </figcaption>
  
</figure>

<ol>
  <li>With the “New email” group selected, click twice on the <strong>Move down</strong> icon from the header to place the group last.<br />You can also press <kbd>Command</kbd> or <kbd>Ctrl</kbd> + arrow down on your keyboard.</li>
<li>Drag the group behind the laptop (on the canvas) to hide it entirely, and also scale it down a little.</li>
<li>With the playhead at 3 seconds, add the animators <strong>Scale</strong> and <strong>Position</strong>.<br />You can also do that by pressing <kbd>Shift</kbd> + <kbd>S</kbd> and <kbd>Shift</kbd> + <kbd>P</kbd> on your keyboard.<br /><br />














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/19-animate-new-email-group.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/19-animate-new-email-group.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/19-animate-new-email-group.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/19-animate-new-email-group.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/19-animate-new-email-group.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/19-animate-new-email-group.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/19-animate-new-email-group.png"
			
			sizes="100vw"
			alt="A screenshot showing steps how to animate the new email group."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/19-animate-new-email-group.png'>Large preview</a>)
    </figcaption>
  
</figure>
</li>
<li>Drag the playhead at the second 3.3 on the timeline.</li>
<li>Move the New Email group above the laptop and scale it up a bit.</li>
<li>You can also bend the motion path line to create a curved trajectory for the position animation.<br /><br />














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/20-animate-new-email-group.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/20-animate-new-email-group.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/20-animate-new-email-group.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/20-animate-new-email-group.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/20-animate-new-email-group.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/20-animate-new-email-group.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/20-animate-new-email-group.png"
			
			sizes="100vw"
			alt="A screenshot showing steps how to animate the new email group."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/20-animate-new-email-group.png'>Large preview</a>)
    </figcaption>
  
</figure>
</li>
<li>Select the first keyframes at the second 3.</li>
<li>Open the easing panel.</li>
<li>And click on the <strong>Ease Out Cubic</strong> preset to add it to both keyframes.</li>
</ol>

<h2 id="animate-the-notification">Animate The Notification</h2>

<p>Let’s animate the notification dot separately. We’ll make it pop in while the email group shows up.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/21-animate-notification.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/21-animate-notification.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/21-animate-notification.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/21-animate-notification.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/21-animate-notification.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/21-animate-notification.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/21-animate-notification.png"
			
			sizes="100vw"
			alt="A screenshot showing steps of how to animate the notification."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/21-animate-notification.png'>Large preview</a>)
    </figcaption>
  
</figure>

<ol>
<li>Select the <strong>Notification</strong> group.</li>
<li>Create a scale-up animation for it with 0 for both the X and Y axes at 3.2 and 1 at 3.5 seconds.</li>
<li>Select the first keyframe and, from the easing panel, choose <strong>Ease Out Back</strong>. This easing function will ensure the popping effect.</li>
</ol>

<h2 id="add-expressiveness-to-the-character">Add Expressiveness To The Character</h2>

<p>Make the character smile while looking at the email that just popped out. For this, you need to animate the stroke offset of the mouth:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/22-add-expressiveness-character.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/22-add-expressiveness-character.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/22-add-expressiveness-character.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/22-add-expressiveness-character.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/22-add-expressiveness-character.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/22-add-expressiveness-character.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/22-add-expressiveness-character.png"
			
			sizes="100vw"
			alt="A screenshot showing the steps of how to add expressiveness to the character."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/22-add-expressiveness-character.png'>Large preview</a>)
    </figcaption>
  
</figure>

<ol>
  <li>Select the mouth path. You can use the <strong>Node tool</strong> to select it directly with one click.</li>
  <li>Drag the playhead at 3.5 seconds, which is the moment from where the smile will start.</li>
  <li>Select the last keyframe of the <strong>Stroke offset</strong> animator from the timeline and duplicate it at second 3.5, or you can also use <kbd>Ctrl</kbd> or <kbd>Cmd</kbd> + <kbd>D</kbd> for duplication.<br /><br />














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/23-add-expressiveness-character.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/23-add-expressiveness-character.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/23-add-expressiveness-character.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/23-add-expressiveness-character.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/23-add-expressiveness-character.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/23-add-expressiveness-character.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/23-add-expressiveness-character.png"
			
			sizes="100vw"
			alt="A screenshot showing the steps of how to add expressiveness to the character."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/23-add-expressiveness-character.png'>Large preview</a>)
    </figcaption>
  
</figure>
</li>
<li>Drag the playhead at second 3.9.</li>
<li>Go to the properties panel and set the Offset to 0. The stroke will now fill the path all the way, creating a stroke offset animation of 4 milliseconds.</li>
</ol>

<h2 id="final-edits">Final Edits</h2>

<p>You can still make all kinds of adjustments to your animation before exporting it. In this case, let’s change the color of the initial Lottie animation we used to start this project:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/24-final-edits-color.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/24-final-edits-color.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/24-final-edits-color.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/24-final-edits-color.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/24-final-edits-color.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/24-final-edits-color.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/24-final-edits-color.png"
			
			sizes="100vw"
			alt="A screenshot showing how to change the color of the initial Lottie animation"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/24-final-edits-color.png'>Large preview</a>)
    </figcaption>
  
</figure>

<ol>
<li>Use the <strong>Node tool</strong> to select all the green paths that form the character’s arms and torso.</li>
<li>Change the color as you desire.</li>
</ol>

<h2 id="export-lottie">Export Lottie</h2>

<p>Once you’re done editing, you can export the animation by clicking on the top right <strong>Export</strong> button and selecting the Lottie format. Alternatively, you can press <kbd>Command</kbd> or <kbd>Ctrl</kbd> + <kbd>E</kbd> on your keyboard to jump directly to the export panel, from where you can still select the animation you want to export.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/25-export-lottie.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/25-export-lottie.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/25-export-lottie.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/25-export-lottie.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/25-export-lottie.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/25-export-lottie.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/25-export-lottie.png"
			
			sizes="100vw"
			alt="A screenshot showing how to export the animation."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/creating-custom-lottie-animations-svgator/25-export-lottie.png'>Large preview</a>)
    </figcaption>
  
</figure>

<ol>
<li>Make sure the Lottie format is selected from the dropdown. In the export panel, you can set a name for the file you are about to export, choose the frame rate and animation speed, or set a background color.</li>
<li>You can preview the Lottie animation with a Lottie player.<br />
<strong>Note</strong>: <em>This step is recommended to make sure all animations are supported in the Lottie format by previewing it on a webpage using the Lottie player. The preview in the export panel isn’t an actual Lottie animation.</em></li>
<li>Get back to the export panel and simply click <strong>Export</strong> to download the Lottie JSON.</li>
</ol>

<h2 id="final-thoughts">Final Thoughts</h2>

<p>Now that you’re done with your animation don’t forget that you have plenty of export options available besides Lottie. You can post the same project on social media in video format, export it as an SVG animation for the web, or turn it into a GIF sticker or any other type of visual you can think of. GIF animations can also be used in Figma presentations and prototypes as a high-fidelity preview of the production-ready Lottie file.</p>

<p>We hope you enjoyed this article and that it will inspire you to create amazing Lottie animations in your next project.</p>

<p>Below, you can find a few useful resources to continue your journey with SVG and SVGator:</p>

<ul>
<li><a href="https://www.svgator.com/tutorials?utm_source=smashingmagazine.com&amp;utm_medium=referral&amp;utm_campaign=lottie-article">SVGator tutorials</a><br />
Check out a series of short video tutorials to help you get started with SVGator.</li>
<li><a href="https://www.svgator.com/help?utm_source=smashingmagazine.com&amp;utm_medium=referral&amp;utm_campaign=lottie-article">SVGator Help Center</a><br />
It answers the most common questions about SVGator, its features, and membership plans.</li>
</ul>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(yk, il)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Costa Alexoglou</author><title>Better Context Menus With Safe Triangles</title><link>https://www.smashingmagazine.com/2023/08/better-context-menus-safe-triangles/</link><pubDate>Mon, 21 Aug 2023 08:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2023/08/better-context-menus-safe-triangles/</guid><description>Imagine the situation when you’ve hovered over a menu item that reveals another list of menu items, then tried to hover over that nested menu only to have the entire menu close on you. Is this a UX challenge you’ve struggled with? A well-known concept called the “safe triangle” solves this issue. While it’s been tackled many ways over the years, Costa Alexoglou has what he believes is a relatively straightforward approach using SVG and tracking a user’s mouse position to prevent nested menus from inadvertently closing on a user.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2023/08/better-context-menus-safe-triangles/" />
              <title>Better Context Menus With Safe Triangles</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Better Context Menus With Safe Triangles</h1>
                  
                    
                    <address>Costa Alexoglou</address>
                  
                  <time datetime="2023-08-21T08:00:00&#43;00:00" class="op-published">2023-08-21T08:00:00+00:00</time>
                  <time datetime="2023-08-21T08:00:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                
                

<p>You’ve no doubt wrestled with menus that have nested menus before. I can’t count how many times I’ve hovered over a menu item that reveals another list of menu items, then tried to hover over that nested menu only to have the entire menu close on me.</p>

<p>That’s the setup for what I think is a pretty common issue when making menus &mdash; preventing nested menus from closing inadvertently. It’s not the users’ fault; leaving hover between menu levels is easy. It’s also not exactly the web’s fault; the menu is supposed to close if the pointer leaves the interactive area.</p>


<figure class="video-embed-container">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/855059002"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
		<figcaption>Keeping the nested menu open is more challenging than it looks.</figcaption>
	
</figure>

<p>Before we dig deeper into the issue, let’s acknowledge that relying on hover interactions for displaying and hiding menu items is already somewhat problematic. Not all devices have a mouse, or a pointer, for that matter. You could argue that click (or tap) interactions are better. Take Amazon’s main navigation as an example. It requires a click to open the main menu and another click to open the nested menus.</p>


<figure class="video-embed-container">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/855060398"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
		<figcaption>Amazon employs a click (or tap) to open and close nested menus.</figcaption>
	
</figure>

<p>Looking past a “hover versus tap” debate, the appeal of hover interactions is obvious. It would be nice to save the user extra clicks, right?</p>

<p>That’s what we’re aiming for in this article. The reason I want to tackle this at all is that I saw Notion showing off its new menu hover interactions.</p>

<p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">A li’l quality-of-life update:<br><br>Before, you had to be really precise with your cursor so menus wouldn’t disappear on you. Should feel much more polished now 🫡 <a href="https://t.co/0yTRz3CMce">pic.twitter.com/0yTRz3CMce</a></p>&mdash; Notion (@NotionHQ) <a href="https://twitter.com/NotionHQ/status/1629175696177389569?ref_src=twsrc%5Etfw">February 24, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></p>

<p>While I don’t have inside information about Notion’s approach, I have what I think is a practical way to go about it based on other examples I’ve seen, and I want to show you how I got there.</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h2 id="the-solution-safe-triangles">The Solution: Safe Triangles</h2>

<p>The solution is <strong>safe triangles</strong>. It’s not exactly a new idea, either. Amazon popularized the idea, and <a href="https://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown?ref=height-blog.ghost.io">Ben Kamens blogged it back in 2013</a> while introducing a jQuery plugin to accomplish it.</p>

<p>The basic idea is nicely illustrated in this video that <a href="https://height.app/blog/guide-to-build-context-menus">Michael Villar includes in a post on the Height app blog</a>.</p>


<figure class="video-embed-container">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/855062328"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
		<figcaption>The safe triangle represents the interactive area that allows the nested menu to remain open.</figcaption>
	
</figure>

<p>There has to be a “modern” approach for adding safe triangles to nested menus. I sought out more examples.</p>

<h3 id="example-1-vs-code">Example 1: VS Code</h3>

<p>VS Code pulls off a nice hover interaction in its web app.</p>


<figure class="video-embed-container">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/855063062"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
		<figcaption>VS Code also supports a safe triangle.</figcaption>
	
</figure>

<p>The VS Code approach uses a delayed trigger of a <code>mouseover</code> event callback in JavaScript. But before the event fires, CSS styles are applied to the <code>:hover</code> state of menu items.</p>

<h3 id="macos">macOS</h3>

<p>I’m on a Mac and noticed that macOS also implements some sort of safe triangle in its menus.</p>


<figure class="video-embed-container">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/855064686"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
		<figcaption>A safe triangle in macOS menus cancels the safe area when returning hover from the nested menu to the top-level menu item.</figcaption>
	
</figure>

<p>I cannot crack macOS open and inspect its code, but this is an excellent example. Notice how the safe triangle works when moving from a top-level menu to a nested menu but not when returning from the nested menu to the top-level menu. It’s the sort of subtle, polished difference we might expect from Apple.</p>

<h3 id="radix-navigation-component">Radix Navigation Component</h3>

<p>The open-source Radix library provides a <a href="https://www.radix-ui.com/docs/primitives/components/dropdown-menu">Dropdown Menu component</a> that pulls it off.</p>


<figure class="video-embed-container">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/855065079"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
		<figcaption>The Radix dropdown component implementation of a safe triangle.</figcaption>
	
</figure>

<p>What a great job! I found this example from a talk <a href="https://www.youtube.com/watch?v=pcMYcjtWwVI">Vercel posted to YouTube</a> where Radix co-creator <a href="https://twitter.com/peduarte">Pedro Duarte</a> gives a master class on the design challenges of dropdown menus.</p>

<p>The functionality is pretty much the same as the macOS approach, where <code>:hover</code> is not triggered in the sibling elements, making the experience really smooth. Unlike macOS, though, this is code that I can access and inspect.</p>

<p>The Radix approach was hugely helpful as far as informing my own work. So, let’s break down the process to show you how I implemented this into the main navigation of the project I work on, <a href="https://neo4j.com">Neo4j</a>.</p>


<figure class="video-embed-container">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/856321151"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
		<figcaption>A safe triangle implementation in Neo4j.</figcaption>
	
</figure>

<h2 id="demo">Demo</h2>

<p>I put together a <a href="https://codesandbox.io/s/vmgyfg?file=%2FApp.js&amp;utm_medium=sandpack">simplified version</a> of my implementation that you can try. The code is written in React, but the solution is not React-specific and can be paired with any UI framework you like.</p>


<figure class="video-embed-container">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/855065911"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
		<figcaption>Demonstrating a safe triangle.</figcaption>
	
</figure>

<p>See that? The second menu exposes the “safe triangle” on hover, showing the hoverable area that allows the nested menu to stay open, even after the pointer has left the hover state.</p>

<div class="partners__lead-place"></div>

<h2 id="how-it-works">How it Works</h2>

<p>The two key ingredients for this approach are SVG and the CSS <code>pointer-events</code> property.</p>

<h3 id="mouse-enter-and-leave">Mouse Enter and Leave</h3>

<p>First things first. When hovering over a menu item that includes nested elements, we trigger an <code>onMouseEnter</code> callback that opens the nested menu, with <code>onMouseEnter={() =&gt; setOpen(true)}</code>.</p>

<p>Then, an <code>onMouseLeave</code> callback is responsible for closing the submenu when the pointer leaves the element and its children via <code>onMouseLeave={() =&gt; setOpen(false)}</code>. This part is identical to a simple nested menu, but notice <code>&lt;SafeArea /&gt;</code> because this is where the magic happens.</p>

<pre><code class="language-javascript">const SafeAreaNestedOption = () =&gt; {
  const [open, setOpen] = useState&lt;boolean&gt;(false);
  const parent = useRef&lt;HTMLLIElement&gt;(null);
  const child = useRef&lt;HTMLDivElement&gt;(null);
  const getTop = useCallback(() =&gt; {
    const height = child.current?.offsetHeight;
    return height ? `-${height / 2 - 15}px` : 0;
  }, [child]);

  return (
    &lt;li
      ref={parent}
      style={{ position: "relative" }}
      onMouseEnter={() =&gt; setOpen(true)}
      onMouseLeave={() =&gt; setOpen(false)}
    &gt;
      &lt;NestedPlaceholder /&gt;
      {/&#42; Safe mouse area &#42;/}
      {/&#42; This is where the magic will happen &#42;/}
      {open && parent.current && child.current && (
        &lt;SafeArea anchor={parent.current} submenu={child.current} /&gt;
      )}
      {/&#42; Nested elements as children &#42;/}
      &lt;div
        style={{
          visibility: open ? "visible" : "hidden",
          position: "absolute",
          left: parent.current?.offsetWidth || 0,
          top: getTop()
        }}
        ref={child}
      &gt;
        &lt;ul&gt;
          &lt;li&gt;Nested Option 1&lt;/li&gt;
          &lt;li&gt;Nested Option 2&lt;/li&gt;
          &lt;li&gt;Nested Option 3&lt;/li&gt;
          &lt;li&gt;Nested Option 4&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/div&gt;
    &lt;/li&gt;
  );
};
</code></pre>

<h3 id="svg">SVG</h3>

<p>We use SVG to “draw” the safe triangle inside the <code>SafeArea</code> component. When a nested menu is open, we create the SVG as a child element that isn’t visible but is there. The idea is that users interact with it when it is exposed, even if they don’t realize it.</p>

<p>The trick is to make sure that the SVG is rectangular with a height equal to the height of the nested menu and a width equal to the distance between the cursor and the nested menu.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/better-context-menus-safe-triangles/2-safe-triangle-safe-area.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="383"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/better-context-menus-safe-triangles/2-safe-triangle-safe-area.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/better-context-menus-safe-triangles/2-safe-triangle-safe-area.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/better-context-menus-safe-triangles/2-safe-triangle-safe-area.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/better-context-menus-safe-triangles/2-safe-triangle-safe-area.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/better-context-menus-safe-triangles/2-safe-triangle-safe-area.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/better-context-menus-safe-triangles/2-safe-triangle-safe-area.jpg"
			
			sizes="100vw"
			alt="The SVG element draws a “safe” area that users interact with, even if they cannot see it"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The SVG element draws a “safe” area that users interact with, even if they cannot see it. (<a href='https://files.smashing.media/articles/better-context-menus-safe-triangles/2-safe-triangle-safe-area.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>As long as the pointer is hovering over the SVG element, we have something we can use to maintain the nested menu’s open state.</p>

<h3 id="pointer-events">Pointer Events</h3>

<p>There are two steps we need to take to achieve this. First, we’ll create a “desired” path that connects our cursor to the submenu.</p>

<p>A triangular shape is the most straightforward path we can construct between a menu item and a nested menu. You can visualize what this triangle might look like in the image below. The green represents the safe area, indicating that it won’t trigger any <code>onMouseLeave</code> events. Conversely, the red area signifies that it will start the <code>onMouseLeave</code> event since we’re likely moving toward a sibling menu item.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-pointer-events.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="608"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-pointer-events.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-pointer-events.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-pointer-events.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-pointer-events.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-pointer-events.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-pointer-events.jpg"
			
			sizes="100vw"
			alt="The bounding SVG forms a safe area in a triangular shape"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The bounding SVG forms a safe area in a triangular shape. (<a href='https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-pointer-events.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>I approached this by creating a <code>SafeArea</code> component in React that contains the SVG markup:</p>

<pre><code class="language-html">&lt;svg
  style={{
    position: "fixed",
    width: svgWidth,
    height: submenuHeight,
    pointerEvents: "none",
    zIndex: 2,
    top: submenuY,
    left: mouseX - 2
  }}
  id="svg-safe-area"
&gt;
  {/&#42; Safe Area &#42;/}
  &lt;path
    pointerEvents="auto"
    stroke="red"
    strokeWidth="0.4"
    fill="rgb(114 140 89 / 0.3)"
    d={
      `M 0, ${mouseY-submenuY} 
        L ${svgWidth},${svgHeight}
        L ${svgWidth},0 
        z`
    }
  /&gt;
&lt;/svg&gt;
</code></pre>

<p>Also, to constantly update our safe triangle and position it appropriately, we need a mouse listener, specifically <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event"><code>onmousemove</code></a>. I relied on a <a href="https://www.joshwcomeau.com/snippets/react-hooks/use-mouse-position/">React hook from Josh Comeau</a> called <code>useMousePosition</code> in a <code>useMousePosition.tsx</code> file that provides the safe triangle component, designating the mouse position with <code>mouseX</code> and <code>mouseY</code>.</p>

<h3 id="the-safe-triangle">The Safe Triangle</h3>

<p>The triangle is the SVG’s only <code>path</code> element. For this to work correctly, we must set the CSS <code>pointer-events</code> property to <code>none</code>, which we can do inline directly in the SVG. Then we set <code>pointer-events</code> to <code>auto</code> inline in the <code>path</code> element. This way, we stop propagating events when they are coming from the <code>path</code> element &mdash; the safe triangle &mdash; but not when events come from the SVG’s <code>red</code> area.</p>

<p>Let’s break down the path we are drawing, as it’s way more straightforward than it looks:</p>

<pre><code class="language-html">&lt;path
  pointerEvents="auto"
  stroke="red"
  strokeWidth="0.4"
  fill="rgb(114 140 89 / 0.3)"
  d={
    `M 0, ${mouseY-submenuY} 
      L ${svgWidth},${svgHeight}
      L ${svgWidth},0 
      z`
  }
/&gt;
</code></pre>

<p>We set the <code>pointer-events</code> property to <code>auto</code> to capture all mouse events, and it does not trigger the <code>onMouseLeave</code> event as long as the cursor is inside the path.</p>

<p>Next, we provide the path with some basic CSS styles for debugging purposes. This way, we can see the safe area while testing interactions.</p>

<p>The <code>0, ${mouseY-submenuY}</code> part is the path’s starting point, designating the center of the SVG’s area.</p>

<p>Then we continue our path drawing with two lines: <code>L ${svgWidth},${svgHeight}</code> and <code>L ${svgWidth},0</code>. The former represents the first line (<code>L</code>) based on the SVG’s width and height, while the latter draws the second line (<code>L</code>) based on the SVG’s width.</p>

<p>The <code>z</code> part of the path is what makes everything work. <code>z</code> is what closes the path, making a straight line to the path’s starting point, preventing the need to draw a third line.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-path-points.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="501"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-path-points.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-path-points.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-path-points.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-path-points.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-path-points.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-path-points.jpg"
			
			sizes="100vw"
			alt="Safe triangle path points"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Showing the points of the safe triangle SVG path. (<a href='https://files.smashing.media/articles/better-context-menus-safe-triangles/safe-triangle-path-points.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>You can explore the path in more detail or adjust it using <a href="https://yqnn.github.io/svg-path-editor/#P=M_0_300_L_600_600_L_600_0_z">this SVG path editor</a>.</p>

<div class="partners__lead-place"></div>

<h2 id="there-are-some-gotchas">There Are Some Gotchas</h2>

<p>This is a relatively simple solution on purpose. There are some situations where this approach may be too simple, and you will need another creative solution, particularly if you’re not working in React like me.</p>

<p>For example, what if the user’s pointer moves diagonally and touches a different menu item? This approach does not capture that interaction to prevent the current nested menu from closing, but that might not be what you want it to do. Perhaps you <em>want</em> the nested menu to close and need to adjust the SVG with a different shape. An “easy” way to solve this is to debounce a cleanup function so that, on every mouse movement, you call the cleanup function. And after some number of milliseconds have passed without a mouse movement, you would remove the SVG element, and the sibling listeners would trigger as expected.</p>

<p>Another example is the navigation paths. A triangle is terrific but might not be the ideal shape for your menu and how it is designed. After doing some of my own tests, I’ve found that a curved path tends to be more effective, closer to <a href="https://www.neo4j.design/40a8cff71/p/96952e-context-menu">Needle’s approach</a> for a safe area:</p>

<figure><a href="https://files.smashing.media/articles/better-context-menus-safe-triangles/needle-context-menu-safe-area-paths.gif"><img src="https://files.smashing.media/articles/better-context-menus-safe-triangles/needle-context-menu-safe-area-paths-800.gif" width="800" height="333" alt="Needle’s Context Menu Safe Area paths" /></a><figcaption>Needle’s Context Menu Safe Area paths. (<a href="https://files.smashing.media/articles/better-context-menus-safe-triangles/needle-context-menu-safe-area-paths.gif">Large preview</a>)</figcaption></figure>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>As you now know, coming up with a solution for nested menus that reveal on hover is more of a challenge than it looks on the surface. Whether a hover-based approach and the clicks it saves are worth the additional considerations that make a better user experience versus a click-based approach is totally up to you. If you go with a menu that relies on a mouse hover to reveal a nested menu, you now have a resource that enhances its usability.</p>

<p>What about you? Is this a UX challenge you’ve struggled with? Have you attempted to solve it differently? Is the “safe triangle” concept effective for your particular use case? I’d love to know in the comments!</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(gg, yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Paul Scanlon</author><title>How To Create Dynamic Donut Charts With TailwindCSS And React</title><link>https://www.smashingmagazine.com/2023/03/dynamic-donut-charts-tailwind-css-react/</link><pubDate>Tue, 07 Mar 2023 15:30:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2023/03/dynamic-donut-charts-tailwind-css-react/</guid><description>In this article, Paul Scanlon shares a super lightweight approach to creating a Donut chart using &lt;code>conic-gradient()&lt;/code>. There are no additional libraries to install or maintain, and there’s no heavy JavaScript that needs to be downloaded by the browser in order for them to work. Let’s explore!</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2023/03/dynamic-donut-charts-tailwind-css-react/" />
              <title>How To Create Dynamic Donut Charts With TailwindCSS And React</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>How To Create Dynamic Donut Charts With TailwindCSS And React</h1>
                  
                    
                    <address>Paul Scanlon</address>
                  
                  <time datetime="2023-03-07T15:30:00&#43;00:00" class="op-published">2023-03-07T15:30:00+00:00</time>
                  <time datetime="2023-03-07T15:30:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                
                

<p>CSS is amazing &mdash; I’m regularly surprised at how far it has come in the years I’ve been using it (~2005 &ndash; present). One such surprise came when I noticed this tweet by <a href="https://twitter.com/shrutibalasa/status/1612785019159982080?s=20&amp;t=6TLkMmRjOFQxKP7W-jFPcA">Shruti Balasa</a> which demonstrated how to create a pie chart using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/conic-gradient#gradient_pie-chart"><code>conic-gradient()</code></a>.</p>

<p>It’s fairly straightforward. Here’s a code snippet:</p>

<pre><code class="language-css">div {
  background: conic-gradient(red 36deg, orange 36deg 170deg, yellow 170deg);
  border-radius: 50%;
}
</code></pre>

<p>Using this tiny amount of CSS, you can create gradients that start and stop at specific angles and define a color for each ‘segment’ of the pie chart.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media//articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/1-css-conic-gradient-charts.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media//articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/1-css-conic-gradient-charts.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media//articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/1-css-conic-gradient-charts.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media//articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/1-css-conic-gradient-charts.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media//articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/1-css-conic-gradient-charts.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media//articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/1-css-conic-gradient-charts.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media//articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/1-css-conic-gradient-charts.jpg"
			
			sizes="100vw"
			alt="CSS conic-gradient charts with Donut Charts and a Pie Chart"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media//articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/1-css-conic-gradient-charts.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<h2 id="happy-days">Happy Days!</h2>

<p>Brills, I thought I could use this instead of a charting library for a data dashboard project I’m working on for the new <a href="https://www.cockroachlabs.com/docs/api/cloud/v1.html#get-/api/v1/clusters">CockroachDB Cloud API</a>, but I had a problem. I didn’t know the values for my chart ahead of time, and the values I was receiving from the API weren’t in degrees!</p>

<p>Here’s a preview link and Open-source repo of how I worked around those two problems, and in the rest of this post, I’ll explain how it all works.</p>

<ul>
<li>🚀 Preview: <a href="https://css-conic-gradient-charts.vercel.app/">https://css-conic-gradient-charts.vercel.app/</a></li>
<li>⚙️ Repo: <a href="https://github.com/PaulieScanlon/css-conic-gradient-charts">https://github.com/PaulieScanlon/css-conic-gradient-charts</a></li>
</ul>

<h2 id="dynamic-data-values">Dynamic Data Values</h2>

<p>Here’s some sample data from a <em>typical</em> API response which I’ve sorted by <code>value</code>.</p>

<pre><code class="language-css">const data = [
  {
    name: 'Cluster 1',
    value: 210,
  },
  {
    name: 'Cluster 2',
    value: 30,
  },
  {
    name: 'Cluster 3',
    value: 180,
  },
  {
    name: 'Cluster 4',
    value: 260,
  },
  {
    name: 'Cluster 5',
    value: 60,
  },
].sort((a, b) =&gt; a.value - b.value);
</code></pre>

<p>You can see that each item in the array has a <code>name</code> and a <code>value</code>.</p>

<p>In order to convert the <code>value</code> from a number into a <code>deg</code> value to use with CSS, there are a few things you need to do:</p>

<ul>
<li>Calculate the total amount of all the values.</li>
<li>Use the total amount to calculate the percentage that each value represents.</li>
<li>Convert the percentage into degrees.</li>
</ul>

<p><strong>Note</strong>: <em>The code I’ll be referring to in the steps below can be found in the repo here: <a href="https://github.com/PaulieScanlon/css-conic-gradient-charts/blob/main/components/donut-1.js">/components/donut-1.js</a>.</em></p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h3 id="calculate-the-total-amount">Calculate The Total Amount</h3>

<p>Using JavaScript, you can use this little one-liner to <em>sum</em> up each value from the data array, which results in a single total.</p>

<pre><code class="language-javascript">const total&#95;value = data.reduce((a, b) =&gt; a + b.value, 0);

// =&gt; 740
</code></pre>

<h3 id="calculate-the-percentage">Calculate The Percentage</h3>

<p>Now that you have a <code>total_value</code>, you can convert each of the values from the data array to a percentage using a JavaScript function. I’ve called this function <code>covertToPercent</code>.</p>

<p><strong>Note</strong>: <em>I’ve used the value of 210 from Cluster 1 in this example.</em></p>

<pre><code class="language-javascript">const convertToPercent = (num) =&gt; Math.round((num / total&#95;value) &#42; 100);

// convertToPercent(210) =&gt; 28
</code></pre>

<h3 id="convert-percentage-to-degrees">Convert Percentage to Degrees</h3>

<p>Once you have a percentage, you can convert the percentage into degrees using another JavaScript function. I’ve called this function <code>convertToDegrees</code>.</p>

<pre><code class="language-javascript">const convertToDegrees = (num) =&gt; Math.round((num / 100) &#42; 360);

// convertToDegrees(28) =&gt; 101
</code></pre>

<h3 id="the-result">The Result</h3>

<p>As a temporary test, if I were to <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map">map</a> over the items in the sorted data array, using the two functions explained above, you’d end up with the following output:</p>

<pre><code class="language-javascript">const test_output = data.map((item) =&gt; {
  const percentage = convertToPercent(item.value);
  const degrees = convertToDegrees(percentage);

  return `${degrees}deg`;
});

// =&gt; ['14deg', '29deg', '86deg', '101deg', '126deg']
</code></pre>

<p>The return value of <code>test_output</code> is an array of the <code>value</code> (in degrees) + the string <code>deg</code>.</p>

<p>This solves one of a two-part problem. I’ll now explain the other part of the problem.</p>

<p>To create a Pie chart using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/conic-gradient#gradient_pie-chart"><code>conic-gradient()</code></a>, you need two <code>deg</code> values. The first is the angle from where the gradient should start, and the second is the angle where the gradient should stop. You’ll also need a color for each segment, but I’ll come to that in a moment.</p>

<div class="break-out">

<pre><code class="language-css"> ['red 🤷 14deg', 'blue 🤷 29deg', 'green 🤷 86deg', 'orange 🤷 101deg', 'pink 🤷 126deg']
</code></pre>
</div>

<p>Using the values from the <code>test_output</code>, I only have the end value (where the gradient should stop). The start angle for each segment is actually the end angle from the previous item in the array, and the end angle is the cumulative value of all previous end values plus the current end value. And to make matters worse, the start value for the first angle needs to be manually set to <code>0</code> 🥴.</p>

<p>Here’s a diagram to better explain what that means:</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/3-diagram-pie-chart-conic-gradient.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="500"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/3-diagram-pie-chart-conic-gradient.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/3-diagram-pie-chart-conic-gradient.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/3-diagram-pie-chart-conic-gradient.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/3-diagram-pie-chart-conic-gradient.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/3-diagram-pie-chart-conic-gradient.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/3-diagram-pie-chart-conic-gradient.png"
			
			sizes="100vw"
			alt="A diagram which explains a function"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/3-diagram-pie-chart-conic-gradient.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>If that sounds confusing, it’s because it is, but if you look at the output of a function that can do all this, it might make more sense.</p>

<pre><code class="language-css">"#...", 0, 14,
"#...",, 14, 43,
"#...",, 43, 130,
"#...",, 130, 234,
"#...",, 234, 360,
</code></pre>

<h2 id="the-function-that-can-do-all-this">The Function That Can Do All This</h2>

<p>And here’s the function that can indeed do all of this. It uses <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce"><code>reduce()</code></a> to iterate over the data array, performs the necessary addition to calculate the angles, and returns a new set of numbers that can be used to create the correct start and end angles for use in a Chart.</p>

<div class="break-out">

<pre><code class="language-javascript">const total&#95;value = data.reduce((a, b) =&gt; a + b.value, 0);
const convertToPercent = (num) =&gt; Math.round((num / total&#95;value) &#42; 100);
const convertToDegrees = (num) =&gt; Math.round((num / 100) &#42; 360);

const css&#95;string = data
  .reduce((items, item, index, array) =&gt; {
    items.push(item);

    item.count = item.count || 0;
    item.count += array[index - 1]?.count || item.count;
    item.start&#95;value = array[index - 1]?.count ? array[index - 1].count : 0;
    item.end&#95;value = item.count += item.value;
    item.start&#95;percent = convertToPercent(item.start&#95;value);
    item.end&#95;percent = convertToPercent(item.end&#95;value);
    item.start&#95;degrees = convertToDegrees(item.start&#95;percent);
    item.end&#95;degrees = convertToDegrees(item.end&#95;percent);

    return items;
  }, [])
  .map((chart) =&gt; {
    const { color, start&#95;degrees, end&#95;degrees } = chart;
    return ` ${color} ${start&#95;degrees}deg ${end&#95;degrees}deg`;
  })
  .join();
</code></pre>
</div>

<p>I’ve purposefully left this pretty verbose, so it’s easier to add in <code>console.log()</code>. I found this to be quite helpful when I was developing this function.</p>

<p>You might notice the additional <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"><code>map</code></a> chained to the end of the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce"><code>reduce</code></a>. By using a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"><code>map</code></a> I’m able to modify the returned values and tack on <code>deg</code>, then return them all together as an array of strings.</p>

<p>Using <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join"><code>join</code></a> right at the end converts the array back to a single <code>css_string</code>, which can be used with <code>conic-gradient()</code> 😅.</p>

<pre><code class="language-css">"#..." 0deg 14deg,
"#..." 14deg 43deg,
"#..." 43deg 130deg,
"#..." 130deg 234deg,
"#..." 234deg 360deg
</code></pre>

<div class="partners__lead-place"></div>

<h2 id="using-the-css-string-with-an-svg-foreignobject">Using The <code>css_string</code> With An SVG <code>foreignObject</code></h2>

<p>Now, unfortunately, you can’t use <code>conic-gradient()</code> with <a href="https://developer.mozilla.org/en-US/docs/Web/SVG">SVG</a>. But you can wrap an HTML element inside a <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/foreignObject"><code>foreignObject</code></a> and style the <code>background</code> using a <code>conic-gradient()</code>.</p>

<div class="break-out">

<pre><code class="language-html">&lt;svg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg' style={{ borderRadius: '100%' }}&gt;
  &lt;foreignObject x='0' y='0' width='100' height='100'&gt;
    &lt;div
      xmlns='http://www.w3.org/1999/xhtml'
      style={{
        width: '100%',
        height: '100%',
        background: `conic-gradient(${css_string})`, // &lt;- 🥳
      }}
    /&gt;
  &lt;/foreignObject&gt;
&lt;/svg&gt;
</code></pre>
</div>

<p>Using the above, you should be looking at a Pie chart. In order to make a Donut chart, I’ll need to explain how to make the hole.</p>

<h2 id="let-s-talk-about-the-hole">Let’s Talk About the Hole</h2>

<p>There’s only really one way you can ‘mask’ off the middle of the Pie chart to reveal the background. This approach involves using a <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/clipPath"><code>clipPath</code></a>. This approach looks like the below code snippet. I’ve used this for Donut 1.</p>

<p><strong>Note</strong>: <em>The <code>src</code> for Donut 1 can be seen here: <a href="https://github.com/PaulieScanlon/css-conic-gradient-charts/blob/main/components/donut-1.js#L44">components/donut-1.js</a>.</em></p>

<div class="break-out">

<pre><code class="language-html">&lt;svg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg' style={{ borderRadius: '100%' }}&gt;

  &lt;clipPath id='hole'&gt;
    &lt;path d='M 50 0 a 50 50 0 0 1 0 100 50 50 0 0 1 0 -100 v 18 a 2 2 0 0 0 0 64 2 2 0 0 0 0 -64' /&gt;
  &lt;/clipPath&gt;

  &lt;foreignObject x='0' y='0' width='100' height='100' clipPath='url(#hole)'&gt;
    &lt;div
      xmlns='http://www.w3.org/1999/xhtml'
      style={{
        width: '100%',
        height: '100%',
        background: `conic-gradient(${css_string})`
      }}
    /&gt;
  &lt;/foreignObject&gt;
&lt;/svg&gt;
</code></pre>
</div>

<p>However, there is another way. This approach involves using a <code>&lt;circle /&gt;</code> element and placing it in the center of the pie chart. This will work if the fill of the <code>&lt;circle /&gt;</code> matches the background color of whatever the chart is placed on. In my example, I’ve used a pattern background, and you’ll notice if you look closely at Donut 3 that you can’t see the <a href="https://heropatterns.com/">bubble pattern</a> through the center of the chart.</p>

<p><strong>Note</strong>: <em>The <code>src</code> for Donut 3 can be seen here: <a href="https://github.com/PaulieScanlon/css-conic-gradient-charts/blob/main/components/donut-3.js#L44">components/donut-3.js</a>.</em></p>

<div class="break-out">

<pre><code class="language-html">&lt;svg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg' style={{ borderRadius: '100%' }}&gt;
  &lt;foreignObject x='0' y='0' width='100' height='100'&gt;
    &lt;div
      xmlns='http://www.w3.org/1999/xhtml'
      style={{
        width: '100%',
        height: '100%',
        background: `conic-gradient(${css&#95;string})`
      }}
    /&gt;
  &lt;/foreignObject&gt;
  &lt;circle cx='50' cy='50' r='32' fill='white' /&gt;
&lt;/svg&gt;
</code></pre>
</div>

<p>IMO the <code>clipPath</code> approach is nicer, but it can be more difficult to amend the path points to get the desired thickness of the hole if you don’t have access to something like Figma or Illustrator.</p>

<h2 id="finally-colors">Finally, Colors!</h2>

<p>Colors for charts are something that always cause me problems. Most of the time, the colors I use are defined in CSS, and all this stuff is happening in JavaScript, so how do you use CSS variables in JavaScript?</p>

<p>In my example site, I’m using <a href="https://tailwindcss.com/">Tailwind</a> to style ‘all the things’ and by using <a href="https://gist.github.com/Merott/d2a19b32db07565e94f10d13d11a8574">this trick</a>, I’m able to expose the CSS variables so they can be referred to by their name.</p>

<p>If you want to do the same, you could add a <code>color</code> key to the data array:</p>

<pre><code class="language-css">data={[
  {
    name: 'Cluster 1',
    value: 210,
    color: 'var(--color-fuchsia-400)',
  },
  {
    name: 'Cluster 2',
    value: 30,
    color: 'var(--color-fuchsia-100)',
  },
  {
    name: 'Cluster 3',
    value: 180,
    color: 'var(--color-fuchsia-300)',
  },
  {
    name: 'Cluster 4',
    value: 260,
    color: 'var(--color-fuchsia-500)',
  },
  {
    name: 'Cluster 5',
    value: 60,
    color: 'var(--color-fuchsia-200)',
  },
].sort((a, b) =&gt; a.value - b.value)
</code></pre>

<p>And then reference the <code>color</code> key in the array <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"><code>map</code></a> to return it as part of the <code>css_string</code>. I’ve used this approach in Donut 2.</p>

<p><strong>Note</strong>: <em>You can see the <code>src</code> for Donut 2 here: <a href="https://github.com/PaulieScanlon/css-conic-gradient-charts/blob/main/components/donut-2.js#L25">components/donut-2.js</a>.</em></p>

<pre><code class="language-javascript">.map((chart) =&gt; {
  const { color, start&#95;degrees, end&#95;degrees } = chart;
  return ` ${color} ${start&#95;degrees}deg ${end&#95;degrees}deg`;
})
.join();
</code></pre>

<p>You could even dynamically create the color name using a hard-coded value (<code>color-pink-</code>) + the <code>index</code> from the array. I’ve used this approach in Donut 1.</p>

<p><strong>Note</strong>: <em>You can see the <code>src</code> for Donut 1 here: <a href="https://github.com/PaulieScanlon/css-conic-gradient-charts/blob/main/components/donut-1.js#L26">components/donut-1.js</a>.</em></p>

<div class="break-out">

<pre><code class="language-javascript">.map((chart, index) =&gt; {
  const { start&#95;degrees, end&#95;degrees } = chart;
  return ` var(--color-pink-${(index + 1) &#42; 100}) ${start&#95;degrees}deg ${end&#95;degrees}deg`;
})
.join();
</code></pre>
</div>

<div class="partners__lead-place"></div>

<h2 id="if-you-re-lucky">If You’re Lucky!</h2>

<p>However, you might get lucky and be working with an API that actually returns values with an associated color. This is the case with the <a href="https://docs.github.com/en/graphql">GitHub GraphQL API</a>. So. I popped together one last example.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/2-ccs-conic-gradient-charts.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="500"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/2-ccs-conic-gradient-charts.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/2-ccs-conic-gradient-charts.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/2-ccs-conic-gradient-charts.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/2-ccs-conic-gradient-charts.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/2-ccs-conic-gradient-charts.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/2-ccs-conic-gradient-charts.png"
			
			sizes="100vw"
			alt="GitHub GraphQL API with Github chart with ten different languages associated with its own color"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/dynamic-donut-charts-css-conic-gradient-tailwindcss-react/2-ccs-conic-gradient-charts.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>You can see this working in your browser by visiting <a href="https://css-conic-gradient-charts.vercel.app/github">/github</a>, and the <code>src</code> for both the GitHub Donut Chart and Legend can be found here:</p>

<ul>
<li><a href="https://github.com/PaulieScanlon/css-conic-gradient-charts/blob/main/components/github-chart.js">components/github-chart.js</a>;</li>
<li><a href="https://github.com/PaulieScanlon/css-conic-gradient-charts/blob/main/components/github-legend.js">components/github-legend.js</a>.</li>
</ul>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>You might be thinking this is quite complicated, and it’s probably easier to use a Charting Library, and you’re probably right. It probably is. But this way is <strong>super lightweight</strong>. There are no additional libraries to install or maintain, and there’s no heavy JavaScript that needs to be downloaded by the browser in order for them to work.</p>

<p>I experimented once before with creating Donut Charts using an SVG and the <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray"><code>stroke-dashoffset</code></a>. You can read about that in my article, “<a href="https://paulie.dev/posts/2021/01/react-svg-doughnut-chart/">Create an SVG Doughnut Chart From Scratch For Your Gatsby Blog</a>.” That approach worked really well, but I think I prefer the approach described in this post. CSS is simply the best!</p>

<p>If you’d like to discuss any of the methods I’ve used here, please come find me on Twitter: <a href="https://twitter.com/PaulieScanlon">@PaulieScanlon</a>.</p>

<p>See you around the internet!</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(yk, il)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Paul Scanlon</author><title>Putting Gears In Motion: Animating Cars With HTML And SVG</title><link>https://www.smashingmagazine.com/2023/02/putting-gears-motion-animating-cars-with-html-svg/</link><pubDate>Thu, 16 Feb 2023 13:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2023/02/putting-gears-motion-animating-cars-with-html-svg/</guid><description>SVG &lt;code>&amp;lt;animateMotion&amp;gt;&lt;/code> provides a way to define how an element moves along a motion path. In this article, Paul Scanlon shares an idea of how to use it by animating race cars in an infinite loop as easy as one-two-three!</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2023/02/putting-gears-motion-animating-cars-with-html-svg/" />
              <title>Putting Gears In Motion: Animating Cars With HTML And SVG</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Putting Gears In Motion: Animating Cars With HTML And SVG</h1>
                  
                    
                    <address>Paul Scanlon</address>
                  
                  <time datetime="2023-02-16T13:00:00&#43;00:00" class="op-published">2023-02-16T13:00:00+00:00</time>
                  <time datetime="2023-02-16T13:00:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                
                

<p>Hello! And if you like HTML, you’ve come to the right place!</p>

<p>I love HTML. As an old-school front-end developer, I think it’s a hugely underrated skill. I’ve been writing HTML since ~2005, and today the browser alone can almost do all the things Flash could do nearly two decades ago!</p>

<p>One such <em>trick</em> HTML now has is called <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animateMotion"><code>&lt;animateMotion&gt;</code></a> &mdash; those familiar with Flash will remember this as <em>The Motion Guide</em>. I found this video from 14 years ago, but the method existed for a while before that:</p>


<figure class="video-embed-container">
  <div
  
  class="video-embed-container--wrapper">
		<lite-youtube
			videoid="owojvNuuOxg"
      
			videotitle="Flash Tutorial - 5 - Motion Guide Layers (&lt;a href=&#39;https://www.youtube.com/watch?v=owojvNuuOxg&#39;&gt;Watch on YouTube&lt;/a&gt;)"
		></lite-youtube>
	</div>
	
		<figcaption>Flash Tutorial - 5 - Motion Guide Layers (<a href='https://www.youtube.com/watch?v=owojvNuuOxg'>Watch on YouTube</a>)</figcaption>
	
</figure>

<p>The idea is, you create a path for elements to follow… and that’s it!</p>

<p>Here’s an example of what you can do with <code>&lt;animateMotion&gt;</code>:</p>

<ul>
<li>🚀 <a href="https://animate-motion-race-cars.vercel.app/">Live Preview</a></li>
<li>⚙️ <a href="https://github.com/PaulieScanlon/animate-motion-race-cars">Repository</a></li>
</ul>

<p>If you take a look at the <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animateMotion#example">MDN Docs</a>, you’ll see a simple example of a red circle following a path on an infinite loop. The race cars in the live preview follow the same simple rules, and it works just like this!</p>

<figure><a href="https://animate-motion-race-cars.vercel.app/"><img src="https://smashing-files.ams3.digitaloceanspaces.com/articles/animate-race-cars-using-html/race-cars-animation.gif" width="600" height="374" alt="Three animated cars in blue, green and pink following dashed lines" /></a><figcaption>A simple example of what can be achieved using <code>animateMotion</code>. (<a href='https://animate-motion-race-cars.vercel.app/'>See animation</a>)</figcaption></figure>

<h2 id="svg-using-animatemotion">SVG Using <code>animateMotion</code></h2>

<p>Here’s a <a href="https://animate-motion-race-cars.vercel.app/simple-version.html">simplified version</a> which I&rsquo;ll use to explain some of the finer details.</p>

<p><strong>Note</strong>: <em>I’ve removed some of the path values for brevity, but you can see  <code>src</code> for the below snippet at <a href="https://github.com/PaulieScanlon/animate-motion-race-cars/blob/main/simple-version.html">simple-version.html</a>.)</em></p>

<div class="break-out">

<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;Simple Example&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;main&gt;
      &lt;svg viewBox="0 0 307 184" xmlns="http://www.w3.org/2000/svg"&gt;
        &lt;g id="track"&gt;
          &lt;g id="track-lines"&gt;
            &lt;path fill="none" stroke="#facc15" d="M167.88,111.3..." /&gt;
          &lt;/g&gt;

          &lt;g id="pink-car"&gt;
            &lt;animateMotion dur="4s" repeatCount="indefinite" rotate="auto" path="M167.88,111.3..." /&gt;
            &lt;path fill="#EC4899" d="M13.71,18.65c0.25-0.5..." /&gt;
          &lt;/g&gt;
        &lt;/g&gt;
      &lt;/svg&gt;
    &lt;/main&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
</div>

<p>The first thing to look at is the <code>&lt;g&gt;</code> element with the <code>id</code> of <code>track-lines</code>. This is the yellow dashed line that represents the path the car will follow.</p>

<p>You’ll also see another <code>&lt;g&gt;</code> element with the <code>id</code> of <code>pink-car</code>. Within this group is the <code>&lt;animateMotion&gt;</code> element. It has an attribute of <code>path</code>. The numbers used to form this path are the same as the numbers that form the <code>track-lines</code>. An <code>&lt;animateMotion&gt;</code> element is invisible, and its only purpose is to provide a path for an element to follow.</p>

<p>Speaking of which, below the <code>&lt;animateMotion&gt;</code> element is another <code>&lt;path&gt;</code> element, this is the pink car, and it will follow the path of its nearest neighbor.</p>

<h2 id="animatemotion-attributes"><code>animateMotion</code> Attributes</h2>

<p>There’s some additional attributes that the <code>&lt;animateMotion&gt;</code> element accepts; these are as follows:</p>

<ul>
<li><code>dur</code>: The duration of the animation.</li>
<li><code>repeatCount</code>: The number of times the animation should loop.</li>
<li><code>rotate</code>: This can be considered as an orientation to the path. It will ensure the element that’s animating around the path always faces the direction of travel.</li>
<li><code>path</code>: As explained, this is the actual path an element will follow.<br /></li>
</ul>

<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animateMotion#example">MDN Docs</a> show the <code>&lt;animateMotion&gt;</code> element as a child of an Svg <code>&lt;circle&gt;</code> shape e.g:</p>

<pre><code class="language-html">&lt;circle r="5" fill="red"&gt;
  &lt;animateMotion
    dur="10s"
    repeatCount="indefinite"
    path="M20,50 C20,-50 180,150 180,50 C180-50 20,150 20,50 z" /&gt;
&lt;/circle&gt;
</code></pre>

<p>Whilst this approach works for shapes, it will only work if the element can accept a child. The SVG path element can’t, so wrapping everything in the <code>&lt;g&gt;</code> element allows HTML to work out where the coordinate system should start and which elements should follow the path. Sneaky ay!</p>

<p>And that’s it. I designed the track and the other elements seen on the <a href="https://animate-motion-race-cars.vercel.app/">preview link</a> in Adobe Illustrator and exported the whole thing as an SVG. I then did a little bit of manual refactoring to ensure the cars were adjacent to an <code>&lt;animateMotion&gt;</code> element. Et voilà! A race track!</p>

<div class="partners__lead-place"></div>

<h2 id="accessibility">Accessibility</h2>

<p>One small snag, the <code>&lt;animateMotion&gt;</code> element doesn’t natively observe <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion">prefers-reduce-motion</a>. To work around this in the preview I&rsquo;ve added a <a href="https://github.com/PaulieScanlon/animate-motion-race-cars/blob/main/index.html#L17">media query</a> that sets any element with the class name of <code>car</code> to <code>display: none;</code>. Not ideal, but it is at least motion safe!</p>

<p>I hope you’ve enjoyed this post, and if you have any questions, please come and find me on Twitter. <a href="https://twitter.com/PaulieScanlon">@PaulieScanlon</a>, oh and if you’re a better illustrator than I am, please, feel free to re-design the race track and cars, and I’ll be happy to convert it into code!</p>

<p>See you around the internet!</p>

<h3 id="further-reading-on-smashingmag">Further Reading On SmashingMag</h3>

<ul>
<li>“<a href="https://www.smashingmagazine.com/2021/10/real-time-multi-user-game/">How To Build A Real-Time Multi-User Game From Scratch</a>,” Martin Grubinger</li>
<li>“<a href="https://www.smashingmagazine.com/2023/01/svg-customization-animation-practical-guide/">Easy SVG Customization And Animation: A Practical Guide</a>,” Adrian Bece</li>
<li>“<a href="https://www.smashingmagazine.com/2021/10/composable-css-animation-vue-animxyz/">Composable CSS Animation In Vue With AnimXYZ</a>,” Ejiro Asiuwhu</li>
<li>“<a href="https://www.smashingmagazine.com/2022/11/guide-keyboard-accessibility-html-css-part1/">A Guide To Keyboard Accessibility: HTML And CSS (Part 1)</a>,” Cristian Díaz</li>
</ul>

<div class="partners__lead-place"></div>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(yk, il)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Adrian Bece</author><title>Easy SVG Customization And Animation: A Practical Guide</title><link>https://www.smashingmagazine.com/2023/01/svg-customization-animation-practical-guide/</link><pubDate>Mon, 30 Jan 2023 14:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2023/01/svg-customization-animation-practical-guide/</guid><description>Developers often feel discouraged from editing SVG markup and experimenting with SVG animations, thinking it’s a significant time investment or they need to use a complex animation library to do so. In this article, Adrian showcases his favorite tricks, which make the process streamlined and fun.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2023/01/svg-customization-animation-practical-guide/" />
              <title>Easy SVG Customization And Animation: A Practical Guide</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Easy SVG Customization And Animation: A Practical Guide</h1>
                  
                    
                    <address>Adrian Bece</address>
                  
                  <time datetime="2023-01-30T14:00:00&#43;00:00" class="op-published">2023-01-30T14:00:00+00:00</time>
                  <time datetime="2023-01-30T14:00:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                
                

<p>Scalable Vector Graphics (SVG) have been a staple in Web Development for quite some time, and for a good reason. They can be scaled up or down without loss of quality due to their vector properties. They can be compressed and optimized due to the XML format. They can also be easily edited, styled, animated, and changed programmatically.</p>

<p>At the end of the day, SVG is a <strong>markup language</strong>. And just as we can use CSS and JavaScript to enhance our HTML, we can use them the same on SVGs. We could add <strong>character and flourishes</strong> to our graphic elements, add <strong>interactions</strong>, and shape truly delightful and memorable user experiences. This optional but crucial detail is often overlooked when building projects, so <strong>SVGs end up somewhat underutilized</strong> beyond their basic graphical use cases.</p>

<p>How can we even <strong>utilize SVGs</strong> beyond just using them statically in our projects?</p>

<p>Take the <a href="https://2021.stateofcss.com/en-US/">“The State of CSS 2021”</a> landing page, for example. This SVG Logo has been beautifully designed and animated by <a href="https://chriskirknielsen.com/">Christopher Kirk-Nielsen</a>. Although this logo would have looked alright just as a static image, it wouldn’t have had as much of an impact and drawn attention without this intricate animation.</p>

<p>Let’s go even further &mdash; SVG, HTML, CSS, and JavaScript can be combined and used to create <strong>delightful, interactive, and stunning projects</strong>. Check out <a href="https://twitter.com/sarah_edo">Sarah Drasner’s</a> incredible work. She has also written <a href="https://www.oreilly.com/library/view/svg-animations/9781491939697/">a book</a> and has a <a href="https://frontendmasters.com/courses/svg-essentials-animation/">video course</a> on the topic.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="ExpENmo"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Vue Time Comparison [forked]](https://codepen.io/smashingmag/pen/ExpENmo) by <a href="https://codepen.io/sdras">Sarah Drasner</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/ExpENmo">Vue Time Comparison [forked]</a> by <a href="https://codepen.io/sdras">Sarah Drasner</a>.</figcaption>
</figure>

<p><strong>Developers often feel discouraged</strong> to play around with SVG animations, either because of time constraints, or they believe that they need to master design, SVG markup, and one of many complex JavaScript-based animation libraries, or because of some other reason.</p>

<p>Even if you are completely unfamiliar with SVG markup, this quick <a href="https://www.youtube.com/watch?v=emFMHH2Bfvo">3-minute intro</a> by Fireship is more than enough to get you up to speed.</p>

<p>Many impressive JavaScript-based animation libraries can be used to create stunning and complex SVG animations but <strong>in this article, we’ll stick to basics</strong> and showcase how few additional lines of CSS and JavaScript can make all the difference. Let’s make these seemingly advanced and often overlooked concepts accessible, so you can <strong>create delightful and meaningful SVG animations and interactions</strong> on the fly.</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="/printed-books/touch-design-for-mobile-interfaces/">Touch Design for Mobile Interfaces</a></strong>, Steven Hoober’s brand-new guide on <strong>designing for mobile</strong> with proven, universal, human-centric guidelines. <strong>400 pages</strong>, jam-packed with in-depth user research and <strong>best practices</strong>.</p>
<a data-instant href="https://www.smashingmagazine.com/printed-books/touch-design-for-mobile-interfaces/" class="btn btn--green btn--large" style="">Jump to table of contents&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="https://www.smashingmagazine.com/printed-books/touch-design-for-mobile-interfaces/" class="feature-panel-image-link">
<div class="feature-panel-image"><picture><source type="image/avif" srcSet="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/14bcab88-b622-47f6-a51d-76b0aa003597/touch-design-book-shop-opt.avif" />
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b14658fc-bb2d-41a6-8d1a-70eaaf1b8ec8/touch-design-book-shop-opt.png"
    alt="Feature Panel"
    width="480"
    height="697"
/>
</picture>
</div>
</a>
</div>
</aside>
</div>

<h2 id="svg-workflow-tips">SVG Workflow Tips</h2>

<p>Before we dive into SVG animations, let’s go over some essential SVG tips and tricks. These should help you get a better grasp of SVG styling and ease you into the concepts of editing SVG markup, which we’ll rely on in later examples.</p>

<h3 id="utilizing-css-currentcolor-for-svg-icons">Utilizing CSS <code>currentColor</code> For SVG Icons</h3>

<p>Let’s start with a simple example. We have a monochromatic SVG of the star icon that we want to use in our button component.</p>

<div class="break-out">

<pre><code class="language-html">&lt;svg width="24" height="24" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg"&gt;
  &lt;path d="..." fill="#C2CCDE" /&gt;
&lt;/svg&gt;
</code></pre>
</div>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/01-star-icon.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="333"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/01-star-icon.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/01-star-icon.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/01-star-icon.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/01-star-icon.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/01-star-icon.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/01-star-icon.png"
			
			sizes="100vw"
			alt="A star icon"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Here’s our wonderful star icon. (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/01-star-icon.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Let’s add it to our HTML and create a simple button component.</p>

<div class="break-out">

<pre><code class="language-html">&lt;button type="button"&gt;
  &lt;svg width="24" height="24" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"&gt;&lt;path d="..." fill="#C2CCDE" /&gt;&lt;/svg&gt;
  Add to favorites
&lt;/button&gt;
</code></pre>
</div>

<p>Our button already has some background and text color styles applied to it so let’s see what happens when we add our SVG star icon to it.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/02-button-star-icon.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="272"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/02-button-star-icon.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/02-button-star-icon.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/02-button-star-icon.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/02-button-star-icon.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/02-button-star-icon.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/02-button-star-icon.png"
			
			sizes="100vw"
			alt="A button with a star icon in light gray color"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Our star icon fits nicely in the button, but its color is not right. (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/02-button-star-icon.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Our SVG icon has a <code>fill</code> property applied to it, more specifically, a <code>fill=&quot;#C2CCDE&quot;</code> in SVG’s <code>path</code> element. This icon could have come from the SVG library or even exported from a design file, so it makes sense for a color to be exported alongside other graphical properties.</p>

<p>SVG elements can be targeted by CSS like any HTML element, so developers usually reach for the CSS and override the <code>fill</code> color.</p>

<pre><code class="language-css">.button svg &#42; {
  fill: var(--color-text);
}
</code></pre>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/03-button-color-star-icon.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="156"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/03-button-color-star-icon.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/03-button-color-star-icon.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/03-button-color-star-icon.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/03-button-color-star-icon.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/03-button-color-star-icon.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/03-button-color-star-icon.png"
			
			sizes="100vw"
			alt="A button with a star icon in dark gray color"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      We’ve successfully applied color to our SVG icon. (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/03-button-color-star-icon.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>However, this is not an ideal solution as this is a greedy selector, and overriding the <code>fill</code> attribute on all elements can have unintended consequences, depending on the SVG markup. Also, <code>fill</code> is not the only property that affects the element’s color.</p>

<p>Let’s showcase this downside by creating a new button and adding a Google logo icon. SVG markup is a bit more complex than our star icon, as it has multiple <code>path</code> elements. SVG elements don’t have to be all visible, there are cases when we want to use them in different ways (as a clipping region, for example), but we won’t go into that. Just keep in mind that greedy selectors that target SVG elements and override their <code>fill</code>  properties can produce unexpected results.</p>

<div class="break-out">

<pre><code class="language-html"> &lt;svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"&gt;
  &lt;path d="..." fill="#4285F4" /&gt;
  &lt;path d="..." fill="#34A853" /&gt;
  &lt;path d="..." fill="#FBBC05" /&gt;
  &lt;path d="..." fill="#EA4335" /&gt;
  &lt;path d="..." fill="none" /&gt;
 &lt;/svg&gt;
</code></pre>
</div>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/04-button-visible-svg-element.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="175"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/04-button-visible-svg-element.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/04-button-visible-svg-element.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/04-button-visible-svg-element.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/04-button-visible-svg-element.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/04-button-visible-svg-element.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/04-button-visible-svg-element.png"
			
			sizes="100vw"
			alt="A button with a visible SVG element and a broken icon"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Some SVG element was not visible until we override the fill color property. Now the icon doesn’t look right at all! (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/04-button-visible-svg-element.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>We can look at the issue from a different perspective. Instead of looking for a silver bullet CSS solution, we can simply edit our SVG. We already know that the <code>fill</code> property affects the SVG element’s color so let’s see what we can do to make our icons more customizable.</p>

<p>Let’s use a very underutilized CSS value: <code>currentColor</code>. I’ve talked about this awesome value in one of <a href="https://www.smashingmagazine.com/2022/05/lesser-known-underused-css-features-2022/?ref=sidebar#currentcolor">my previous articles</a>.</p>

<blockquote>Often referred to as “the first CSS variable,” <code>currentColor</code> is a value equal to the element’s <code>color</code> property. It can be used to assign a value equal to the value of the <code>color</code> property to any CSS property which accepts a color value. It forces a CSS property to inherit the value of the <code>color</code> property.</blockquote>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="MWQjEKN"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [currentColor svg](https://codepen.io/smashingmag/pen/MWQjEKN) by <a href="https://codepen.io/smashingmag">Smashing Magazine</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/MWQjEKN">currentColor svg</a> by <a href="https://codepen.io/smashingmag">Smashing Magazine</a>.</figcaption>
</figure>

<p>We can simply use the CSS <code>color</code> property to change SVG’s <code>fill</code> property simply by assigning it the <code>currentColor</code> value. This comes in very handy when SVG fill color needs to match text color and respond to various states, like hover and focus.</p>

<pre><code class="language-html">&lt;!-- BEFORE --&gt;
&lt;path d="..." fill="#C2CCDE" /&gt;

&lt;!-- AFTER--&gt;
&lt;path d="..." fill="currentColor" /&gt;
</code></pre>

<p>We can do the same for our Google logo. We need to make sure to avoid replacing the <code>fill=&quot;none&quot;</code> value as this element shouldn’t be visible.</p>

<pre><code class="language-html">&lt;path d="..." fill="currentColor" /&gt;
&lt;path d="..." fill="currentColor" /&gt;
&lt;path d="..." fill="currentColor" /&gt;
&lt;path d="..." fill="currentColor" /&gt;
&lt;path d="..." fill="none" /&gt;
</code></pre>

<p>And we can safely remove our <code>.button svg *</code> selector as our SVGs will respond to the CSS <code>color</code> property value. Check out the following demo to see <code>currentColor</code>  in action, and feel free to play around and experiment with it.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="JjBLbzw"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG button example [forked]](https://codepen.io/smashingmag/pen/JjBLbzw) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/JjBLbzw">SVG button example [forked]</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<p>By avoiding the override, we retain complete control over the icon styling. For example, if we needed to keep the original Google logo colors in the icon, we simply wouldn’t edit the SVG. If we decided to stick with the initial idea of overriding styles, we’d have to override the override to put the fill colors back or write a separate selector for that button, resulting in code duplication or unnecessarily increasing specificity.</p>

<p>If you are using Figma to create or export SVG icons, consider using a plugin to automatically replace all visible color values in an SVG with the <code>currentColor</code> value. Here are a few Figma plugins that support this, alongside some other neat tricks for optimizing SVGs for the Web:</p>

<ul>
<li><a href="https://www.figma.com/community/plugin/814345141907543603/SVG-Export">SVG Export</a>,</li>
<li><a href="https://www.figma.com/community/plugin/1020972132670952724">One Click SVG</a>.</li>
</ul>

<h3 id="svg-optimization">SVG Optimization</h3>

<p>SVG elements can be optimized just like any other markup language &mdash; we can minify it and remove unnecessary formatting and metadata, we can remove unnecessary properties and invisible elements, and so on.</p>

<p>These optimizations can be done automatically, depending on your tech stack. <a href="https://github.com/svg/svgo">SVGO</a> is the most popular tool for optimizing SVGs and can be <a href="https://github.com/svg/svgo#other-ways-to-use-svgo">easily integrated</a> with pretty much any tech stack. If you are looking for a handy web tool for quick manual optimization, <a href="https://twitter.com/jaffathecake">Jake Archibald’s</a> <a href="https://jakearchibald.github.io/svgomg/">SVGOMG</a> has been a go-to choice for many developers, including myself.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/05-SVGOMG-tool.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="486"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/05-SVGOMG-tool.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/05-SVGOMG-tool.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/05-SVGOMG-tool.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/05-SVGOMG-tool.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/05-SVGOMG-tool.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/05-SVGOMG-tool.png"
			
			sizes="100vw"
			alt="SVGOMG tool"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Jake Archibald’s SVGOMG makes SVG optimization a breeze. (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/05-SVGOMG-tool.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>If you are looking for more, CSS-Tricks keeps a <a href="https://css-tricks.com/tools-for-optimizing-svg/">comprehensive list</a> of various SVG optimization tools with plenty of information and articles on the topic.</p>

<h3 id="using-svgs-with-popular-javascript-based-frameworks">Using SVGs With Popular JavaScript-Based Frameworks</h3>

<p>Many popular JavaScript frameworks like React have fully integrated SVG in their toolchains to make the developer experience easier. In React, this could be as simple as importing the SVG as a component, and the toolkit would do all the heavy lifting optimizing it.</p>

<pre><code class="language-javascript">import React from 'react';
import {ReactComponent as ReactLogo} from './logo.svg';

const App = () =&gt; {
  return (
    &lt;div className="App"&gt;
      &lt;ReactLogo /&gt;
    &lt;/div&gt;
  );
}
export default App;
</code></pre>

<p>However, as <a href="https://mobile.twitter.com/_developit/status/1382838799420514317">Jason Miller</a> and many other developers have noted, including the SVG markup in JSX bloats the JavaScript bundle and makes the SVG less performant as a result. Instead of just having the browser parse and render an SVG, with JSX, we have expensive extra steps added to the browser. Remember, JavaScript is the most expensive Web resource, and by injecting SVG markup into JSX, we’ve made SVG as expensive as well.</p>

<p>One solution would be to create <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/symbol">SVG symbol</a> objects and include them with <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use">SVG use</a>. That way, we’ll be defining the SVG icon library in HTML, and we can instantiate it and customize it in React as much as we need to.</p>

<pre><code class="language-html">&lt;!-- Definition --&gt;
&lt;svg viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg"&gt;
  &lt;symbol id="myIcon" width="24" height="24" viewBox="0 0 24 24"&gt;
      &lt;!-- ... --&gt;
  &lt;/symbol&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;

&lt;!-- Usage --&gt;
&lt;svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"&gt;
  &lt;use href="#myIcon" /&gt;
&lt;/svg&gt;
</code></pre>

<div class="partners__lead-place"></div>

<h2 id="breathing-life-into-svgs">Breathing Life Into SVGs</h2>

<p>Animating SVGs can be easy and fun. It takes just a few minutes to create some simple and effective animations and interactions. If you are unsure which animation would be ideal for a graphic or should you animate it at all, it’s best to consult with the designer. You can even look for some similar examples and use cases on <a href="https://dribbble.com/shots/popular/animation">Dribble</a> or other similar websites.</p>

<p>It’s also important to keep in mind that <strong>animations should be tasteful, add to the overall user experience, and serve some purpose</strong> (draw the user’s attention, for example).</p>

<p>We’ll cover various use cases that you might encounter on your projects. Let’s start with a really sweet example.</p>

<h3 id="animating-a-cookie-banner">Animating A Cookie Banner</h3>

<p>Some years ago, I was working on a project where a designer made an adorable cookie graphic for an unobtrusive cookie consent popup to make the element more prominent. This cookie graphic was whimsical and a bit different from the general design of the website.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/06-cookie-banner.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="280"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/06-cookie-banner.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/06-cookie-banner.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/06-cookie-banner.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/06-cookie-banner.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/06-cookie-banner.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/06-cookie-banner.png"
			
			sizes="100vw"
			alt="A cookie banner"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Here is the design for our example and it’s very similar to the one I was working with some years ago. (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/06-cookie-banner.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>I’ve created the element and added the graphic, but when looking at the page as a whole, it felt kind of lifeless, and it didn’t stand out as much as we thought it will. The user needed to accept cookies as the majority of website functionality depended on cookies. We wanted to create an unobtrusive banner that doesn’t block user navigation from the outset, so I decided to animate it to make it more prominent and add a bit of flourish and character.</p>

<p>I’ve decided to create three animations that’ll be applied to the cookie SVG:</p>

<ul>
<li>Quick and snappy rolling fade-in entry animation;</li>
<li>Repeated wiggle animation with a good amount of delay in between;</li>
<li>Repeating and subtle eye sparkle animation.</li>
</ul>

<p>Here’s the final result of the element that we’ll be creating. We’ll cover each animation step by step.</p>


<figure class="video-embed-container">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/792218002"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
</figure>

<p>Now, this looks much better, doesn’t it? Let’s see what it adds to it:</p>

<ul>
<li>Animation matches the whimsical nature of the graphic itself.</li>
<li>Movement feels natural enough, although JavaScript-based animation libraries would produce better results.</li>
<li>Animation is tasteful and not intrusive, but it draws attention. It acts as a gentle reminder for a user to make a decision.</li>
</ul>

<p>Let’s have a quick look at the markup (some markup here is removed for clarity). You can check out the complete markup and CSS in the <a href="https://codepen.io/AdrianBece/pen/BaPQJZv?editors=1100">CodePen demo</a>.</p>

<p>We’ll animate the <code>svg</code> element itself and two <code>path</code> elements inside the SVG. Notice that the animation also features a shadow located in a <code>span</code> element, but <strong>we’ll focus only on our SVG elements</strong> as shadow just boils down to adding a simple CSS animation to an HTML element.</p>

<div class="break-out">

<pre><code class="language-html">&lt;figure role="presentation" class="cookie-notice&#95;&#95;graphic-container"&gt;
  &lt;span class="cookie-notice&#95;&#95;shadow"&gt;&lt;/span&gt;
  &lt;svg class="cookie-notice&#95;&#95;graphic" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 223.233 228.464"&gt;
    &lt;path fill="#f8c256" d="..." /&gt;
    &lt;!-- ... --&gt;
  &lt;/svg&gt;
&lt;/figure&gt;
</code></pre>
</div>

<h4 id="entry-animation">Entry Animation</h4>

<p>For the entry animation, we want the cookie to roll in from the left side of the screen and fade in as it rolls into place. Banner content is centered on the screen, so we cannot just roll the cookie from off-screen &mdash; it would look alright on smaller screens, but on larger screens, the movement would be jarring, so we’ll add a fade-in to keep the animation nice and snappy:</p>

<ul>
<li><code>opacity</code>: controls fade-in,</li>
<li><code>transform</code>: controls horizontal movement and rotation.</li>
</ul>

<pre><code class="language-css">@keyframes enter {
  0% {
    opacity: 0;
    transform: translate3d(-60%, 0, 0) rotateZ(-50deg);
  }

  100% {
    opacity: 1;
    transform: translate3d(0, 0, 0) rotateZ(17deg);
  }
}
</code></pre>

<p>We have animation keyframes in place, but we’re not done yet. It’s important to choose an appropriate <a href="https://www.smashingmagazine.com/2021/04/easing-functions-css-animations-transitions/">easing function</a>. The default linear movement wouldn’t look nice, and the pre-defined easing functions might look alright, but we can do better by creating our custom <a href="https://www.smashingmagazine.com/2021/04/easing-functions-css-animations-transitions/#cubic-b%C3%A9zier-functions">cubic-bezier function</a>.</p>

<p>Let’s <strong>consider the look and feel of the animation</strong>. We want the movement to be whimsical, so let’s add a very basic bounce-like easing function - we’ll make our cookie overshoot the final position and snap back into it, resulting in a single bounce. With JavaScript-based animation libraries, we can create more impressive <strong>spring-like easing functions</strong>. Still, I want to keep this as simple and as effective as possible to show you that you can add nice-looking animations on the fly, regardless of time constraints or lack of knowledge of SVGs.</p>

<p>If you’re interested in learning more about advanced JavaScript-based animations, I would recommend Josh Comeau’s <a href="https://www.joshwcomeau.com/animation/a-friendly-introduction-to-spring-physics/">wonderful article</a> on the topic. He also points out the <strong>importance of creating believable animations</strong> by choosing the correct easing functions.</p>

<blockquote>Spring physics are like a secret ingredient; they make all animations taste better. The motion produced using spring physics is fluid and organic. It’s believable; springs do a better job of tricking our brains into thinking that something is actually moving.</blockquote>

<p>We can use any easing function as a starting point and modify it. We’ll set the fourth number to a value above one, so we get that simple bouncing movement at the end.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/07-easing-function-simple-single-bounce.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="457"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/07-easing-function-simple-single-bounce.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/07-easing-function-simple-single-bounce.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/07-easing-function-simple-single-bounce.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/07-easing-function-simple-single-bounce.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/07-easing-function-simple-single-bounce.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/07-easing-function-simple-single-bounce.png"
			
			sizes="100vw"
			alt="Easing function"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Easing function for a simple single bounce. (Image source: <a href='https://easings.co/'>easings.co</a>) (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/07-easing-function-simple-single-bounce.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Let’s store it in a CSS variable so that we can reuse it for the repeatable wiggle movement animation.</p>

<pre><code class="language-css">--transition-bounce: cubic-bezier(0.2, 0.7, 0.4, 1.65);
</code></pre>

<p>Let’s put everything together, set a duration value and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/animation-fill-mode">fill-mode</a>, and add the animation to our <code>svg</code> element.</p>

<pre><code class="language-javascript">/&#42; Our SVG element &#42;/
.cookie-notice&#95;&#95;graphic {
  opacity: 0; /&#42; Should not be visible at the start &#42;/
  animation: enter 0.8s var(--transition-bounce) forwards;
}
</code></pre>

<p>Let’s check out what we’ve created. It already looks really nice. Notice how the bouncing easing function made a lot of difference to the overall look and feel of the whole element.</p>


<figure class="video-embed-container">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/792221004"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
</figure>

<h4 id="repeating-wiggle-animation">Repeating Wiggle Animation</h4>

<p>We’ve made a nice entry animation that might draw the user&rsquo;s attention at first, but the user might get distracted by other elements on the page or focus on performing other tasks. Remember, the cookies are important for our website’s main functionality, so we want to create <strong>a gentle reminder</strong> for the user <strong>that draws attention</strong> that is not obtrusive or blocks user actions.</p>

<p>If you are looking for ideas, <a href="https://dribbble.com/shots/popular/animation">Dribble</a> can be a great source of inspiration. For the wiggle animation, I was inspired by the following <a href="https://dribbble.com/shots/4576524-Alpaca-Animation">Alpaca animation example</a> by Matthew Ware. More specifically, I wanted to create a repeating wiggle movement with plenty of pauses between repeats, similar to how Matthew has animated the alpaca’s right ear.</p>

<p>So, let’s outline the five steps for this repeated animation:</p>

<ol>
<li>The object remains still (delay between iterations).</li>
<li>It pulls back gently to the left and rotates back.</li>
<li>It releases and moves forward and rotates forward.</li>
<li>It slows down as it returns to its original position and stops.</li>
<li>The object remains still (delay between iterations).</li>
</ol>

<p>However, <strong>CSS doesn’t offer an option to pause animation between iterations</strong> out of the box, so let’s be creative and solve this for ourselves. We can use percentage values in the <code>@keyframes</code> definition to create an <strong>artificial delay between iterations</strong>.</p>

<pre><code class="language-css">@keyframes wiggle {
  0% {}   /&#42; Stands still &#42;/
  45% {}  /&#42; Movement starts &#42;/
          /&#42; ... &#42;/
  60% {}  /&#42; Movement ends &#42;/
  100% {} /&#42; Stands still &#42;/
}
</code></pre>

<p>Just like in the previous example, we have to be aware of the easing properties that we’ll be using so that the wiggle movement still feels natural. We need to <strong>stay true to the look and feel</strong> that we’ve previously established, so the different animations blend nicely, so we’ll use our bouncing easing function that we’ve defined.</p>

<p>Easing functions are repeated between each pair of keyframe percentage values. We can make our movement overshoot the target and make the animation nice and bouncy.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="KKaEjbM"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Cubic bezier functions 3 keyframes](https://codepen.io/smashingmag/pen/KKaEjbM) by <a href="https://codepen.io/smashingmag">Smashing Magazine</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/KKaEjbM">Cubic bezier functions 3 keyframes</a> by <a href="https://codepen.io/smashingmag">Smashing Magazine</a>.</figcaption>
</figure>

<p>Let’s visualize the bouncing easing curve and apply it to the five animation step pairs that we’ve outlined: standing still, backward movement, forward movement, slowing down into the original place, and, finally, standing still.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/08-easing-function-bouncing-curve.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="397"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/08-easing-function-bouncing-curve.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/08-easing-function-bouncing-curve.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/08-easing-function-bouncing-curve.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/08-easing-function-bouncing-curve.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/08-easing-function-bouncing-curve.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/08-easing-function-bouncing-curve.png"
			
			sizes="100vw"
			alt="Easing function bouncing curve"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Easing function repeats between each keyframe value definition. Original curve created with <a href='https://easings.co/'>easings.co</a>. (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/08-easing-function-bouncing-curve.png'>Large preview</a>)
    </figcaption>
  
</figure>

<pre><code class="language-css">@keyframes wiggle {
  /&#42; Stands still &#42;/
  0% {
    transform: translate3d(0, 0, 0) rotateZ(17deg);
  }
  /&#42; Starts moving &#42;/
  45% {
    transform: translate3d(0, 0, 0) rotateZ(17deg);
  }

  /&#42; Pulls back &#42;/
  50% {
    transform: translate3d(-10%, 0, 0) rotateZ(8deg);
  }

  /&#42; Moves forward &#42;/
  55% {
    transform: translate3d(6%, 0, 0) rotateZ(24deg);
  }

  /&#42; Returns to starting position &#42;/
  60% {
    transform: translate3d(0, 0, 0) rotateZ(17deg);
  }

  /&#42; Stands still &#42;/
  100% {
    transform: translate3d(0, 0, 0) rotateZ(17deg);
  }
}
</code></pre>

<pre><code class="language-javascript">/&#42; Our SVG element &#42;/
.cookie-notice&#95;&#95;graphic {
  opacity: 0;
  animation: enter 0.8s var(--transition-bounce) forwards,
    wiggle 6s 3s var(--transition-bounce) infinite;
}
</code></pre>


<figure class="video-embed-container">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/792235075"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
</figure>

<h4 id="repeating-eye-sparkle-animation">Repeating Eye Sparkle Animation</h4>

<p>We’ve added to our SVG element, but so far, we’ve been working exclusively on the parent <code>svg</code> element, so we haven’t dug into the actual SVG markup. If you haven’t worked with SVG markup before, don’t worry &mdash; we’ll use just <strong>a few very simple tricks that we’ll repeat</strong> across all examples in the article to create various great-looking animations.</p>

<p>We need to find the six elements (circles) inside the SVG markup that we’ll animate. We’ll use the <strong>browser’s inspect element tool</strong> to quickly find the elements we’re looking for.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/09-browsers-inspect-element-tool.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="362"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/09-browsers-inspect-element-tool.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/09-browsers-inspect-element-tool.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/09-browsers-inspect-element-tool.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/09-browsers-inspect-element-tool.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/09-browsers-inspect-element-tool.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/09-browsers-inspect-element-tool.png"
			
			sizes="100vw"
			alt="Browser’s inspect element tool"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      We can use inspect element on our SVG file and find elements that we want to target. (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/09-browsers-inspect-element-tool.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>SVG elements can have a CSS <code>class</code> attribute, so we’ll use that to target them. Let’s add the <code>class</code> attribute to the two <code>path</code> elements that we identified.</p>

<pre><code class="language-css">&lt;!-- ... --&gt;
&lt;path fill="#351f17" d="..." /&gt;
&lt;path class="cookie&#95;&#95;eye" fill="#fff" d="..." /&gt;
&lt;path fill="#351f17" d="..." /&gt;
&lt;path class="cookie&#95;&#95;eye" fill="#fff" d="..." /&gt;
&lt;!-- ... --&gt;
</code></pre>

<p>We want to make cookie’s eyes sparkle. I got this idea from a music video for a song by <a href="https://youtu.be/-zIxPUPWVq8?t=300">Devin Townsend</a>. You can see the animation play at the 5-minute mark. It just goes to show how <strong>you can find ideas pretty much anywhere</strong>.</p>

<p>Let’s just change the scale and opacity. Notice how so far, we’ve relied only on those two attributes for all three animations, which are quite different from each other.</p>

<pre><code class="language-css">@keyframes sparkle {
  from {
    opacity: 0.95;
    transform: scale(0.95);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}
</code></pre>

<p>We want this animation to repeat without delay. It should be subtle enough to blend in nicely with the graphic and the overall element and not obtrusive for the user. As for the easing function, we’ll do something different. We’ll use <a href="https://www.smashingmagazine.com/2021/04/easing-functions-css-animations-transitions/#staircase-functions">staircase functions</a> to achieve that quick and snappy transition between the two animation states (our <code>from</code> and <code>to</code> values).</p>

<p>We need to be careful here. <strong>Transform origin is going to be set relative to the parent SVG element’s <code>viewbox</code></strong> and not the element itself. So if we set <code>transform-origin: center center</code>, the transformation will use the center coordinates of the parent SVG and not the <code>path</code> element. We can easily fix that by setting a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform-box">transform-box</a> property to <code>fill-box</code>.</p>

<blockquote>The nearest SVG viewport is used as the reference box. If a `viewBox` attribute is specified for the SVG viewport creating element, the reference box is positioned at the origin of the coordinate system established by the `viewBox` attribute, and the dimension of the reference box is set to the width and height values of the `viewBox` attribute.</blockquote>

<div class="break-out">

<pre><code class="language-css">.cookie&#95;&#95;eye {
  animation: sparkle 0.15s 1s steps(2, jump-none) infinite alternate;
  transform-box: fill-box;
  transform-origin: center center;
}
</code></pre>
</div>

<p>Last but not least, let’s <a href="https://web.dev/prefers-reduced-motion/">respect the user’s accessibility preferences</a> and turn off all animations if they have it set.</p>

<pre><code class="language-css">@media (prefers-reduced-motion: reduce) {
  &#42;,
  ::before,
  ::after {
    animation-delay: -1ms !important;
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    background-attachment: initial !important;
    scroll-behavior: auto !important;
    transition-duration: 0s !important;
    transition-delay: 0s !important;
  }
}
</code></pre>

<p>Here is the final result. Feel free to play around with the demo and experiment with keyframe values and easing values to change the look and feel of the animation.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="eYjMvXz"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Animated cookie svg [forked]](https://codepen.io/smashingmag/pen/eYjMvXz) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/eYjMvXz">Animated cookie svg [forked]</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<h3 id="transform-animations-on-svg-elements">Transform Animations On SVG Elements</h3>

<p>Let’s play around with the CSS transform property and CSS animation attributes and create a nice entry animation for an SVG that we have here.</p>

<p>First, let’s plan out our animation:</p>

<ol>
<li>No element will be visible at the beginning.</li>
<li>The dark-colored rectangle will come down from the top.</li>
<li>A white half-circle will rotate in.</li>
<li>Lines will scale horizontally and appear with some delay between them.</li>
</ol>


<figure class="video-embed-container">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/792238146"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
</figure>

<p>Let’s set up the keyframes we’ll need for the animations that we’ve outlined.</p>

<pre><code class="language-css">@keyframes fromTop {
    from {
    transform: translateY(100%);
  } to {
    transform: translateY(0);
  }
}

@keyframes rotateIn {
  from {
    transform: rotateZ(180deg);
  } to {
    transform: rotateZ(0);
  }
}

@keyframes scaleXIn {
  from {
    transform: scaleX(0);
  }
  
  to {
    transform: scaleX(1);
  }
}
</code></pre>

<p>We’ll use inspect element to find the target elements for the first two animations and set the appropriate <code>class</code> attributes.</p>

<div class="break-out">

<pre><code class="language-html">&lt;svg xmlns="http://www.w3.org/2000/svg"xml:space="preserve" viewBox="0 0 1600 1066"&gt;
  &lt;!-- ... --&gt;
  &lt;path class="sun&#95;&#95;bg" d="..." style="fill:#363636;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path14" /&gt;
  &lt;path class="sun&#95;&#95;top" d="..." style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path16" /&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;
</code></pre>
</div>

<p>Let’s define the <code>animation</code> for each element. We’ll use the <code>transform-box</code> property to set the transform-origin to the <code>path</code> element instead of the parent SVG.</p>

<pre><code class="language-css">.sun&#95;&#95;bg {
  transform: translateY(100%);
  animation: fromTop 0.5s 1s ease forwards;
  transform-box: fill-box;
}

.sun&#95;&#95;top {
  transform: rotateZ(180deg);
  animation: rotateIn 1s 1.4s ease-in-out forwards;
  transform-origin: center top;
  transform-box: fill-box;
}
</code></pre>

<p>So far, so good. We’ve used the same approach and techniques as in the previous example. Let’s introduce a new trick for animating the bottom rectangles.</p>

<p>We have 17 rectangles, and instead of going to each one of them and adding a <code>class</code> attribute to each one of them, <strong>let’s group them and apply the class to a group.</strong> It’s similar to a <code>div</code> element in HTML &mdash; we can use it to add a useful generic wrapper around elements so we can group them and style them easily. SVG has a handy little <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g">group element</a> (<code>g</code>). So we’ll add an opening tag before the first rectangle and a closing tag after the last element.</p>

<div class="break-out">

<pre><code class="language-html">&lt;g class="sun&#95;&#95;lines" id="g20" clip-path="url(#clipPath24)"&gt;
  &lt;path d="... style="fill:#363636;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path26" /&gt;
  &lt;path d="... style="fill:#363636;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path26" /&gt;
  &lt;path d="... style="fill:#363636;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path26" /&gt;
  &lt;!-- ... --&gt;
 &lt;/g&gt;
</code></pre>
</div>

<p>Much quicker and maintainable than adding 17 <code>class</code> attributes, isn’t it? And we haven’t changed anything visually on our SVG by doing that.</p>

<p>Let’s add animation to these <code>path</code> elements in a group. If we wanted all group elements to have the same animation properties, we could have simply added the animation to the group, but we want each <code>path</code> element to have a slight delay.</p>

<pre><code class="language-css">.sun&#95;&#95;lines &gt; path {
  transform: scaleX(0);
  animation: scaleXIn 0.5s 2.5s ease-in-out forwards;
  transform-origin: center center;
  transform-box: fill-box;
}
</code></pre>

<p>And finally, let’s add a delay to each path child element of the group. If we were using SASS or some other CSS pre-processor, we could have easily added these styles with a loop.</p>

<pre><code class="language-css">.sun&#95;&#95;lines &gt; path:nth-child(2) {
  animation-delay: 2.6s;
}

.sun&#95;&#95;lines &gt; path:nth-child(3) {
  animation-delay: 2.7s;
}

/&#42; ... &#42;/
</code></pre>

<p>Here is the demo and the final result. Feel free to play around with easing and delay values to create different animation effects.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="GRBxmRP"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Animated SVG graphic [forked]](https://codepen.io/smashingmag/pen/GRBxmRP) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/GRBxmRP">Animated SVG graphic [forked]</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<h3 id="repeating-animations-for-svg-patterns">Repeating Animations For SVG Patterns</h3>

<p>SVGs often serve as a background graphic and just as a decoration to some content, like a very prominent hero container like in our next example. Although it looks good enough on its own, we can make it look better by adding some motion to it.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/10-image-text-content-background-svg.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="409"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/10-image-text-content-background-svg.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/10-image-text-content-background-svg.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/10-image-text-content-background-svg.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/10-image-text-content-background-svg.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/10-image-text-content-background-svg.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/10-image-text-content-background-svg.png"
			
			sizes="100vw"
			alt="Image consisting of text content and background SVG"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Our hero image consists of text content and background SVG. (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/10-image-text-content-background-svg.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Let’s take a closer look at the SVG we’ll be working with. It consists of a few dozen <code>circle</code> elements.</p>

<pre><code class="language-html">&lt;!-- ... --&gt;
&lt;circle cx="103.5" cy="34.5" r="11.3"&gt;&lt;/circle&gt;
&lt;circle cx="172.5" cy="34.5" r="15.7"&gt;&lt;/circle&gt;
&lt;circle cx="310.5" cy="34.5" r="24.6"&gt;&lt;/circle&gt;
&lt;circle cx="517.5" cy="34.5" r="34.5"&gt;&lt;/circle&gt;
&lt;circle cx="586.5" cy="34.5" r="34.5"&gt;&lt;/circle&gt;
&lt;circle cx="655.5" cy="34.5" r="33.4"&gt;&lt;/circle&gt;
&lt;!-- ... --&gt;
</code></pre>

<p>Let’s start by adding a bit of opacity to our background and making it more chaotic. When we apply CSS transforms to elements inside SVG, they are transformed relative to the SVG’s main viewbox. That is why we’re getting a slightly chaotic displacement when applying a <code>scale</code> transform. We’ll use that to our advantage and not change the reference box.</p>

<p>To make things a little bit easier for us, we’ll use SASS. If you are unfamiliar with SASS and SCSS, you can view compiled CSS in CodePen below.</p>

<pre><code class="language-css">svg circle {
  opacity: 0.85;

  &:nth-child(2n) {
    transform: scale3d(0.75, 0.75, 0.75);
    opacity: 0.3;
}
</code></pre>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/11-image-sass.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="409"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/11-image-sass.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/11-image-sass.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/11-image-sass.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/11-image-sass.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/11-image-sass.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/11-image-sass.png"
			
			sizes="100vw"
			alt="Image with chaotic background"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/11-image-sass.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>With that in mind, let’s add some keyframes. We’ll use two sets of keyframes that we’ll apply randomly to our circle elements. Once again, we’ll leverage the <code>scale</code> transform displacement and change the opacity value.</p>

<pre><code class="language-css">@keyframes a {
  0% {
    opacity: 0.8;
    transform: scale3d(1, 1, 1);
  }
  100% {
    opacity: 0.3;
    transform: scale3d(0.75, 0.75, 0.75);
  }
}

@keyframes b {
  0% {
    transform: scale3d(0.75, 0.75 0.75);
    opacity: 0.3;
  }
  100% {
    opacity: 0.8;
    transform: scale3d(1, 1, 1);
  }
}
</code></pre>

<p>Now, let’s use quite a few <code>:nth-child</code> selectors. Every odd child will use the <code>a</code> keyframes, while every even circle will use a <code>b</code> keyframes. We’ll use <code>:nth-child</code> selectors to play around with animation duration and animation delay values.</p>

<div class="break-out">

<pre><code class="language-css">svg circle {
  opacity: 0.85;
  animation: a 10s cubic-bezier(0.45,0.05,0.55,0.95) alternate infinite;

  &:nth-child(2n) {
    transform: scale3d(0.75, 0.75, 0.75);
    opacity: 0.3;

    animation-name: b;
    animation-duration: 6s;
    animation-delay: 0.5s;
  }

  &:nth-child(3n) {
    animation-duration: 4s;
    animation-delay: 0.25s;
  }

  /&#42; ... &#42;/
}
</code></pre>
</div>

<p>And, once again, just by playing around with opacity values and CSS transforms on our SVG and playing around with child selectors and animation parameters, we’ve managed to create a more interesting background for our hero container.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="OJwvmWK"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Animated welcome screen [forked]](https://codepen.io/smashingmag/pen/OJwvmWK) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/OJwvmWK">Animated welcome screen [forked]</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<h4 id="svg-as-a-css-background-image">SVG As A CSS Background Image</h4>

<p>As a quick bonus example, we can also convert our repeatable SVG pattern shape into base64 using <a href="https://codebeautify.org/svg-to-base64-converter">this handy tool</a> and use it as a repeatable <code>background-image</code> pattern.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/12-design-repeatable-circle-pattern.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="275"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/12-design-repeatable-circle-pattern.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/12-design-repeatable-circle-pattern.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/12-design-repeatable-circle-pattern.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/12-design-repeatable-circle-pattern.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/12-design-repeatable-circle-pattern.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/12-design-repeatable-circle-pattern.png"
			
			sizes="100vw"
			alt="Design with a repeatable circle pattern"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Let’s use this design for our example with a repeatable circle pattern. (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/12-design-repeatable-circle-pattern.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Here is a markup for our circle SVG.</p>

<div class="break-out">

<pre><code class="language-html">&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"&gt;&lt;circle cx="50" cy="50" r="50" fill-opacity=".03"/&gt;&lt;/svg&gt;
</code></pre>
</div>

<p>Be careful not to inline too much data with base64, so stylesheets can be downloaded and parsed quickly. When we convert it to base64, we get this handy CSS <code>background-image</code> snippet:</p>

<div class="break-out">

<pre><code class="language-css">background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI1MCIgZmlsbC1vcGFjaXR5PSIuMDMiLz48L3N2Zz4=);
</code></pre>
</div>

<p>We can simply apply a simple animation where we offset the <code>background-position</code> by the <code>background-size</code> value and get this neat background animation.</p>

<pre><code class="language-css">.wrapper {
  animation: move-background 3.5s linear;
  background-image: url(data:image/svg+xml;base64,...);
  background-size: 96px;
  background-color: #16a757;
  /&#42; ... &#42;/
}

@keyframes move-background {
  from {
    background-position: 0 0;
  }

  to {
    background-position: 96px 0;
  }
}
</code></pre>

<p>Our example looks more interesting with this subtle moving animation going on in the background. Remember to respect users’ accessibility preferences and turn off the animations if they have a preference set.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="mdjxmXZ"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Animating background SVG pattern [forked]](https://codepen.io/smashingmag/pen/mdjxmXZ) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/mdjxmXZ">Animating background SVG pattern [forked]</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<h3 id="self-drawing-eelf-erasing-svg-animation">Self-Drawing &amp; Eelf-Erasing SVG Animation</h3>

<p>Self-drawing / Self-erasing SVG is a cool and impressive animation that can be applied to any SVG <code>path</code> or another element that has a <code>stroke</code> attribute set. Let’s take our SVG text, for example, and check out its markup. Notice the <code>stroke</code> and <code>stroke-width</code> properties.</p>

<div class="break-out">

<pre><code class="language-html">&lt;svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 323 151"&gt;
  &lt;path fill="none" stroke="#a56a19" stroke-width="2" d="..." /&gt;
&lt;/svg&gt;
</code></pre>
</div>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/13-starting-text.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="271"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/13-starting-text.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/13-starting-text.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/13-starting-text.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/13-starting-text.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/13-starting-text.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/13-starting-text.png"
			
			sizes="100vw"
			alt="Starting text"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Our starting text. (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/13-starting-text.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Before diving into the animation, we need to cover two SVG properties that we’ll be using: <code>stroke-dasharray</code> and <code>stroke-dashoffset</code>. They’re integral for pulling off this animation.</p>

<p>Stroke can be converted to dashes with a certain length using a <code>stroke-dasharray</code> property.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/14-stroke-dasharray-property.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="257"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/14-stroke-dasharray-property.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/14-stroke-dasharray-property.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/14-stroke-dasharray-property.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/14-stroke-dasharray-property.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/14-stroke-dasharray-property.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/14-stroke-dasharray-property.png"
			
			sizes="100vw"
			alt="Text with stroke converted into dashes"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Converting stroke into dashes with the length of 10 pixels with CSS <code>stroke-dasharray: 10</code>. (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/14-stroke-dasharray-property.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>And we can offset the positions of those strokes by a certain amount using the <code>stroke-dashoffset</code> property.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/15-stroke-dashoffset-property.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="245"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/15-stroke-dashoffset-property.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/15-stroke-dashoffset-property.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/15-stroke-dashoffset-property.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/15-stroke-dashoffset-property.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/15-stroke-dashoffset-property.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/15-stroke-dashoffset-property.png"
			
			sizes="100vw"
			alt="Strokes with a slight dash offset"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Adding a slight 10px dash offset with CSS <code>stroke-dashoffset: 10</code>. (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/15-stroke-dashoffset-property.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>So, what’s this have to do with our drawing and erasing animation? Imagine what would happen if we could have a dash that covers the whole stroke length and offset it by the same value. In that case, the starting point of the stroke would be way past the ending point of the stroke, and we wouldn’t see it.</p>

<div class="break-out">

<pre><code class="language-css">svg path {
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 800;  /&#42; Dash covering the whole stroke &#42;/
  stroke-dashoffset: 800; /&#42; Offset it to make it invisible &#42;/
}
</code></pre>
</div>

<p>If we animate the offset value from that value back to 0, the stroke would slowly become visible, <strong>as it was drawing itself</strong>.</p>

<div class="break-out">

<pre><code class="language-css">svg path {
  /&#42; ... &#42;/
  animation: draw 6s linear infinite;
}

@keyframes draw{
  to {
    stroke-dashoffset: 0; /&#42; Reduce offset to make it visible &#42;/
  }
}
</code></pre>
</div>

<p>If we continue to animate the offset value from 0 to a negative value, <strong>we’d get the erasing effect</strong>.</p>

<pre><code class="language-css">svg path {
  /&#42; ... &#42;/
  animation: drawAndErase 6s linear infinite;
}

@keyframes drawAndErase {
  to {
    stroke-dashoffset: -800;
  }
}
</code></pre>

<p>You’re probably wondering where the magical <code>800</code> pixel value came from. This value depends on the SVG and the length of the dash needed to cover the whole stroke length. It can be easily guessed, but <a href="https://css-tricks.com/svg-line-animation-works/">Chris Coyier</a> has a handy function that can do it for you. However, depending on the stroke properties and SVG shape, this function might not always return an ideal value, but it can guide you closer to it.</p>

<p>Check out the complete demo and feel free to play around with values to see how the stroke properties affect the animation. If you are looking for more examples, CodyHouse has covered a <a href="https://codyhouse.co/nuggets/self-drawing-svg-animation">fun-looking button animation</a> using the same trick.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="LYBdLNK"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [SVG stroke animation [forked]](https://codepen.io/smashingmag/pen/LYBdLNK) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/LYBdLNK">SVG stroke animation [forked]</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<div class="partners__lead-place"></div>

<h2 id="playing-with-the-smashing-cat">Playing With The Smashing Cat</h2>

<p>I simply could not resist including Magazine’s fabulous cat mascot and animating it! Smashing Magazine has so many fun-looking SVGs, it was very difficult to choose just one, so I’ve narrowed it down to two SVGs.</p>

<p>For the first SVG, we’ll take everything we’ve learned so far and add a nice-looking animation, and for the second SVG, we’ll add some fun interactions using JavaScript.</p>

<h3 id="using-everything-that-we-ve-learned-so-far">Using Everything That We’ve Learned So Far</h3>

<p>Everything we’ve done for this example is the same as in previous ones, only combined into a single SVG and involving a few more elements and groups of elements. Feel free to check out the CodePen demo below and use the browser inspect element feature to check out what is going on under the hood.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="xxJWrOB"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Smashing cat animated [forked]](https://codepen.io/smashingmag/pen/xxJWrOB) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/xxJWrOB">Smashing cat animated [forked]</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<p>For this example, every animation was pretty much straightforward &mdash; just find the element using the browser’s inspect element tool, group it if needed, apply a class and add an animation.</p>

<p>I guess that the pipe animation was the most difficult one to get right as the pipe has to follow the contours of the mouth precisely. I was just playing around with the transform values and picked whatever ended up looking great.</p>

<pre><code class="language-javascript">.pipe {
  transform-box: fill-box;
  transform-origin: top left;
  animation: pipeMove 4s ease-in-out infinite alternate;
}

@keyframes pipeMove {
  from {
    transform: translate3d(4px, -12px, 0) rotateZ(-5deg);
  }
  to {
    transform: translate3d(-5px, 12px, 0) rotateZ(5deg);
  }
}
</code></pre>

<h3 id="adding-interactivity">Adding Interactivity</h3>

<p>Now for an even more interesting example: Smashing cat playing a barista. Let’s focus on making it interactive by adding the following:</p>

<ol>
<li>Eyes should follow the cursor.</li>
<li>Clicking on the hat should animate it.</li>
<li>Clicking on a bowtie should animate it.</li>
<li>Clicking on the coffee machine handle should make coffee pour into a cup.</li>
</ol>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/svg-customization-animation-practical-guide/16-smashing-cat-barista.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="537"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/16-smashing-cat-barista.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/svg-customization-animation-practical-guide/16-smashing-cat-barista.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/svg-customization-animation-practical-guide/16-smashing-cat-barista.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/svg-customization-animation-practical-guide/16-smashing-cat-barista.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/svg-customization-animation-practical-guide/16-smashing-cat-barista.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/svg-customization-animation-practical-guide/16-smashing-cat-barista.png"
			
			sizes="100vw"
			alt="Smashing cat playing a barista"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Just looking at awesome these SVGs sparks so many animation ideas! (<a href='https://files.smashing.media/articles/svg-customization-animation-practical-guide/16-smashing-cat-barista.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Let’s start by adding the <strong>mouse-tracking eye animation</strong>. We’ll skip manually implementing this feature in JavaScript and use a handy library called <a href="https://github.com/jj811208/watching-you">watching-you</a>.</p>

<p>Using the browser’s inspect element tool, we’ll find the target elements inside the SVG and add the eye-left and eye-right CSS classes to these elements, respectively.</p>

<div class="break-out">

<pre><code class="language-javascript">&lt;ellipse class="cls-5 eye eye-left" cx="245.15133" cy="134.57033" rx="5.31264" ry="8.61816" transform="translate(-33.47349 110.5587) rotate(-23.83807)" /&gt;
&lt;ellipse class="cls-4 eye eye-right" cx="284.42686" cy="116.68559" rx="5.31264" ry="8.61816" transform="translate(-22.89477 124.9063) rotate(-23.83807)" /&gt;
</code></pre>
</div>

<p>We’ll configure the library and make it target the classes that we’ve added.</p>

<pre><code class="language-javascript">const optionsLeft = { power: 4, rotatable: false };
const watcherLeft = new WatchingYou(".eye-left", optionsLeft);
watcherLeft.start();

const optionsRight = { power: 3, rotatable: false };
const watcherRight = new WatchingYou(".eye-right", optionsRight);
watcherRight.start();
</code></pre>

<p>We also need to remember to apply the <code>transform-box</code> property, so our eyes move around the center.</p>

<pre><code class="language-css">.eye {
  transform-box: fill-box;
  transform-origin: center center;
}
</code></pre>

<p>Let’s check out what we’ve got. With just a few lines of code and a tiny JavaScript library to do the heavy lifting, we’ve made the SVG element respond to the mouse position. Now that’s amazing, isn’t it?</p>


<figure class="video-embed-container">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/792276628"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
</figure>

<p>Bowtie and hat animation will be created in a very similar way. Let’s start with a hat and find it using the browser’s inspect element tool. The hat graphic consists of two path elements, so let’s group them.</p>

<pre><code class="language-html">&lt;g class="hat"&gt;
  &lt;path class="cls-6" d="..." /&gt;
  &lt;path class="cls-9" d="..." /&gt;
&lt;/g&gt;
</code></pre>

<p>We’ll apply the same <code>transform-box</code> property and add a <code>hat--active</code> class that will run the animation when applied.</p>

<pre><code class="language-css">.hat {
  transform-box: fill-box;
  transform-origin: center bottom;
  cursor: pointer;
}

.hat--active {
  animation: hatJump 1s cubic-bezier(0, 0.7, 0.5, 1.25);
}

@keyframes hatJump {
  0% {
    transform: rotateZ(0) translateY(0);
  }

  50% {
    transform: rotateZ(-10deg) translateY(-50%);
  }

  100% {
    transform: rotateZ(0) translateY(0);
  }
}
</code></pre>

<p>Finally, let’s set up a click event listener that applies an active class to the element and then removes it after the animation has finished running.</p>

<pre><code class="language-javascript">const hat = document.querySelector(".hat");

hat.addEventListener("click", function () {
  if (hat.classList.contains("hat--active")) {
    return;
  }
  // Add the active class.
  hat.classList.add("hat--active");
  
  // Remove the active class after 1.2s.
  setTimeout(function () {
    hat.classList.remove("hat--active");
  }, 1200);
});
</code></pre>

<p>We use the same trick with the bowtie element, only applying a different animation and class. Feel free to check out the <a href="https://codepen.io/AdrianBece/pen/gOjgQGw">CodePen demo</a> for more details.</p>


<figure class="video-embed-container">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/792697095"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
</figure>

<p>Let’s move on to the coffee machine. Notice we don’t have any SVG element acting as a coffee on our SVG, so we’ll need to add it ourselves. You should <strong>feel comfortable editing SVG markup</strong> and we don’t even have to break a sweat here. Let’s make it easy for ourselves and find and copy the coffee machine’s pipe rectangle, which is similar to the coffee stream shape we want to have. We just have to change the color to brown and slightly adjust the dimensions.</p>

<div class="break-out">

<pre><code class="language-html">&lt;!-- Pipe --&gt;
&lt;rect class="cls-12" x="137.81171" y="243.99883" width="6.21967" height="12.29272" transform="translate(281.84309 500.29037) rotate(-180)" /&gt;

&lt;!-- Copied and adjusted Pipe rect to act as a coffee --&gt;
&lt;rect class="coffee" x="139" y="243.99883" width="4" height="12.29272" transform="translate(281.84309 500.29037) rotate(-180)" fill="brown" /&gt;
</code></pre>
</div>

<p>Like in the previous examples, let’s add active classes and their respective animation keyframes. We’ll compose the two animations and play around with duration and delay.</p>

<pre><code class="language-css">.lever, .coffee {
  transform-box: fill-box;
  transform-origin: center bottom;
}

.lever {   
  cursor: pointer; 
}

.lever--active {
  animation: leverPush 2.5s linear;
}

@keyframes leverPush {
  0% {
    transform: translateY(0);
  }
  8% {
    transform: translateY(50%);
  }
  90% {
    transform: translateY(50%);
  }
  100% {
    transform: translateY(0);
  }
}

.coffee--active {
  animation: coffeeStream 2.4s 0.1s ease-out forwards;
}

@keyframes coffeeStream {
  0% {
    transform: translateY(0);
  }
  5% {
    transform: translateY(50%);
  }
  95% {
    transform: translateY(50%);
  }
  100% {
    transform: translateY(150%);
  }
}
</code></pre>

<p>Let’s apply the active classes on click and remove them after the animation has finished running. And that’s it!</p>

<pre><code class="language-javascript">const lever = document.querySelector(".lever");
const coffee = document.querySelector(".coffee");

lever.addEventListener("click", function () {
  if (lever.classList.contains("lever--active")) {
    return;
  }

  lever.classList.add("lever--active");
  coffee.classList.add("coffee--active");

  setTimeout(function () {
    lever.classList.remove("lever--active");
    coffee.classList.remove("coffee--active")
  }, 2500);
});
</code></pre>

<p>Check out the complete example below, and, as always, feel free to play around with the animations and experiment with other elements, like the speech bubble or making the coffee machine’s lights blink while coffee is pouring out. Have fun!</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="gOjzMap"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Smashing cat interaction [forked]](https://codepen.io/smashingmag/pen/gOjzMap) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/gOjzMap">Smashing cat interaction [forked]</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<h2 id="conclusion">Conclusion</h2>

<p>I hope that this article encourages you to play around and make some wonderful SVG animations and interactions and integrate this workflow into your day-to-day projects. We’ve used only a handful of tricks and CSS properties to create a whole variety of nice effects on the fly. With some extra time, knowledge, and effort, you can create some truly amazing and interactive graphics.</p>

<p>Feel free to reach out on <a href="https://twitter.com/AdrianBeceDev">Twitter</a> and share your work. Happy to hear your thoughts and see what you come up with!</p>

<h3 id="references">References</h3>

<ul>
<li><a href="https://www.youtube.com/watch?v=emFMHH2Bfvo">SVG explained in 100 seconds</a>, Fireship</li>
<li>“<a href="https://css-tricks.com/svg-line-animation-works/">How SVG Line Animation Works</a>”, Chris Coyier</li>
<li><a href="https://www.w3.org/TR/SVG2/">SVG specs</a>, W3C</li>
<li>“<a href="https://web.dev/prefers-reduced-motion/"><code>prefers-reduced-motion</code>: Sometimes less movement is more</a>”, Thomas Steiner</li>
</ul>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(vf, yk, il)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Adrian Bece</author><title>Lesser-Known And Underused CSS Features In 2022</title><link>https://www.smashingmagazine.com/2022/05/lesser-known-underused-css-features-2022/</link><pubDate>Mon, 23 May 2022 09:30:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2022/05/lesser-known-underused-css-features-2022/</guid><description>CSS is constantly evolving, and some cool and useful properties either go completely unnoticed or are not talked about as much as others for some reason or another. In this article, we’ll cover a fraction of those CSS properties and selectors.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2022/05/lesser-known-underused-css-features-2022/" />
              <title>Lesser-Known And Underused CSS Features In 2022</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Lesser-Known And Underused CSS Features In 2022</h1>
                  
                    
                    <address>Adrian Bece</address>
                  
                  <time datetime="2022-05-23T09:30:00&#43;00:00" class="op-published">2022-05-23T09:30:00+00:00</time>
                  <time datetime="2022-05-23T09:30:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                
                

<p>After reading Louis Lazaris’ insightful article “<a href="https://www.smashingmagazine.com/2022/03/html-attributes-you-never-use/">Those HTML Attributes You Never Use</a>”, I’ve asked myself (<a href="https://twitter.com/AdrianBeceDev/status/1511312060780630019">and the community</a>) which properties and selectors are lesser-known or should be used more often. Some answers from the community surprised me, as they’ve included some very useful and often-requested CSS features which were made available in the past year or two.</p>

<p>The following list is created with community requests and my personal picks. So, let’s get started!</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h2 id="all-property"><code>all</code> Property</h2>

<p>This is a shorthand property which is often used for <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/all">resetting all properties</a> to their respective initial value by effectively stopping inheritance, or to enforce inheritance for all properties.</p>

<ul>
<li><code>initial</code><br />
Sets all properties to their respective initial values.</li>
<li><code>inherit</code><br />
Sets all properties to their inherited values.</li>
<li><code>unset</code><br />
Changes all values to their respective default value which is either <code>inherit</code> or <code>initial</code>.</li>
<li><code>revert</code><br />
Resulting values depend on the stylesheet origin where this property is located.</li>
<li><code>revert-layer</code><br />
Resulting values will match a previous cascade layer or the next matching rule.</li>
</ul>

<figure >
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="NWyRvZL"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [all property](https://codepen.io/smashingmag/pen/NWyRvZL) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/NWyRvZL">all property</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<p>This property can be used effectively for resetting styles or when <a href="https://www.smashingmagazine.com/2021/07/refactoring-css-introduction-part1/">refactoring CSS</a> to stop inheritance and prevent unwanted styles for leaking in.</p>

<pre><code class="language-javascript">h2 {
   color: var(--color-primary);
   font-size: var(--font-size-large);
   line-height: 1.5;
   text-decoration: underline;
   margin-bottom: 2rem;
}

.article h2 {
  padding: 2em;
  border-bottom: 2px solid currentColor;
}

.article__title {
  /&#42; We don't want styles from previous selector. We only need a margin and a font size. &#42;/
  all: unset;
  margin-bottom: 2rem;
  font-size: var(--font-size-medium);
}</code></pre>

<p>With <code>revert-layer</code> we can skip to a next cascade layer, to inherit styles from another selector but to prevent styles from the closest selector in the cascade from leaking in.</p>

<p>While playing around with this property, I’ve discovered an interesting behavior — underline color won’t update to a currently assigned color unless <code>text-decoration: underline;</code> is applied again to the <code>.title</code> selector which contains the <code>all</code> property.</p>

<figure >
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="bGLwoGx"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [all property - revert-layer](https://codepen.io/smashingmag/pen/bGLwoGx) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/bGLwoGx">all property - revert-layer</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<h2 id="currentcolor"><code>currentColor</code></h2>

<p>Often referred to as “the first CSS variable”, <code>currentColor</code> is a value equal to the element’s <code>color</code> property. It can be used to <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentcolor_keyword">assign a value</a> equal to the value of the <code>color</code> property to any CSS property which accepts a color value. It forces a CSS property to inherit the value of the <code>color</code> property.</p>

<p>This value can be very useful to avoid assigning the same value to multiple CSS properties which accept color like <code>border-color</code>, <code>background</code>, <code>box-shadow</code>, etc. within the same selector.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="MWQjEYN"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [curentColor alerts](https://codepen.io/smashingmag/pen/MWQjEYN) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/MWQjEYN">curentColor alerts</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<p>In my opinion, one of the best use-cases for <code>currentColor</code> is styling inlined SVG elements. Whenever we export an icon from a design tool, it comes with a specific <code>fill</code> and other color values defined in the design. We can manually replace all those color values with <code>currentColor</code>, and that way we can easily customize the color of SVG without having to go into the SVG markup and override the <code>fill</code> or other color-based attributes for an individual <code>path</code>, or other SVG elements, and make our CSS selectors complex and convoluted.</p>

<pre><code class="language-css">&lt;!-- Before --&gt;
&lt;path fill="#bbdb44" d="..."/&gt;

&lt;!-- After --&gt;
&lt;path fill="currentColor" d="..."/&gt;</code></pre>

<pre><code class="language-css">/&#42; Before &#42;/
.icon:hover path {
  fill: #112244;
}

/&#42; After &#42;/
.icon {
  color: #bbdb44;
}

.icon:hover {
  color: #112244;
}</code></pre>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="MWQjEKN"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [currentColor svg](https://codepen.io/smashingmag/pen/MWQjEKN) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/MWQjEKN">currentColor svg</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<h2 id="custom-property-fallback-value">Custom Property Fallback Value</h2>

<p>Custom properties brought significant improvements to CSS by allowing developers to create reusable values in their stylesheet without the need for CSS preprocessor like SASS. Custom properties were instantly adopted and are widely used today to great effect, especially in theming and interaction with JavaScript.</p>

<p>However, I feel like the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties#custom_property_fallback_values">fallback value</a> was somewhat ignored. If you are unfamiliar with the fallback value, it’s the second value that can be assigned to <code>var</code> function which is applied if the first value is not set.</p>

<pre><code class="language-css">color: var(--color-icon, #9eeb34);</code></pre>

<p>We can also set another variable as a fallback.</p>

<div class="break-out">

<pre><code class="language-css">color: var(--color-icon-primary, var(--color-icon-default));</code></pre>

</div>

<p>You can probably already see how this value can be used to provide a reliable fallback to default styles while allowing for customization.</p>

<p>This secondary value also allows for an elegant way to override theme colors without increasing specificity.</p>

<p>We can easily change custom variable values by overriding them.</p>

<div class="break-out">

<pre><code class="language-css">:root {
  --theme-color-background: #f5f5f5;
  --theme-color-text: #111111;
}

/&#42; Global override on a parent class on &lt;body&gt; or &lt;html&gt; element &#42;/
.theme--dark {
  --theme-color-background: #111111;
  --theme-color-text: #f5f5f5;
}</code></pre>

</div>

<p>But what about the cases where this global override is not ideal for all components, and we want to fine-tune the properties for individual components? In such cases, we would have to override the styles.</p>

<pre><code class="language-css">.box {
    color: var(--color-theme-default);
}

.theme--dark .box {
  color: var(--color-component-override);
}</code></pre>

<p>We have increased specificity as a result which is not ideal and can cause issues in more complex cases or in cases where specificity is left unmanaged. What we can do instead is use the fallback value to apply theming without increasing specificity inside the component. This makes the component more themable and portable, as it doesn’t introduce any parent class names for the component and other similar dependencies.</p>

<div class="break-out">

<pre><code class="language-javascript">:root {
--theme-color-default: darkgoldenrod;
--color-some-other-color: cyan;
}

.theme--dark {
/&#42; Dark theme &#42;/
  --color-component-override: var(--color-some-other-color);
}

.box {
  color: var(--color-component-override, var(--theme-color-default));
}</code></pre>

</div>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="wvyzroQ"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Custom properties fallback theme](https://codepen.io/smashingmag/pen/wvyzroQ) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/wvyzroQ">Custom properties fallback theme</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<h2 id="counters">Counters</h2>

<p>CSS allows developers to define <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Counter_Styles/Using_CSS_counters">named counters</a> that can be incremented, decremented, and displayed using CSS <code>content</code> property.</p>

<ul>
<li><code>counter-reset</code><br />
This property is used for initializing single or multiple counters. A default starting value can also be assigned.</li>
<li><code>reversed</code><br />
Function used when defining a counter with <code>counter-reset</code> to make the counter count down instead of up.</li>
<li><code>counter-increment</code><br />
Specify a counter to increment (or decrements if counter is defined as <code>reversed</code> or if a negative value is passed to <code>counter-increment</code>). Default increment value is 1, but a custom value value can also be passed to this property.</li>
<li><code>counter</code><br />
Used for accessing counter value. Usually used in <code>content</code> property.</li>
</ul>

<p>In the following example, we are initializing two counters <code>articles</code> which keeps count of the main sections and <code>notes</code> which keeps count of the notes on the page. A single section section can have multiple notes.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="RwQGLpQ"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [counters articles and notes](https://codepen.io/smashingmag/pen/RwQGLpQ) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/RwQGLpQ">counters articles and notes</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<p>What if we want to easily identify which note belongs to which article on a page? We need to add an article number to each note. For example, a second note of the third article — “Note 3.2.”.</p>

<p>We can easily adjust how notes counters are initialized and displayed. We can use multiple counter values in a single <code>content</code> property.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="vYdXemd"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [counters articles and notes - nested](https://codepen.io/smashingmag/pen/vYdXemd) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/vYdXemd">counters articles and notes - nested</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<p>Using CSS counters, allows us to easily add, remove, and rearrange these elements without having to worry about updating the counter values manually and without the need for using JavaScript.</p>

<h2 id="interaction-media-queries">Interaction Media Queries</h2>

<p>Cristian Díaz covered this topic in <a href="https://www.smashingmagazine.com/2022/03/guide-hover-pointer-media-queries/">his recent article</a>. When creating responsive websites, we often make assumptions about input mechanisms based on their screen size. We assume that the screen size of <code>1920px</code> belongs to a desktop computer or laptop and the user is interacting with the website using a mouse and keyboard, but what about laptops with touchscreen or smart TV screens?</p>

<p>This is where Interaction Media Features come in and allow us to fine-tune the usability of our components that users can interact with (inputs, offcanvas menus, dropdowns, modals, etc.) depending on the primary input mechanism — touch, stylus, mouse pointer, etc.</p>

<pre><code class="language-css">@media (pointer: fine) {
  /&#42; using a mouse or stylus &#42;/
}
@media (pointer: coarse) {
  /&#42; using touch &#42;/
}
@media (hover: hover) {
  /&#42; can be hovered &#42;/
}
@media (hover: none) {
  /&#42; can't be hovered &#42;/
}</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/744bbae0-ea35-481d-bdbe-a5b424c3f17e/1-lesser-known-underused-css-features-2022.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="398"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/744bbae0-ea35-481d-bdbe-a5b424c3f17e/1-lesser-known-underused-css-features-2022.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/744bbae0-ea35-481d-bdbe-a5b424c3f17e/1-lesser-known-underused-css-features-2022.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/744bbae0-ea35-481d-bdbe-a5b424c3f17e/1-lesser-known-underused-css-features-2022.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/744bbae0-ea35-481d-bdbe-a5b424c3f17e/1-lesser-known-underused-css-features-2022.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/744bbae0-ea35-481d-bdbe-a5b424c3f17e/1-lesser-known-underused-css-features-2022.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/744bbae0-ea35-481d-bdbe-a5b424c3f17e/1-lesser-known-underused-css-features-2022.png"
			
			sizes="100vw"
			alt="Table with three clolumns: media query hover&#39;s value, media query pointer&#39;s value, device"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Table from the article 'A Guide To Hover And Pointer Media Queries' by Cristian Diaz. (<a href='https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/744bbae0-ea35-481d-bdbe-a5b424c3f17e/1-lesser-known-underused-css-features-2022.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h2 id="aspect-ratio-for-sizing-control"><code>aspect-ratio</code> For Sizing Control</h2>

<p>When <code>aspect-ratio</code> was initially released, I thought I won’t use it outside image and video elements and in very narrow use-cases. I was surprised to find myself using it in a similar way I would use <code>currentColor</code> — to avoid unnecessarily setting multiple properties with the same value.</p>

<p>With <code>aspect-ratio</code>, we can easily <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio">control size</a> of an element. For example, equal width and height buttons will have an aspect ratio of <code>1</code>. That way, we can easily create buttons that adapt to their content and varying icon sizes, while maintaining the required shape.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="qBxaPoX"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [aspect-radio buttons](https://codepen.io/smashingmag/pen/qBxaPoX) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/qBxaPoX">aspect-radio buttons</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<h2 id="better-gradients">Better Gradients</h2>

<p>We’ve been using gradients on the Web for a while, and they’ve become a staple in design. However, as <a href="https://www.joshwcomeau.com/css/make-beautiful-gradients/">Josh W. Comeau points out</a>, the middle part of the gradient can sometimes look gray and washed out, depending on the colors you are using.</p>

<p>In the following example, we are setting two gradients between the same two values (green and red). Notice in the first example how the colors in the middle part look muddy and washed out, because the browser is using RGB color interpolation by default. We cannot change that at the moment, but we might in the future with new CSS features. However, we can fix that by adding some midpoints to the gradient.</p>

<p>The second example uses an interpolation technique with multiple midpoints, which is generated using Josh W. Comeau’s <a href="https://www.joshwcomeau.com/gradient-generator/">Gradient generator</a> tool. Notice how the middle part is now darker yellow and orange, and it looks much more vibrant and beautiful than in the first example.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="BaYLwxM"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Gradients](https://codepen.io/smashingmag/pen/BaYLwxM) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/BaYLwxM">Gradients</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<div class="partners__lead-place"></div>

<h2 id="where-and-is-pseudo-selectors"><code>:where</code> And <code>:is</code> Pseudo-Selectors</h2>

<p>These two pseudo-selectors gained wider browser support last year, and although there was much talk around them, I haven’t seen all those many uses around the Web. Stephanie Eckles has talked in-depth about these two pseudo-selectors in <a href="https://www.smashingmagazine.com/2021/04/guide-supported-modern-css-pseudo-class-selectors/#is">her article</a>.</p>

<p>Both of these selectors deal with grouping and specificity, so let’s start with <code>:is</code> pseudo-selector.</p>

<p>Let’s take a look at the following example. We want to set the following default styles for list items and nested lists. We need to cover both ordered and unordered lists and their combinations.</p>

<pre><code class="language-css">ol li,
ul li {
  margin-bottom: 0.25em;
}

ol ol,
ul ul,
ol ul,
ul ol {
  margin: 0.25em 0 1em;
}</code></pre>

<p>With <code>:is</code> pseudo-selector, we can easily turn these selectors into a single expression.</p>

<pre><code class="language-css">:is(ol,ul) li {
  margin-bottom: 0.25em;
}

:is(ol,ul) :is(ol,ul) {
  margin: 0.25em 0 1em;
}</code></pre>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="jOZMGvO"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Nested lists](https://codepen.io/smashingmag/pen/jOZMGvO) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/jOZMGvO">Nested lists</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<p><code>:where</code> works the same as <code>:is</code>, but it reduces the specificity of the expression to zero. Now, why is this important? Let’s go back to our example and change the markup a bit. Let’s add a <code>.list</code> selector, so we can add styles to list by assigning a class. Let’s add an additional class for a nested list <code>.list-highlight</code> which adds a background color and adjusts paddings and margins, so the nested list looks more prominent.</p>

<pre><code class="language-css">/&#42; Default styles for nested lists &#42;/
.list :is(ol,ul) {
  margin: 0.25em 0 1em;
}

/&#42; Utility class for a nested list &#42;/
.list-highlight  {
  background: #eeeeee;
  padding: 1em 1em 1em 2em;
  margin: 0.5em 0;
}</code></pre>

<p>However, when we apply <code>list-highlight</code> class to any of the nested lists, the margins look off, because that style doesn’t apply. What is going on?</p>

<p>Resulting specificity for <code>:is</code> selector matches the highest one in the list. So, margin styles from our <code>.list-highlight</code> util class will never win against it.</p>

<p>We want to avoid increasing specificity and adding dependencies for our utility classes, so let’s switch <code>:is</code> with <code>:where</code> and see what happens.</p>

<pre><code class="language-css">.list :where(ol,ul) {
  /&#42; ... &#42;/
}</code></pre>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="mdXrBzz"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Nested lists - :where](https://codepen.io/smashingmag/pen/mdXrBzz) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/mdXrBzz">Nested lists - :where</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<p>Our utility class works without the need for a higher specificity or other overrides! <code>:where</code> sets the specificity of the selectors in the list to zero and allows us to override the default styles.</p>

<p>We can use <code>:is</code> and <code>:where</code> to group multiple selectors into a single expression. With <code>:where</code>, we can set safe default styles with complex selectors which can be easily overridden with simple utility classes without needlessly increasing specificity.</p>

<h2 id="scroll-padding"><code>scroll-padding</code></h2>

<p>One of my pet-peeves, when implementing a fixed page header, used to be how the on-page scroll links cause fixed page header to cover part of the content. We had to use JavaScript to fix this issue and implement custom scroll logic to take into account the fixed header offset. And things would only become more complicated if the header height changed on breakpoints, so we needed to cover those cases with JavaScript, too.</p>

<p>Luckily, we don’t have to rely on JavaScript for that anymore. We can specify <code>scroll-padding-top</code> and change its value using standard CSS media queries.</p>

<pre><code class="language-css">html {
  scroll-padding-top: 6rem;
  scroll-behavior: smooth;
}</code></pre>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="QWQKqzW"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Scroll offset](https://codepen.io/smashingmag/pen/QWQKqzW) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/QWQKqzW">Scroll offset</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<p>We can also set other directions or use a longhand <code>scroll-padding</code>.</p>

<pre><code class="language-css">scroll-padding: /&#42; ... &#42;/;

scroll-padding-top: /&#42; ... &#42;/;
scroll-padding-right: /&#42; ... &#42;/;
scroll-padding-bottom: /&#42; ... &#42;/;
scroll-padding-left: /&#42; ... &#42;/;</code></pre>

<h2 id="font-rendering-options">Font Rendering Options</h2>

<p>I’ve recently worked on animating numeric values on a project where a value would increment from zero to a final value. I’ve noticed that the text kept jumping left and right during the animation due to individual characters having different widths.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/643992dc-a2da-4f97-b6dd-cef517ea085e/2-lesser-known-underused-css-features-2022.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="501"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/643992dc-a2da-4f97-b6dd-cef517ea085e/2-lesser-known-underused-css-features-2022.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/643992dc-a2da-4f97-b6dd-cef517ea085e/2-lesser-known-underused-css-features-2022.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/643992dc-a2da-4f97-b6dd-cef517ea085e/2-lesser-known-underused-css-features-2022.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/643992dc-a2da-4f97-b6dd-cef517ea085e/2-lesser-known-underused-css-features-2022.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/643992dc-a2da-4f97-b6dd-cef517ea085e/2-lesser-known-underused-css-features-2022.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/643992dc-a2da-4f97-b6dd-cef517ea085e/2-lesser-known-underused-css-features-2022.png"
			
			sizes="100vw"
			alt="Numeric values in Fira Sans font"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Notice how Fira Sans font has different character widths for numeric values. (Second row has one extra character) (<a href='https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/643992dc-a2da-4f97-b6dd-cef517ea085e/2-lesser-known-underused-css-features-2022.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>I assumed that this issue cannot be fixed, and I moved on. One of the tweets from the community poll suggested that I should look into <code>font-variant-numeric: tabular-nums</code>, and I was surprised to find a plethora of options that affect font rendering.</p>

<p>For example, <code>tabular-nums</code> fixed the aforementioned issue by setting the equal width for all numeric characters.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="ZErpayJ"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [font-variant-numeric](https://codepen.io/smashingmag/pen/ZErpayJ) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/ZErpayJ">font-variant-numeric</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<p>Please note that <strong>available features depend on the font itself, and some features might not be supported.</strong> For a complete list of options, consult the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant-numeric">documentation</a>. There is also a <code>font-variant</code> <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant">CSS property</a> that allows us to activate even more features for all characters, not just the numeric.</p>

<p>Here are a few more examples of <code>font-variant-numeric</code> that are available in the font Source Sans 3.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="ExQgbvE"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [font-variant](https://codepen.io/smashingmag/pen/ExQgbvE) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/ExQgbvE">font-variant</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<h2 id="creating-stacking-context-with-isolate">Creating Stacking Context With <code>isolate</code></h2>

<p>This property may be confusing to developers, and I wasn’t aware of it until I read <a href="https://www.joshwcomeau.com/css/stacking-contexts/#airtight-abstractions-with-isolation">Josh W. Comeau’s awesome article</a> on the topic of <code>z-index</code> and stacking contexts. In short, it allows us to compartmentalize our <code>z-index</code> stacks.</p>

<p>You probably ran into a case where you, for example, added a reusable tooltip component to your page, only to find out that the tooltip element has a <code>z-index</code> lower than some other adjacent element on the page, causing the tooltip to display below it. We would usually solve it by increasing the <code>z-index</code> value of the tooltip, but that could potentially cause regressions and similar issues somewhere else in the projects.</p>

<p>This is exactly what happens in the example below. The tooltip is locked in a hovered state for demo purposes.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="ZErpaXX"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [stacking context - no isolate](https://codepen.io/smashingmag/pen/ZErpaXX) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/ZErpaXX">stacking context - no isolate</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<p>Let’s see what is going on here. A developer made a styled title component that has a decorative element behind it, as defined in a design. But they went overboard with <code>z-index</code> values:</p>

<ul>
<li>title text has <code>z-index: 2</code>;</li>
<li>decorative background element has a <code>z-index: 1</code>.</li>
</ul>

<p>This component works as expected and was merged with a main codebase. After some time had passed, someone else made a tooltip component with a <code>z-index: 1</code>. There is no reason to assign a higher value to <code>z-index: 1</code>, as the tooltip needs to be just above the text. After a while, an edge case happened where title text ended up above the tooltip.</p>

<p>We could mess around with <code>z-index</code> values for title component and tooltip component or assign a <code>z-index</code> to their respective parent elements with <code>position: relative</code> to create a new stacking context, but we are relying on magic numbers!</p>

<p>Let’s think about the issue differently — what if we could create a new stacking context without relying on <code>z-index</code> magic numbers? This is exactly what <code>isolation: isolate</code> does! It creates a new stacking context or a group. It tells the browser not to mix these two stacking groups, not even if we increase title <code>z-index</code> value to highest possible value. So, we can keep the <code>z-index</code> values low and not worry if value should be 2, 10, 50, 100, 999999, etc.</p>

<p>Let’s create a new stacking context at the root of our title component and at the root of our tooltip component and see what happens.</p>

<pre><code class="language-css">.title {
  isolation: isolate;
  /&#42; ... &#42;/
}

.tooltip-root {
  isolation: isolate;
  /&#42; ... &#42;/
}</code></pre>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="oNEzooJ"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [stacking context - isolate](https://codepen.io/smashingmag/pen/oNEzooJ) by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/oNEzooJ">stacking context - isolate</a> by <a href="https://codepen.io/AdrianBece">Adrian Bece</a>.</figcaption>
</figure>

<p>And we fixed the issue by isolating the stacking contexts for our two conflicting components without messing around with magic numbers for <code>z-index</code> values.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/8d7cb6ce-3084-4c28-ba42-bebb7beecb11/3-lesser-known-underused-css-features-2022.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="297"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/8d7cb6ce-3084-4c28-ba42-bebb7beecb11/3-lesser-known-underused-css-features-2022.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/8d7cb6ce-3084-4c28-ba42-bebb7beecb11/3-lesser-known-underused-css-features-2022.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/8d7cb6ce-3084-4c28-ba42-bebb7beecb11/3-lesser-known-underused-css-features-2022.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/8d7cb6ce-3084-4c28-ba42-bebb7beecb11/3-lesser-known-underused-css-features-2022.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/8d7cb6ce-3084-4c28-ba42-bebb7beecb11/3-lesser-known-underused-css-features-2022.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/8d7cb6ce-3084-4c28-ba42-bebb7beecb11/3-lesser-known-underused-css-features-2022.png"
			
			sizes="100vw"
			alt="Screenshot with the visualisation of the assigned high value to the title text"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Even if we assign an unnecesarilly high value like 99999 to title text, this value won’t affect another isolated group — tooltip still ends up above the title which makes our components even more robust and reusable. (<a href='https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/8d7cb6ce-3084-4c28-ba42-bebb7beecb11/3-lesser-known-underused-css-features-2022.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h2 id="render-performance-optimization">Render Performance Optimization</h2>

<p>When it comes to rendering performance, it’s very rare to run into these issues when working on regular projects. However, in the case of large DOM trees with several thousands of elements or other similar edge cases, we can run into some performance issues related to CSS and rendering. Luckily, we have a direct way of dealing with these performance issues that cause lag, unresponsiveness to user inputs, low FPS, etc.</p>

<p>This is where <code>contain</code> property comes in. It tells the browser what won’t change in the render cycle, so the browser can safely skip it. This can have consequences on the layout and style, so make sure to test if this property doesn’t introduce any visual bugs.</p>

<div class="break-out">

<pre><code class="language-css">.container {
  /&#42; child elements won't display outside of this container so only the contents of this container should be rendered&#42;/
  contain: paint;
{</code></pre>

</div>

<p>This property is quite complex, and <a href="https://www.smashingmagazine.com/2019/12/browsers-containment-css-contain-property/">Rachel Andrew has covered it</a> in great detail in her article. This property is somewhat difficult to demonstrate, as it is most useful in those very specific edge cases. For example, Johan Isaksson covered one of those <a href="https://medium.com/%40johan.isaksson/how-i-made-googles-data-grid-scroll-10x-faster-with-one-line-of-css-78cb1e8d9cb1">examples in his article</a>, where he noticed a major scroll lag on Google Search Console. It was caused by having over 38 000 elements on a page and was fixed by containing property!</p>

<p>As you can see, <code>contain</code> relies on <strong>the developer knowing exactly which properties won’t change</strong> and knowing how to avoid potential regressions. So, it’s a bit difficult to use this property safely.</p>

<p>However, there is an option where we can signal the browser to apply the required <code>contain</code> value automatically. We can use the <code>content-visibility</code> property. With <a href="https://web.dev/content-visibility/#skipping-rendering-work-with-content-visibility">this property</a>, we can defer the rendering of off-screen and below-the-fold content. Some even refer to this as “lazy-rendering”.</p>

<p>Una Kravets and Vladimir Levin covered this property in their <a href="https://web.dev/content-visibility/#example">travel blog example</a>. They apply the following class name to the below-the-fold blog sections.</p>

<div class="break-out">

<pre><code class="language-javascript">.story {
  content-visibility: auto; /&#42; Behaves like overflow: hidden; &#42;/
  contain-intrinsic-size: 100px 1000px;
}</code></pre>

</div>

<p>With <code>contain-intrinsic-size</code>, we can estimate the size of the section that is going to be rendered. Without this property, the size of the content would be <code>0</code>, and page dimensions would keep increasing, as content is loaded.</p>

<p>Going back to Una Kravets and Vladimir Levin’s travel blog example. Notice how the scrollbar jumps around, as you scroll or drag it. This is because of the difference between the placeholder (estimated) size set with <code>contain-intrinsic-size</code> and the actual render size. If we omit this property, the scroll jumps would be even more jarring.</p>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="jOZMapm"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Content-visibility Demo: Base (With Content Visibility)](https://codepen.io/smashingmag/pen/jOZMapm) by <a href="https://codepen.io/vmpstr">Vladimir Levin</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/jOZMapm">Content-visibility Demo: Base (With Content Visibility)</a> by <a href="https://codepen.io/vmpstr">Vladimir Levin</a>.</figcaption>
</figure>

<p>Thijs Terluin covers <a href="https://www.terluinwebdesign.nl/en/css/calculating-contain-intrinsic-size-for-content-visibility/">several ways of calculating</a> this value including PHP and JavaScript. Server-side calculation using PHP is especially impressive, as it can automate the value estimation on larger set of various pages and make it more accurate for a subset of screen sizes.</p>

<p>Keep in mind that <strong>these properties should be used to fix issues once they happen</strong>, so it’s safe to omit them until you encounter render performance issues.</p>

<h2 id="conclusion">Conclusion</h2>

<p>CSS evolves constantly, with more features being added each year. It’s important to keep up with the latest features and best practices, but also keep an eye out on browser support and use progressive enhancement.</p>

<p>I’m sure there are more CSS properties and selectors that aren’t included here. Feel free to let us know in the comments which properties or selectors are less known or should be used more often, but may be a bit convoluted or there is not enough buzz around them.</p>

<div class="partners__lead-place"></div>

<h3 id="further-reading">Further Reading</h3>

<ul>
<li><a href="https://www.smashingmagazine.com/2025/01/transitioning-top-layer-entries-display-property-css/">Transitioning Top-Layer Entries And The Display Property In CSS</a></li>
<li><a href="https://www.smashingmagazine.com/2024/12/mastering-svg-arcs/">Mastering SVG Arcs</a></li>
<li><a href="https://www.smashingmagazine.com/2024/11/why-optimizing-lighthouse-score-not-enough-fast-website/">Why Optimizing Your Lighthouse Score Is Not Enough For A Fast Website</a></li>
<li><a href="https://www.smashingmagazine.com/2024/05/netlify-platform-primitives/">The Era Of Platform Primitives Is Finally Here</a></li>
</ul>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(vf, yk, il, mrn)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Cosima Mielke</author><title>Magical SVG Techniques</title><link>https://www.smashingmagazine.com/2022/05/magical-svg-techniques/</link><pubDate>Tue, 10 May 2022 10:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2022/05/magical-svg-techniques/</guid><description>SVGs are scalable, flexible, and, most importantly, lightweight. And, well, they have even more to offer than you might think. Smart SVG techniques, from generative SVG grids to SVG paths with masks, grainy SVG gradients, cut-out effects and fractional SVG stars. Let’s look at some magical SVG techniques that you can use right away.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2022/05/magical-svg-techniques/" />
              <title>Magical SVG Techniques</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Magical SVG Techniques</h1>
                  
                    
                    <address>Cosima Mielke</address>
                  
                  <time datetime="2022-05-10T10:00:00&#43;00:00" class="op-published">2022-05-10T10:00:00+00:00</time>
                  <time datetime="2022-05-10T10:00:00&#43;00:00" class="op-modified">2025-10-14T04:02:41+00:00</time>
                </header>
                
                

<p>SVGs have become more and more popular in the past few years. For good reasons. They are scalable, flexible, and, most importantly, lightweight. And, well, they have even more to offer than you might think. We came across some magical SVG techniques recently that we’d love to share with you. From <strong>SVG grids</strong> and <strong>fractional SVG stars</strong> to SVG masks, fancy <strong>grainy SVG gradients</strong>, and handy SVG tools. We hope you’ll find something useful in here.</p>

<p>By the way, a while ago, we also looked at <a href="https://www.smashingmagazine.com/2021/03/svg-generators/">SVG Generators</a> — for everything from shapes and backgrounds to SVG path visualizers, cropping tools, and SVG → JSX generators. If you’re tinkering with SVG, these might come in handy, too.</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="/printed-books/touch-design-for-mobile-interfaces/">Touch Design for Mobile Interfaces</a></strong>, Steven Hoober’s brand-new guide on <strong>designing for mobile</strong> with proven, universal, human-centric guidelines. <strong>400 pages</strong>, jam-packed with in-depth user research and <strong>best practices</strong>.</p>
<a data-instant href="https://www.smashingmagazine.com/printed-books/touch-design-for-mobile-interfaces/" class="btn btn--green btn--large" style="">Jump to table of contents&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="https://www.smashingmagazine.com/printed-books/touch-design-for-mobile-interfaces/" class="feature-panel-image-link">
<div class="feature-panel-image"><picture><source type="image/avif" srcSet="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/14bcab88-b622-47f6-a51d-76b0aa003597/touch-design-book-shop-opt.avif" />
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b14658fc-bb2d-41a6-8d1a-70eaaf1b8ec8/touch-design-book-shop-opt.png"
    alt="Feature Panel"
    width="480"
    height="697"
/>
</picture>
</div>
</a>
</div>
</aside>
</div>

<h2 id="generative-svg-grids">Generative SVG Grids</h2>

<p>Generative art is a wonderful opportunity for everyone who would love to create art but feels more at home in code. Let’s say you want to create <strong>geometric patterns</strong>, for example. Generative art will take away the difficult decisions from you: What shapes do I use? Where do I put them? And what colors should I use? If you want to give it a try, Alex Trost wrote a <a href="https://frontend.horse/articles/generative-grids/">tutorial on creating generative art with SVG grids</a> that is bound to tickle your creativity — and teach you more about SVG.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://frontend.horse/articles/generative-grids/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="479"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/7c3f5704-0a4c-483d-8a63-70601dc84305/generative-grids-opt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/7c3f5704-0a4c-483d-8a63-70601dc84305/generative-grids-opt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/7c3f5704-0a4c-483d-8a63-70601dc84305/generative-grids-opt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/7c3f5704-0a4c-483d-8a63-70601dc84305/generative-grids-opt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/7c3f5704-0a4c-483d-8a63-70601dc84305/generative-grids-opt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/7c3f5704-0a4c-483d-8a63-70601dc84305/generative-grids-opt.png"
			
			sizes="100vw"
			alt="Creating Generative SVG Grids"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Alex Trost shows <a href='https://frontend.horse/articles/generative-grids/'>how to create generative art with SVG grids</a>.
    </figcaption>
  
</figure>

<p>The generative art that Alex creates is a grid of blocks with a random number of rows and columns. Each block has a randomly chosen design and colors from a shared color palette. Alex takes you step by step through the process of coding this piece: from setting up the grid and creating isolated <strong>functions to draw SVGs</strong> to working with color palettes, adding animations, and more. A fun little project — not only if you’re new to generative art and creative coding.</p>

<h2 id="generative-landscape-rolls">Generative Landscape Rolls</h2>

<p>An awe-inspiring project that bridges the gap between a century-old tradition and state-of-the-art coding is <em><a href="https://github.com/LingDong-/shan-shui-inf">{Shan, Shui}</a></em>. Created by Lingdong Huan and inspired by traditional Chinese landscape rolls, it creates procedurally generated, infinitely-scrolling <strong>Chinese landscapes in SVG format</strong>. The mountains and trees in the landscape are modeled from scratch using noise and mathematical functions. Fascinating!</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://github.com/LingDong-/shan-shui-inf">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="433"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/99853108-7dd6-40c2-950b-47d41be709cc/shan-shui-opt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/99853108-7dd6-40c2-950b-47d41be709cc/shan-shui-opt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/99853108-7dd6-40c2-950b-47d41be709cc/shan-shui-opt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/99853108-7dd6-40c2-950b-47d41be709cc/shan-shui-opt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/99853108-7dd6-40c2-950b-47d41be709cc/shan-shui-opt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/99853108-7dd6-40c2-950b-47d41be709cc/shan-shui-opt.png"
			
			sizes="100vw"
			alt="Shan, Shui"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The generative art project <em><a href='https://github.com/LingDong-/shan-shui-inf'>{Shan, Shui}</a></em> is inspired by traditional Chinese landscape paintings.
    </figcaption>
  
</figure>

<p>Now, if you’re asking yourself how something as complex might work, you’re not alone. Victor Shepelev wanted to get behind the secret of {Shan, Shui}* and made it his advent project to understand how it works. And, indeed, it took him 24 days to fully <strong>dig into the code</strong>. He summarized his findings in a <a href="https://zverok.github.io/blog/2021-12-28-grok-shan-shui.html">series of articles</a>.</p>

<h2 id="svg-paths-with-masks">SVG Paths With Masks</h2>

<p>SVGs have a lot of benefits compared to raster images. They are small in size, scalable, animatable, they can be edited with code, and a lot more. You can’t get the textured feel that raster graphics can provide, though. However, we can combine the <strong>strengths of vector and raster</strong> to create some charming effects. Like Tom Miller did in his <a href="https://codepen.io/creativeocean/pen/abLGMwv">Silkscreen Squiggles</a> demo.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://frontend.horse/articles/painting-svg-paths-with-masks/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="477"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/4fe06f6f-6d54-41fe-8d73-5c7ff06a2103/svg-paths-opt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/4fe06f6f-6d54-41fe-8d73-5c7ff06a2103/svg-paths-opt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/4fe06f6f-6d54-41fe-8d73-5c7ff06a2103/svg-paths-opt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/4fe06f6f-6d54-41fe-8d73-5c7ff06a2103/svg-paths-opt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/4fe06f6f-6d54-41fe-8d73-5c7ff06a2103/svg-paths-opt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/4fe06f6f-6d54-41fe-8d73-5c7ff06a2103/svg-paths-opt.png"
			
			sizes="100vw"
			alt="Painting SVG Paths with Masks"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Adding a paintbrush effect to an SVG? A <a href='https://frontend.horse/articles/painting-svg-paths-with-masks/'>little trick</a> makes it possible.
    </figcaption>
  
</figure>

<p>Silkscreen Squiggles is an animation where squiggles fill a rectangular canvas. What makes the squiggles special is that they appear to have a <strong>paintbrush texture</strong>. The secret: a mask with an alpha layer that gives the simple squiggly paths their texture. Alex Trost <a href="https://frontend.horse/articles/painting-svg-paths-with-masks/">dissects how it works</a>. Inspiring!</p>

<h2 id="grainy-gradients">Grainy Gradients</h2>

<p>Noise is a simple technique to <strong>add texture</strong> to an image and make otherwise solid colors or smooth gradients more realistic. But despite designer’s affinity for texture, noise is rarely used in web design. Jimmy Chion explores <a href="https://css-tricks.com/grainy-gradients/">how we can add texture to a gradient</a> with only a small amount of CSS and SVG.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://css-tricks.com/grainy-gradients/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="499"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a7f7f36b-6f4c-43db-8dae-d49471554ce8/holographic-type-opt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a7f7f36b-6f4c-43db-8dae-d49471554ce8/holographic-type-opt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a7f7f36b-6f4c-43db-8dae-d49471554ce8/holographic-type-opt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a7f7f36b-6f4c-43db-8dae-d49471554ce8/holographic-type-opt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a7f7f36b-6f4c-43db-8dae-d49471554ce8/holographic-type-opt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a7f7f36b-6f4c-43db-8dae-d49471554ce8/holographic-type-opt.png"
			
			sizes="100vw"
			alt="Grainy Gradients"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      A fascinating <a href='https://css-tricks.com/grainy-gradients/'>holographic type effect</a> achieved with a grainy SVG gradient.
    </figcaption>
  
</figure>

<p>The trick is to use an SVG filter to create the noise, then apply that noise as a background. Layer it underneath your gradient, boost the brightness and contrast, and that’s already it. Potential use cases could be light and shadows or <strong>holographic foil effects</strong>, for example. The core of this technique is supported by all modern browsers. A clever visual effect to add depth and texture to a design.</p>

<h2 id="adding-texture-and-depth">Adding Texture And Depth</h2>

<p>“Analog” materials like paint and paper naturally add depth to an artwork, but when working digitally, we often sacrifice the <strong>organic depth</strong> they provide for precision and speed. Let’s bring some texture back into our work! George Francis shares <a href="https://georgefrancis.dev/writing/generative-texture/">three ways to do so</a>.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://georgefrancis.dev/writing/generative-texture/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="569"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b9749a39-5afc-4b17-b091-de9f1cb162ac/texture-generative-snacks-opt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b9749a39-5afc-4b17-b091-de9f1cb162ac/texture-generative-snacks-opt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b9749a39-5afc-4b17-b091-de9f1cb162ac/texture-generative-snacks-opt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b9749a39-5afc-4b17-b091-de9f1cb162ac/texture-generative-snacks-opt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b9749a39-5afc-4b17-b091-de9f1cb162ac/texture-generative-snacks-opt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b9749a39-5afc-4b17-b091-de9f1cb162ac/texture-generative-snacks-opt.png"
			
			sizes="100vw"
			alt="Texture Generative Snacks"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      George Francis explores <a href='https://georgefrancis.dev/writing/generative-texture/'>how to create texture and depth</a>.
    </figcaption>
  
</figure>

<p>The techniques that George explores are quite simple but effective. Tiny <strong>random shapes</strong> added to a canvas at random points, solid shape fills with lines, and non-overlapping circles distributed evenly but randomly with an algorithm. Inspiring ideas to tinker with.</p>

<h2 id="cut-out-effects-with-css-and-svg">Cut-Out Effects With CSS And SVG</h2>

<p>In a recent front-end project that Ahmad Shadeed was working on, one of the components included a cut-out effect where an area is cut out of a shape. And because there are multiple ways to create such an effect in CSS or SVG, he decided to explore the <strong>pros and cons</strong> that each of the solutions brings along.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://ishadeed.com/article/thinking-about-the-cut-out-effect/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="501"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/0e4319a1-8dcf-443b-b781-e659380aa800/cut-out-effect-opt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/0e4319a1-8dcf-443b-b781-e659380aa800/cut-out-effect-opt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/0e4319a1-8dcf-443b-b781-e659380aa800/cut-out-effect-opt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/0e4319a1-8dcf-443b-b781-e659380aa800/cut-out-effect-opt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/0e4319a1-8dcf-443b-b781-e659380aa800/cut-out-effect-opt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/0e4319a1-8dcf-443b-b781-e659380aa800/cut-out-effect-opt.png"
			
			sizes="100vw"
			alt="Thinking About The Cut-Out Effect"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      If you want to create a <a href='https://ishadeed.com/article/thinking-about-the-cut-out-effect/'>cut-out effect</a>, Ahmad Shadeed helps you find the best technique for your use case.
    </figcaption>
  
</figure>

<p>In his blog post “<a href="https://ishadeed.com/article/thinking-about-the-cut-out-effect/">Thinking About The Cut-Out Effect</a>”, Ahmad takes a look at three different use cases for a cutout effect: an avatar with a cut-out <strong>status badge</strong> that indicates that a user is currently online, a “seen avatar” that consists of overlapping circle avatars that are indicators that a message has been seen in a group chat, as well as a website header with a cut-out area behind a circular logo. Ahmad presents different solutions for each use case — SVG-only, CSS-only, and a mix of both — and explains the pros and cons of each one of them. A comprehensive overview.</p>

<h2 id="fractional-svg-stars">Fractional SVG Stars</h2>

<p>Are you building a rating component and you want it to support fractional values like 4.2 or 3.7 stars but without using images? Good news, you can achieve <strong>fractional ratings</strong> with only CSS and inline SVG. Samuel Kraft <a href="https://samuelkraft.com/blog/fractional-svg-stars-css">explains how it works</a>.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://samuelkraft.com/blog/fractional-svg-stars-css">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="348"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/7e4ace33-8648-4ec4-a5a9-68988ad459ce/fractional-stars-opt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/7e4ace33-8648-4ec4-a5a9-68988ad459ce/fractional-stars-opt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/7e4ace33-8648-4ec4-a5a9-68988ad459ce/fractional-stars-opt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/7e4ace33-8648-4ec4-a5a9-68988ad459ce/fractional-stars-opt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/7e4ace33-8648-4ec4-a5a9-68988ad459ce/fractional-stars-opt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/7e4ace33-8648-4ec4-a5a9-68988ad459ce/fractional-stars-opt.png"
			
			sizes="100vw"
			alt="Fractional SVG Stars"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      A clever trick to create <a href='https://samuelkraft.com/blog/fractional-svg-stars-css'>fractional rating stars</a> only with CSS and SVG comes from Samuel Kraft.
    </figcaption>
  
</figure>

<p>The component basically consists of two parts: a list of star icons based on the max rating and an “overlay” <code>div</code> that will be responsible for <strong>changing the colors</strong> of the stars underneath. This is the magic that makes the fractional part work. The technique is supported in all modern browsers; for older browsers, you can fall back to opacity instead. Clever!</p>

<h2 id="generative-mountain-ridge-dividers">Generative Mountain Ridge Dividers</h2>

<p>When Alistair Shepherd built his personal website, he wanted to have section dividers that match the mountain theme of the site. But not any mountain dividers, but dividers with <strong>unique ridges</strong> for every divider.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://alistairshepherd.uk/writing/svg-generative-ridges/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="269"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/f16fdf7a-1bc9-4d41-acd1-805f897d5025/generative-mountain-divider-opt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/f16fdf7a-1bc9-4d41-acd1-805f897d5025/generative-mountain-divider-opt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/f16fdf7a-1bc9-4d41-acd1-805f897d5025/generative-mountain-divider-opt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/f16fdf7a-1bc9-4d41-acd1-805f897d5025/generative-mountain-divider-opt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/f16fdf7a-1bc9-4d41-acd1-805f897d5025/generative-mountain-divider-opt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/f16fdf7a-1bc9-4d41-acd1-805f897d5025/generative-mountain-divider-opt.png"
			
			sizes="100vw"
			alt="SVG generative mountain ridge dividers"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Alistair Shepherd created <a href='https://alistairshepherd.uk/writing/svg-generative-ridges/'>generative SVG mountain ridge dividers</a>.
    </figcaption>
  
</figure>

<p>Instead of creating a variety of different dividers manually, Alistair decided to use a combination of SVG and terrain generation, a technique that is usually used in <strong>game development</strong>, to generate the dividers automatically. In a <a href="https://alistairshepherd.uk/writing/svg-generative-ridges/">blog post</a>, he explains how it works.</p>

<p>If you’re up for some more horizontal divider inspiration, also be sure to check out Sara Soueidan’s blog post “<a href="https://www.sarasoueidan.com/blog/horizontal-rules/">Not Your Typical Horizontal Rules</a>” in which she shows how she turned a boring horizontal line into a cute “<strong>birds on a wire</strong>” divider with the help of some CSS and SVG.</p>

<div class="partners__lead-place"></div>

<h2 id="flexible-repeating-svg-masks">Flexible Repeating SVG Masks</h2>

<p>Sometimes it’s a small idea, a little detail in a project that you tinker with and that you can’t let go off until you come up with a tailor-made solution to make it happen. Nothing that seems like a big deal at first glance, but that requires you to think outside the box. In Tyler Gaw’s case, this little detail was a <strong>flexible header</strong> with a little squiggle at the bottom instead of a straight line. The twist: to make the component future-proof, Tyler wanted to use a seamless, horizontal repeating pattern that he could color with CSS.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://tylergaw.com/articles/css-repeating-svg-masks/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="447"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/58c7f164-9648-4cb0-8e17-eeca66eee1ad/banner-squiggles-opt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/58c7f164-9648-4cb0-8e17-eeca66eee1ad/banner-squiggles-opt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/58c7f164-9648-4cb0-8e17-eeca66eee1ad/banner-squiggles-opt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/58c7f164-9648-4cb0-8e17-eeca66eee1ad/banner-squiggles-opt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/58c7f164-9648-4cb0-8e17-eeca66eee1ad/banner-squiggles-opt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/58c7f164-9648-4cb0-8e17-eeca66eee1ad/banner-squiggles-opt.png"
			
			sizes="100vw"
			alt="Flexible Repeating SVG Masks"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Tyler Gaw’s <a href='https://tylergaw.com/articles/css-repeating-svg-masks/'>banner squiggles</a> are a great base for some fun experiments.
    </figcaption>
  
</figure>

<p>To get the job done, Tyler settled on <a href="https://tylergaw.com/articles/css-repeating-svg-masks/">flexible repeating SVG masks</a>. SVG <strong>provides the shape</strong>, CSS handles the color, and <code>mask-image</code> does the heavy lifting by hiding anything in the underlying <code>div</code> that doesn’t intersect with the shape. A clever approach that can be used as the base for some fun experiments.</p>

<h2 id="swipey-image-grids">Swipey Image Grids</h2>

<p>When you think of “SVG animation”, what comes to your mind? Illustrative animation? Well, SVG can be useful for much more than pretty graphics. As Cassie Evans points out, a whole new world of <strong>UI styling</strong> opens up once you stop looking at SVG purely as a format for illustrations and icons. One of her favorite use cases for SVG: <a href="https://www.cassie.codes/posts/swipey-image-grids/">responsive animated image grids</a>.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://www.cassie.codes/posts/swipey-image-grids/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="505"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/1a60334e-e053-484c-9da7-6dde0a1f86d2/swipey-image-grids-opt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/1a60334e-e053-484c-9da7-6dde0a1f86d2/swipey-image-grids-opt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/1a60334e-e053-484c-9da7-6dde0a1f86d2/swipey-image-grids-opt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/1a60334e-e053-484c-9da7-6dde0a1f86d2/swipey-image-grids-opt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/1a60334e-e053-484c-9da7-6dde0a1f86d2/swipey-image-grids-opt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/1a60334e-e053-484c-9da7-6dde0a1f86d2/swipey-image-grids-opt.png"
			
			sizes="100vw"
			alt="Swipey Image Grids"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Cassie Evans uses SVG’s internal coordinate system to create a <a href='https://www.cassie.codes/posts/swipey-image-grids/'>swipey image grid</a>.
    </figcaption>
  
</figure>

<p>Cassie doesn’t build her image grid on CSS Grid but uses SVG’s <strong>internal coordinate system</strong> (which is responsive by design) to design the grid layout. She then adds images to the grid and positions them with <code>preserveAspectRatio</code>. <code>clipPath</code> “swipes” the images in. The final animation relies on GreenSock to ensure that the transforms work consistently across browsers. If you want to dig deeper into the code, be sure to check out Cassie’s <a href="https://www.cassie.codes/posts/swipey-image-grids/">blog post</a> in which she explains each step in detail.</p>

<h2 id="animated-svg-debit-card-illustrations">Animated SVG Debit Card Illustrations</h2>

<p>What if you could animate a debit card design? Probably not on an actual physical card, but rather for a landing page where you’d like to drive interest towards the card’s <strong>design or features</strong>? Well that’s an unusual challenge to tackle, and Tom Miller decided to take it on.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://codepen.io/collection/MgYZwW">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="459"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6be13a6e-5850-49e2-82b0-89d47212c66c/animated-debit-card-opt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6be13a6e-5850-49e2-82b0-89d47212c66c/animated-debit-card-opt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6be13a6e-5850-49e2-82b0-89d47212c66c/animated-debit-card-opt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6be13a6e-5850-49e2-82b0-89d47212c66c/animated-debit-card-opt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6be13a6e-5850-49e2-82b0-89d47212c66c/animated-debit-card-opt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6be13a6e-5850-49e2-82b0-89d47212c66c/animated-debit-card-opt.png"
			
			sizes="100vw"
			alt="Animated SVG Debit Card Illustrations"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The <a href='https://codepen.io/collection/MgYZwW'>animated SVG illustrations</a> that Tom Miller created bring debit cards to life.
    </figcaption>
  
</figure>

<p>In a series of <a href="https://codepen.io/collection/MgYZwW">SVG debit card animations</a>, Tom uses GreenSock to <strong>animate SVG paths and shapes</strong> smoothly, so every card literally comes to life on its own, transforming, rotating, and scaling beautifully, alongside just a few lines of JavaScript. A wonderful inspiration for your next landing page design!</p>

<h2 id="raster-image-to-svg-converter">Raster Image To SVG Converter</h2>

<p>You need to quickly convert a raster image into an SVG? Then <a href="https://svgco.de/">SVGcode</a> is for you. The progressive web app converts image formats like JPG, PNG, GIF, WebP, and AVIF to vector graphics in SVG format.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://svgco.de/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="518"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a6434f1f-f711-472e-89a1-fea1a40edd78/svgcode-opt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a6434f1f-f711-472e-89a1-fea1a40edd78/svgcode-opt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a6434f1f-f711-472e-89a1-fea1a40edd78/svgcode-opt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a6434f1f-f711-472e-89a1-fea1a40edd78/svgcode-opt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a6434f1f-f711-472e-89a1-fea1a40edd78/svgcode-opt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a6434f1f-f711-472e-89a1-fea1a40edd78/svgcode-opt.png"
			
			sizes="100vw"
			alt="SVGcode"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      <a href='https://svgco.de/'>SVGcode</a> turns raster images into vector images.
    </figcaption>
  
</figure>

<p>To convert an image, drop your raster image into the SVGcode app, and the app will trace the image, color by color, until a vectorized version of the input appears. You can choose between color SVG and monochrome SVG and there also are a number of <strong>customization settings</strong> to improve the output further, by suppressing speckles and adjusting the color, for example. If you install the PWA, you can even use it as a default file handler on your machine. A real timesaver.</p>

<h2 id="download-svgs-from-any-site">Download SVGs From Any Site</h2>

<p>A handy little tool to enhance your SVG workflow is <a href="https://www.svggobbler.com/">SVG Gobbler</a>. The browser extension finds the vector content on the page you’re viewing and gives you the option to download, optimize, copy, <strong>view the code</strong>, or export it as an image.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://www.svggobbler.com/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="485"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b6930c11-dd66-4b3e-b3d3-8c4a2f88fe1f/svg-gobbler-opt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b6930c11-dd66-4b3e-b3d3-8c4a2f88fe1f/svg-gobbler-opt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b6930c11-dd66-4b3e-b3d3-8c4a2f88fe1f/svg-gobbler-opt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b6930c11-dd66-4b3e-b3d3-8c4a2f88fe1f/svg-gobbler-opt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b6930c11-dd66-4b3e-b3d3-8c4a2f88fe1f/svg-gobbler-opt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/b6930c11-dd66-4b3e-b3d3-8c4a2f88fe1f/svg-gobbler-opt.png"
			
			sizes="100vw"
			alt="SVG Gobbler"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      <a href='https://www.svggobbler.com/'>SVG Gobbler</a> makes it easy to download SVGs from any site.
    </figcaption>
  
</figure>

<p>When you click the browser extension, it shows you all SVGs detected on the site. You can quickly download the ones you like or copy them to your clipboard. When you view the code, you can toggle <strong>optimization options</strong> from SVGO — to beautify the markup or clean up attributes or numeric values, for example. And if you need a PNG version of an SVG, you can export it in any size you want. A fantastic addition to any developer’s toolkit.</p>

<h2 id="scaling-svgs-made-simple">Scaling SVGs Made Simple</h2>

<p>Scaling <code>svg</code> elements can be a daunting task, since they act very differently than normal images. Amelia Wattenberger came up with an <a href="https://2019.wattenberger.com/guide/scaling-svg">ingenious comparison</a> to help us make sense of SVGs and their special features: “The <code>svg</code> element is a <strong>telescope</strong> into another world.”</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://2019.wattenberger.com/guide/scaling-svg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="541"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/53b477a1-4d91-4f1c-af48-5f2f781b716c/scaling-svg-opt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/53b477a1-4d91-4f1c-af48-5f2f781b716c/scaling-svg-opt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/53b477a1-4d91-4f1c-af48-5f2f781b716c/scaling-svg-opt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/53b477a1-4d91-4f1c-af48-5f2f781b716c/scaling-svg-opt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/53b477a1-4d91-4f1c-af48-5f2f781b716c/scaling-svg-opt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/53b477a1-4d91-4f1c-af48-5f2f781b716c/scaling-svg-opt.png"
			
			sizes="100vw"
			alt="SVG Gobbler"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Imagine the <code>svg</code> element as a <a href='https://2019.wattenberger.com/guide/scaling-svg'>telescope into another world</a>, and scaling will become a lot easier.
    </figcaption>
  
</figure>

<p>Based on the idea of the telescope, Amelia explains how to use the <code>viewBox</code> property to zoom in or out with your “telescope”, and, thus, change the size of your <code>&lt;svg&gt;</code>. A small tip that works wonders.</p>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>We hope that these techniques will tickle your curiosity and inspire you to try some SVG magic yourself. If <em>you</em> came across an interesting SVG technique that left you in awe, please don’t hesitate to share it in the comments below. We’d love to hear about it. Happy creating!</p>

<h3 id="more-on-svg">More On SVG</h3>

<ul>
    <li><a href="https://www.smashingmagazine.com/2021/03/svg-generators/">SVG Generators</a></li>
    <li><a href="https://www.smashingmagazine.com/2019/05/svg-design-tools-practical-guide/">A Practical Guide To SVG And Design Tools</a></li>
    <li><a href="https://www.smashingmagazine.com/2019/03/svg-circle-decomposition-paths/">SVG Circle Decomposition To Paths</a></li>
    <li><a href="https://www.smashingmagazine.com/2021/05/accessible-svg-patterns-comparison/">Accessible SVGs: Perfect Patterns For Screen Reader Users</a></li>
    <li>Also, <a href="/the-smashing-newsletter/">subscribe to our newsletter</a> to not miss the next ones.</li>
</ul>

<p><style>
    .nl-box__form {
  display: flex;
  padding-bottom: 0.5em;
  text-align: center;
  letter-spacing: -0.5px;
  color: #ffffff;
  font-size: 1.15em;
}</p>

<p>.nl-box<strong>form .nl-box</strong>form&ndash;button,
.nl-box<strong>form .nl-box</strong>form&ndash;email {
  flex-grow: 1;
  flex-shrink: 0;
  box-sizing: border-box;
  width: auto;
  margin: 0;
  padding: 0.75em 1em;
  border: 0;
  border-radius: 11px;
  background: #ffffff;
  font-size: 1em;
}</p>

<p>input.nl-box<strong>form&ndash;email:active,
input.nl-box</strong>form&ndash;email:focus,
.nl-box<strong>form&ndash;button:active,
.nl-box</strong>form&ndash;button:focus {
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
}</p>

<p>.nl-box<strong>form&ndash;button:-ms-input-placeholder,
.nl-box</strong>form&ndash;email:-ms-input-placeholder {
  color: #777777;
  font-style: italic;
}</p>

<p>.nl-box<strong>form&ndash;email::placeholder,
.nl-box</strong>form&ndash;button::placeholder {
  color: #777777;
  font-style: italic;
}</p>

<p>.nl-box<strong>form .nl-box</strong>form&ndash;button {
  transition: all 0.2s ease-in-out;
  color: #ffffff;
  background-color: #0168b8;
  font-family: Mija, -apple-system, Arial, BlinkMacSystemFont, &ldquo;Roboto Slab&rdquo;,
    &ldquo;Droid Serif&rdquo;, &ldquo;Segoe UI&rdquo;, Ubuntu, Cantarell, Georgia, serif;
  font-weight: 700;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
  width: 100%;
  border: 0;
  border-left: 1px solid #ddd;
  flex: 2;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}</p>

<p>.nl-box<strong>form .nl-box</strong>form&ndash;email {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  width: 100%;
  flex: 4;
}</p>

<p>.nl-box__img {
  height: auto;
  width: 100%;
}</p>

<p>@media all and (max-width: 650px) {
  .nl-box<strong>form .nl-box</strong>group {
    flex-wrap: wrap;
    box-shadow: none;
  }
  .nl-box<strong>form .nl-box</strong>form&ndash;email,
  .nl-box<strong>form .nl-box</strong>form&ndash;button {
    border-radius: 11px;
    border-left: none;
  }</p>

<p>.cards<strong>grid {
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  }
  .nl-box</strong>form .nl-box<strong>form&ndash;email {
    box-shadow: 0 13px 27px -5px rgba(50, 50, 93, 0.25),
      0 8px 16px -8px rgba(0, 0, 0, 0.3), 0 -6px 16px -6px rgba(0, 0, 0, 0.025);
    min-width: 100%;
  }
  .nl-box</strong>form .nl-box__form&ndash;button {
    margin-top: 1em;
    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.5);
  }
}</p>

<p>.nl-box<strong>form .nl-box</strong>form&ndash;button:active,
.nl-box<strong>form .nl-box</strong>form&ndash;button:focus,
.nl-box<strong>form .nl-box</strong>form&ndash;button:hover {
  cursor: pointer;
  color: #ffffff;
  background-color: #0168b8;
  border-color: #dadada;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
}</p>

<p>.nl-box<strong>form .nl-box</strong>form&ndash;button:focus,
.nl-box<strong>form .nl-box</strong>form&ndash;button:active {
  outline: none !important;
  text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3);
  box-shadow: inset 0 3px 3px rgba(0, 0, 0, 0.3);
}</p>

<p>.nl-box__group {
  display: flex;
  box-shadow: 0 13px 27px -5px rgba(50, 50, 93, 0.25),
    0 8px 16px -8px rgba(0, 0, 0, 0.3), 0 -6px 16px -6px rgba(0, 0, 0, 0.025);
  border-radius: 11px;
}</p>

<p>.nl-box__wrapper {
  display: flex;
  flex-direction: column;
  justify-content: center;
}</p>

<p>.nl-box__form form {
  width: 100%;
}</p>

<p>.nl-box<strong>form .nl-box</strong>group {
  margin: 0;
}</p>

<p>.nl-box__caption {
  font-size: 0.9em;
  line-height: 1.5em;
  color: #fff;
  border-radius: 11px;
  padding: 0.5em 1em;
  display: inline-block;
  background-color: #0067b859;
  text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3);
}</p>

<p>.nl-box {
  margin: 1.5em 0;
  padding: 1em 0;
  box-shadow: none;
  max-width: 750px;
  justify-self: center;
}</p>

<p>.nl-box__blue {
  background-color: #1b71bb;
  background-image: linear-gradient(#1b71bb 60%, #01a6c1 100%);
}</p>

<p>.nl-box__desc {
  padding: 0.5rem 2rem 1rem 2rem;
}</p>

<p>.nl-box__image {
  width: 100%;
  height: auto;
}</p>

<p>@media screen and (min-width: 48em) {
  .nl-box__desc {
    padding: 0.5rem calc(2rem + 0.5vw) 1rem calc(2rem + 0.5vw);
  }
}</p>

<p>.nl-box__desc&ndash;heading-link {
  color: #fff;
  text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.9);
}</p>

<p>.nl-box__summary {
  border-bottom: 0;
  color: #fff;
  font-style: normal;
  text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.4);
}</p>

<p>.promo-box&ndash;blue {
  &ndash;promo-background: #e7f8ff;
  &ndash;promo-text: #000;
  &ndash;promo-highlight-text: #e7f8ff;
  &ndash;promo-highlight: #006fc6;
  &ndash;promo-highlight&ndash;hover: #006fc6;
  }</p>

<p>.promo-box {
  background: var(&ndash;promo-background);
  color: var(&ndash;promo-text);
  position: relative;
  padding: 125px 1.5em 2em 1.5em;
  margin-top: 125px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border-radius: 11px;
  width: 100%
}</p>

<p>.promo-box__image-link {
  position: absolute;
  display: block;
  top: 0;
  padding: 0;
  left: 50%;
  transform: translate(-50%,-50%);
  width: 250px;
  height: 250px;
  text-decoration: none;
  background: 0 0
}</p>

<p>.promo-box__image {
  width: 100%;
  height: 100%
}</p>

<p>.promo-box__cta {
  background: #fff;
  color: #d33a2c;
  text-decoration: none;
  padding: .5em .8em;
  border-radius: 11px;
  box-shadow: 0 0 1px 1px rgba(0,0,0,.15);
  background-image: none;
  font-weight: 700;
  font-size: 1.2em;
  margin: 0;
  position: relative;
  box-shadow: 0 2px 6px 0 rgba(0,0,0,.12);
  transition: background .4s ease-in-out, color .4s ease-in-out;
}</p>

<p>.promo-box<strong>cta:active,
.promo-box</strong>cta:focus {
  outline: 0!important;
  background: #fff;
  text-shadow: none;
  box-shadow: inset 0 3px 3px rgba(0,0,0,.3)
}</p>

<p>.promo-box__heading {
  line-height: 1.2;
  font-size: 1.5em;
  font-weight: 700;
  margin: 1.25em 0 0 0;
}</p>

<p>.promo-box__button {
  background: var(&ndash;promo-highlight);
  border-radius: 11px;
  padding: 0.8em 1em;
  font-size: 1.15em;
  text-shadow: 1px 1px 1px rgba(0,0,0,.3);
  text-decoration: none;
  color: white;
  font-weight: bold;
  display: flex;
  width: 100%;
  justify-content: center;
  transition: all .2s ease-in-out;
}</p>

<p>.promo-box<strong>button:active,
.promo-box</strong>button:focus,
.promo-box__button:hover {
  border-bottom: none;
  cursor: pointer;
  border-color: #dadada;
}</p>

<p>.promo-box<strong>button:active,
.promo-box</strong>button:focus {
  outline: 0!important;
  box-shadow: inset 0 3px 3px rgba(0,0,0,.3)
}
</style></p>

<p><div class="promo-box promo-box--blue mbl">
    <a href="https://www.smashingmagazine.com/the-smashing-newsletter/" class="promo-box__image-link" tabindex="-1"><img src="/images/smashing-cat/cat-in-the-chair.svg" class="promo-box__image" width="258" height="290" alt="The Smashing Cat exploring new insights, at Smashing Workshops, of course."></a><p class="promo-box__heading">Useful front-end &amp; UX bits, delivered once a week.</p><p>With tools to help you get your work done better. Subscribe and get Vitaly’s <a href="https://www.smashingmagazine.com/2020/03/smart-interface-design-patterns-checklists/"><strong>Smart Interface Design Checklists PDF</strong></a> via email. 🎁</p><section class="nl-box__form"><form action="//smashingmagazine.us1.list-manage.com/subscribe/post?u=16b832d9ad4b28edf261f34df&amp;id=a1666656e0" method="post"><div class="nl-box__wrapper"><label for="mce-EMAIL-hp" class="sr-only">Your (smashing) email</label><div class="nl-box__group"><input type="email" name="EMAIL" class="nl-box__form--email" id="mce-EMAIL-hp" placeholder="Your email">
    <input type="submit" value="Meow!" name="subscribe" class="nl-box__form--button"></div></div></form></section><p class="mtn mbn"><small class="promo-box__footer mtm block grey"><em>On <a href="https://www.smashingmagazine.com/the-smashing-newsletter/">front-end &amp; UX</a>. Trusted by 207,000+ folks.</em></small></p></div></p>

<div class="partners__lead-place"></div>

<h3 id="further-reading">Further Reading</h3>

<ul>
<li><a href="https://www.smashingmagazine.com/2024/12/mastering-svg-arcs/">Mastering SVG Arcs</a></li>
<li><a href="https://www.smashingmagazine.com/2023/07/sustainable-design-toolkits-and-resources/">Sustainable Design Toolkits And Frameworks</a></li>
<li><a href="https://www.smashingmagazine.com/2022/09/ux-checklists-for-interface-designers/">UX Checklists For Interface Designers</a></li>
<li><a href="https://www.smashingmagazine.com/2023/02/putting-gears-motion-animating-cars-with-html-svg/">Putting Gears In Motion: Animating Cars With HTML And SVG</a></li>
</ul>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(mrn)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item></channel></rss>