SVG Tabs (Using an SVG Shape as Template)

Avatar of Chris Coyier
Chris Coyier on (Updated on )

An excellent feature of SVG is that you can define a shape (or set of shapes) once, and then use it multiple times throughout a page. You can even apply different fills and filters and whatnot to the different versions. SVG templating, if you will.

Let’s see if we can put that to good use by creating some tabs.

I learned this was possible through Oleg Solomka’s article on Codrops. Clever stuff!

It works like this:

  1. Define the shape (could be a circle or rect or polygon or any of the shapes, or a group of them wrapped in a <g>).
  2. Give that shape an ID.
  3. Hide that shape.
  4. Use the shape where needed with <use>.

That looks like this in HTML:

<!-- TEMPLATE -->
<svg width="100px" height="100px" viewBox="0 0 100 100" class="visually-hidden">
  <circle id="template" cx="50" cy="50" r="50">
</svg>
  
<!-- USE TEMPLATE SEVERAL TIMES -->
<svg viewBox="0 0 100 100" class="circle circle-1">
  <use xlink:href="#template">
</svg>
  
<svg viewBox="0 0 100 100" class="circle circle-2">
  <use xlink:href="#template">
</svg>
  
<svg viewBox="0 0 100 100" class="circle circle-3">
  <use xlink:href="#template">
</svg>

You can then reference those different uses of the template individually and do whatever you like to them. For instance use SVG gradient fills or SVG filters or whatever.

You can hide that template by positioning it off the page or display: none;ing. I’d probably avoid trying to use it directly just to keep a nice separation of concerns.

Demo:

See the Pen Basic SVG Templating by Chris Coyier (@chriscoyier) on CodePen

Creating Tabs

I just got some paperwork back from a lawyer and of course it’s all tabbed out with that classic file folder design.

That kind of shape has been attempted before with CSS. Round out tabs deals with the tapering of the shape down into the rest of the folder. Slanted tabs deals with the angled sides. But neither of them are perfect, it would be difficult or impossible to combine them, and, while clever, they are a bit “hacky” with the use of generated content and such.

Wouldn’t it be easier to just draw the dang shape as a vector and use it? Absolutely, that’s perfect SVG territory. And better, we can just use a template to define it once and use it over and over. That allows us to change the color and such without have to request a new asset, as well as look sharp no matter what size we end up using it at.

You could easily draw that shape in any vector drawing tool and snag the SVG code for it. If you need help with that, this article should be useful.

I went poking about stock photography sites to find some, just for fun, and I found some at Shutterstock that were about what I was trying to go for. I downloaded them and snagged the shape out of there.

Boiled down is basically this:

<svg>
  <path id="tab-shape" d="M116.486,29.036c-23.582-8-14.821-29-42.018-29h-62.4C5.441,0.036,0,5.376,0,12.003v28.033h122v-11H116.486
			z">
</svg>

The HTML

Since we would likely call tabs navigation and navigation is lists, here is what we would end up with:

<nav role="navigation" class="main-navigation">
  <ul>
    <li class="tab-1 active">
      <a href="#home">
        <span>Home</span>
        <svg viewBox="0 0 122 40">
           <use xlink:href="#tab-shape"></use>
        </svg>
      </a>
    </li>
    <li class="tab-2">
      <a href="#about">
        <span>About</span>
        <svg viewBox="0 0 122 40">
          <use xlink:href="#tab-shape"></use>
        </svg>
      </a>
    </li>

    <!-- etc. as many tabs as you'd like -->

  </ul>
</nav>

We need to use <svg> in the markup there because that’s how this works. We wrap it in an <a> because we intended to have the tabs be clickable/tappable links. We need the <span> because we intend to position the <svg> and text right on top of each other, with the text on top.

The CSS

We can use one of the wrapping elements as a positioning context. We’ll absolutely position both the <span> and <svg> and give the <span> a higher z-index.

Presented in SCSS here, as the nesting makes it nicer to read:

.main-navigation {
  overflow: hidden;
  position: relative;
  padding: 0 0 0 1rem; /* push tabs back to right */
  ul {
    list-style: none;
    padding: 0;
  }
  li {
    float: left;
    width: 12rem;
    height: 5rem;
    margin: 0 0 0 -1rem; /* pull to left to overlap tabs */
    position: relative;
    &.active {
      z-index: 6; /* active one on top */
    }
  }
  a {
    position: relative;
    display: inline-block;
    width: 100%;
    height: 100%;
  }
  svg {
    width: 120%; /* tab shape should stick out past clickable box */
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
    pointer-events: none; /* SVG eats clicks weird */
  }
  span {
    position: relative;
    padding: 1rem 0 0 3.3rem; /* position the text */
    z-index: 2;
    display: inline-block;
    width: 100%;
    height: 100%;
  }
}

Now to change each tab’s appearance, you can reference them by class name and change the fill. We also need them in reverse z-index order so the first one is on top instead of the last, which is easy enough to do by explicitly declaring them.

.tab-1 {
    z-index: 4;
    svg {
      fill: red; 
    }
  }
.tab-2 {
    z-index: 3;
    svg {
      fill: orange; 
    }
  }

The Demo

See the Pen SVG Tabs by Chris Coyier (@chriscoyier) on CodePen

There is some JavaScript in there to activate the different tabs, which is at essence just changing a class name.

You might notice the tabs also have slight gradients to them. The interesting connection here is that you can declare that SVG gradient in the same <svg> as the original template shape, then fill a templated shape just by referencing the filter by ID.

<linearGradient id="tab-1-bg" x1="0%" y1="0%" x2="0%" y2="65%">
  <stop offset="0%" style="stop-color: rgba(136, 195, 229, 1.0);" />
  <stop offset="100%" style="stop-color: rgba(118, 160, 192, 1.0);" />
</linearGradient>
.tab-1 {
  svg {
    fill: url(#tab-1-bg); 
  }
}

So basically you can think of your filters as templates as well, re-using them at will. Play around with the demo a bit too. There are some commented-out parts in there dealing with shadow primarily that you might like. What would be really cool is to make them flexy where tab size changes bigger and smaller as there is room available (the text as well) and then ditch them entirely when it’s impartially small and swap out for a mobile navigation solution.