Grow your CSS skills. Land your dream job.

Swapping Out SVG Icons

Published by Chris Coyier

Let's say you're using inline SVG and want to change the SVG icon that is displayed in an element on state change, like changing a class or on :hover/:focus. There are a number of ways you can approach that.

Technique #1: Hide/Show SVG Element

Include both icons:

<a href="#0" class="expand-link">
  <svg class="icon icon-expand" viewBox="0 0 32 32"><use xlink:href="#icon-expand"></use></svg>
  <svg class="icon icon-contract" viewBox="0 0 32 32"><use xlink:href="#icon-contract"></use></svg>
</a>

Hide one by default:

.expand-link .icon-contract {
  display: none;
}

When the state changes, swap the display property:

.expand-link:hover .icon-expand,
.expand-link:active .icon-expand{
  display: none;
}
.expand-link:hover .icon-contract,
.expand-link:active .icon-contract{
  display: block;
}

See the Pen praEH by Chris Coyier (@chriscoyier) on CodePen.

Technique #2: Hide/Show Paths

You won't be able to use the <use> technique with this (because a state change on an HTML element happens at a level above the shadow root the <use> creates, thus CSS can't penetrate through) but you can hide/show shape elements directly if the inline SVG is just right in there raw:

<a href="#0"class="icon-expand-link">
  <svg ... >
    <path class="expand" d="M32 0v12l-etc"></path>
    <path class="contract" d="M2 18h12v12l-etc"></path>
  </svg>
</a>
.icon-expand-link .contract {
  display: none;
}
.icon-expand-link:hover .expand {
  display: none;
}
.icon-expand-link:hover .contract {
  display: block;
}

See the Pen cCiBn by Chris Coyier (@chriscoyier) on CodePen.

Technique #3: Alter the xlink:href

Using JavaScript, you could alter the chunk of SVG that is referenced in the <use>. Seems weird to do styling things with JavaScript, but, not having duplicated markup is nice, and if you reference a <symbol>, the <title> and <desc> would change too which is nice.

<a href="#0" class="expand-link">
  <svg class="icon icon-expand" viewBox="0 0 32 32"><use id="target" xlink:href="#icon-expand"></use></svg>
</a>
$(".expand-link")
  .on("mouseenter", function() {
    $(this).find("use").attr("xlink:href", "#icon-contract");
  })
  .on("mouseleave", function() {
    $(this).find("use").attr("xlink:href", "#icon-expand");
  });

See the Pen Dqpib by Chris Coyier (@chriscoyier) on CodePen.

Technique #4: Restyle with CSS

Remember you can do a lot with CSS as well. Perhaps you don't need an entirely different set of shapes, but you can just change things with CSS to essentially make a new icon.

If you don't set styles on anything inside the SVG, you can set them directly on the SVG (essentially crossing the shadow DOM boundary). Plus you can do CSS transforms and such to rotate/scale/move:

See the Pen JFjdl by Chris Coyier (@chriscoyier) on CodePen.

Not using inline SVG?

If you aren't using inline SVG, but instead using SVG-as-<img>, this hide-show stuff or altering the source should work fine.

If you're using SVG as background-image, changing the source is also an option. Also know that even if you use Data URI's in your CSS for multiple versions of an icon, that can seem bloat-y, but if they are pretty similar GZIP can do great work on that.

Comments

  1. Permalink to comment#

    Hi Chris

    I really appreciate your blob post, thanks for this. Can you tell me why something only visual and presentational should even be represented in the markup and why not use the SVG as background image via style? Don’t get me wrong, I love inline svg because graphics / illustrations also have their semantics and styling them / applying behavior in the DOM is awesome! But for me icons dont fall into this category. Id rather go with a link and afaik you can also specify the SVG fragment ID in the background image url right? This way you can use a different fragment for each state and only use CSS to dictate the appearance of the icon link.

    Cheers
    Gion

  2. Lisa
    Permalink to comment#

    Why don’t just use classes on the “ element?

    example code:

    <a href="#0" rel="nofollow">
    
    
    
    
    </a>
    
    

  3. Permalink to comment#

    I have recently been so focused on using just the on hover and display properties that I never realized you could do this with Javascript and paths as well. Thanks for the tip!

  4. Thank you very much Chris for your contribution. I’m keen to know if this also works well on external svgs and resources.

  5. Hi,
    I am a bit confused about how to use that with svg as background. I though css would not apply in that case?

    Could you (or anybody) provide a quick example? Thank you

  6. mvaneijgen
    Permalink to comment#

    In this example i couldn’t understand why not have four separated paths in one SVG and just have them rotate, but then i tried it out and you can’t reference inside the use element.

    Is there any way around this? i Really like to design a menu button like this

    and have it change shapes when opening or closing the menu.

  7. Permalink to comment#

    I find switching the source out in CSS using in HTML works great. And if you have a transition property going it’ll even smoothly transition from one state to another.

Leave a Comment

Posting Code

  • Use Markdown, and it will escape the code for you, like `<div class="cool">`.
  • Use triple-backticks for blocks of code.
    ``` 
    <div>
      <h1>multi-line block of code</h1>
      <span>be cool yo.</span>
    </div>
    ```
  • Otherwise, escape your code, like <code>&lt;div class="cool"&gt;</code>. Markdown is just easier though.

Current ye@r *

*May or may not contain any actual "CSS" or "Tricks".