Grow your CSS skills. Land your dream job.

SVG Tabs (Using an SVG Shape as Template)

Published by Chris Coyier

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.

Comments

  1. hey! Thanks for crediting me) Could I mention that Iconmelon was built on top of that technique? ;)

  2. Chris Reed
    Permalink to comment#

    Very Cool article. Just another reason to use a vector editor (or browser if that’s your thing) to design your sites.

  3. Great article…

    What does the “viewbox=”0 0 20 30″ attribute mean and do…what do the numbers represent? Are they equivalent to pixels?

    • Amelia BR
      Permalink to comment#

      The viewbox attribute on an SVG element creates the coordinate system for the image. It is the key to the “scalable” aspect of Scalable Vector Graphics: instead of defining shapes in terms of pixels or ems or any absolute measurement, you define them by arbitrary “user coordinates”. Note that in the definition of the SVG path for the tab shape, the numbers are given without units; these are in user coordinates.

      The browser scales the user coordinate system to the rectangle in which the SVG is drawn based on the viewbox attribute. The four numbers are respectively the coordinates of the top left corner and the width and height of the image, in user coordinates. The width and height values are scaled as necessary to match the final width and height of the image as defined by CSS or by the SVG width and height attributes. A separate attribute, preserveAspectRatio, can force uniform scaling (same scale factor for both height and width) so the image won’t be distorted.

      The first two numbers in the viewbox can be used to shift the image around relative to the box in which it is drawn. This is similar to using position values to move around img elements or background images. You can even do something like viewbox=”0 100 100 -100″ if you find it easier to work with the origin of your coordinate system in the bottom left corner: the negative height value flips the default direction, while the first two numbers define the top-left corner as (0,100).

      If you don’t use a viewbox, then all dimensions within the SVG will be interpreted as pixels measured relative to the top left corner, and no scaling would occur.

      Here’s the full spec:

      http://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute

    • Thank you Amelia BR for your great explanation of the viewbox attribute…

    • Amelia BR
      Permalink to comment#

      CORRECTION

      It’s a good idea to actually read the linked sources before posting an answer. Sorry. I was mixing up D3.js scales with basic SVG viewbox syntax.

      No, you can’t use a negative height value to flip the user co-ordinate direction. That’s clearly described as an error in the spec. If you want to flip the direction of the coordinates, you have to use a transform of scale(1,-1). (All transforms alter the way the user coordinates get mapped to the space in which the drawing is rendered.)

      http://www.w3.org/TR/SVG11/coords.html#TransformAttribute

    • Amelia BR
      Permalink to comment#

      Pictures are worth a few hundred words:

      http://codepen.io/AmeliaBR/full/lvkyD

  4. Also the <lineargradient> tag does that have to be a child of an<svg> tag? or can it just be a standalone tag?

    • Amelia BR
      Permalink to comment#

      It’s possible some browsers would render it properly anyway, but the <linearGradient> tag is an svg element and should therefore be part on an svg block. The recommended format is to also wrap it in a <defs> tag, too, but that doesn’t affect rendering since an SVG gradient will not be rendered on its own anyway.

    • Thanks again Amelia BR…your time answering my questions is greatly appreciated…

  5. Mazurka
    Permalink to comment#

    Not only will this scale really well like you said, it’s gonna look great on large “retina/HD” devices like macbook pros, tablets, etc.

  6. Great post Chris! SVG has so many cool features, and with the option to embed it directly in HTML it really shown just how useful and powerful it is. Also it is perfect for retina ready graphics.

  7. Phil
    Permalink to comment#

    Cool technique. I don’t really like seeing the svg inside the HTML like that. Makes it less semantic.

    • Permalink to comment#

      I was about to say the same thing. The websites I work on all have the same HTML so I can’t use techniques like this that rely on non-semantic markup. I love SVGs but they have to be in the CSS.

  8. ArpiDesign
    Permalink to comment#

    Great technique. Very useful. Thanks Chris!

  9. Permalink to comment#

    Very nice. I’d like to mention from my own experience that it is worth to construct curved paths (d=”…”) oneself and let not do AI the job (if the path is not too complicated). Good curve syntax is easy calculus and of course much shorter than the usual Adobe markup. One decimal (0.1) is enough in most cases.

  10. Camaron
    Permalink to comment#

    Whats also very cool is saving your Illustrator Files as a SVG file then when the SVG box pops up you see SVG code, as easy as that you have an SVG code super fast. Awesome Article!!

  11. Guillaume
    Permalink to comment#

    Woww, that’s really impressive, i heard about Iconmelon, but did have a chance to look at it.
    Seems like more and more svg is going to invade our html files.

    Thanks again for this great demo, will definitely use some of this stuff on future projects.

  12. c0ns0l3
    Permalink to comment#

    Hey! It’s realy nice! but what about streighting tabs? what if my tabs must be flexible?

  13. Roy
    Permalink to comment#

    Thanks Chris, this is going to be very useful…

  14. Permalink to comment#

    Great article. Thanks Chris.

  15. John Holt Ripley
    Permalink to comment#

    Hi, this is a great technique, but I’m having trouble changing the path fill of a cloned element.

    I can use CSS to target a path in the original SVG, but not, it seems, to make any of the cloned SVGs different from the parent.
    Hopefully this pen should show the problem.

    Any ideas anyone?

  16. Atelierbram
    Permalink to comment#

    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)

    Forked the demo to try to combine the concept behind Douglas Bowman’s “Sliding doors”, and this SVG template technique. Requires two SVG shapes: one very wide one for the left side, and one small one, just for the right corner.

  17. Really useful! If you’re using grunt, I wrote a tool that scans your project folder for SVGs, extracts the shapes, removes headers and some white-spaces, puts them into individual groups with their filename as ID and writes these combined groups into an invisible SVG tag of a HTML file of your choice.

    https://github.com/serrynaimo/grunt-svgtemplater

This comment thread is closed. If you have important information to share, you can always contact me.

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