Tessa Thornton 02/11/2015

Building Year in Review 2014 with SVG and Rails

As we have for the past 3 years, Shopify released a Year in Review to highlight some of the exciting growth and change we’ve observed over the past year. Designers James and Veronica had ambitious ideas for this year’s review, including strong, bold typographic treatments and interactive data visualizations. We’ve gotten some great feedback on the final product, as well as some curious developers wondering how we pulled it off, so we’re going to review the development process for Year in Review and talk about some of the technologies we leveraged to make it all happen.

A lot of the feedback we’ve gotten from front-end developers has been along the lines of “that’s cool! Did you use images or CSS for [that effect]?” In most cases, the answer was actually neither, because most of the fun graphics, animations, and effects rely heavily on SVG with a bit of JavaScript and some clever Rails helpers.

It’s easy to get excited about CSS3 graphics or animations. While these effects are interesting and push the boundaries of what we can do with the technology, CSS wasn’t really made for these purposes and it’s not always practical. SVG was created specifically for web graphics, and it suits the medium perfectly. SVGs are resolution-independent, easily scriptable and styleable, endlessly flexible, responsive, and even well-supported by most major browsers post IE8.

Leveraging SVG for Year in Review

Some of the ways we used SVGs in Year in Review:

Animated SVG charts using d3.js

d3.js is a library for making “data-driven documents”, which effectively means it allows you to associate data with DOM elements, and then apply attributes and transformations to those elements based on the data. Since SVG elements can be manipulated with attributes, it’s easy to use d3 to generate interesting scalable data visualizations using JavaScript without ever opening Excel or Illustrator. Since attributes like the radius of a circle or the width of a rectangle are just attributes on <circle> or <rect> elements, we can also easily animate these attributes.

Animating the <path> element

Taking inspiration from Polygon’s PS4 review, we have some SVG paths that appear to draw themselves as the user scrolls. As fancy as this effect looks, it’s actually not that complicated. The effect is achieved by taking advantage of the stroke-dashoffset property. CSS-Tricks has some animated explanations of the technique, but all it really requires is that you have a <path> element with a stroke, and that you can calculate the total length of the path.

Making SVG responsive

Despite their resolution-independence, SVG has a pixel-based grid system for positioning and building graphics, and forcing inline SVG graphics to stay relative to screen size is a little more complicated than you’d want. You need 3 ingredients for a responsive inline SVG:

1. viewBox attribute

One of the most mystical attributes in the SVG world, viewBox is essential to preserving the aspect ratio of your image as it scales. The viewBox attribute has 4 values: min-x, min-y, width, and height. Width and height are what you’d expect them to be, and min-x and min-y define the origin of the graphic within its parent. Basically, the first two values specify where the top left of the SVG should be in relation to its parent element. In most cases, you want the origin to be at 0,0, so your viewBox attribute would be 0 0 width height.

To get the proper viewBox attribute for a generated SVG, set it to 0 0 width height. It can be a bit trickier when you’re exporting SVG documents from programs like Illustrator. In Illustrator, you’ll want to use the artboard tool to crop the container of the graphic, and select the ‘fit to artwork bounds’ option.

2. Some positioning weirdness

To get the SVG to scale with the browser window, we can use the same technique used to make responsive iframe/video embeds. We need to create a container element with position: relative and height: 0 and then set the SVG element itself to position: absolute:

.svg-ratio-hack {
  position: relative;
  height: 0;
  svg {
    position: absolute;
    top: 0;
    right: 0;
    width: 100%;
    height: 100%;
  }
}

3. Padding-bottom based on ratio of graphic

The container element is set to height: 0, so we need to add padding to the bottom or our graphic will be cut off. The bottom padding value needs to be set to the percentage ratio of the graphic itself, so if your graphic is a 1:1 square, you would set padding-bottom to 100%. If your image has a 4:3 aspect ratio, the padding-bottom needs to be 75%.

.svg-ratio-hack {
    padding-bottom: 75%
}

Unfortunately this has to be set manually for each differently-sized SVG you use based on the ratio of width to height.

Offset SVG text

James came up with a really cool typographic treatment for the headlines in Year in Review, featuring offset strokes and patterned fills. They look great but their very specific design makes them difficult to achieve with regular web text and CSS.

So, SVG to the rescue yet again. We created two <text> elements with one slightly offset via its x and y attributes down and to the left. We hide the duplicate element from screen readers with the aria-hidden attribute.

SVG code like this can be a bit of a pain to hand-write repeatedly, but since SVG is markup-based, you can actually generate the code using many server and client-side technologies. Since shopify.com is a Rails app, we use .erb templates and Rails helpers to generate the code for us, so we just have to feed in the text and font size of the headline we want rendered.

In Ruby/erb, the template looks something like this (note: not valid .erb, simplified for readability)

The height attribute also allows us to set a viewBox and determine the padding-bottom value we need to make the SVG responsive.

SVG patterned backgrounds

To achieve the patterned backgrounds on the filled SVG text, we used the <pattern> element. Having an SVG containing a <pattern> element with a unique ID allows you to set the background fill of an SVG element to the contents of the respective <pattern> tag.

It can be tricky to create the pattern to be used in a program like Illustrator, it’s usually easiest if you work at literal sizes so you don’t have to worry about scaling the pattern (ex. you should set your artboard to be 10px by 10px if that’s the size of your pattern). The <pattern> element should also have explicit width, height, x, and y attributes set. To prevent the pattern from scaling to the element you’re applying it to (which would likely stretch it), make sure the patternUnits attribute is set to userSpaceOnUse.

Since we had a couple different patterns with various colour combinations, we created a single SVG element at the top of our erb file and rendered our patterns inside it using a rails helper to generate variations of patterns with different colours.

To use the pattern, specify the id of the <pattern> element in the fill attribute.

SVG is your friend

SVG can seem like a lot to learn, but like HTML, it’s a fairly shallow topic (unless you get into the math of bezier curves…) and once you have a grasp of the basics it can be a very powerful technology. Part of what makes it so flexible is the variety of ways you can generate and manipulate SVG elements: you can create SVGs with GUI editors like Illustrator, with server-side technologies like Ruby and erb, or with JavaScript; and you can easily manipulate, style, and animate your graphics with JavaScript and CSS.

There are even more possibilities if you’re using a JavaScript framework that supports data-binding in SVG, like Ember or Angular, where you can bind SVG attributes to your data, and have your graphics update and animate automatically as data changes.

Resources

View original comments on this article