SVG, Clip-path & Sketch… What I learned

Bryce Howitson
Prototypr
Published in
5 min readAug 29, 2017

--

I recently ran across a project where I needed to easily change the fill of a vector shape. I assumed this would be relatively straight forward but alas it was not. I’m documenting my saga here in case it might help someone else.

First of all, here’s what I was attempting to create, it’s a poster created by Mike Joyce of Swissted and part of a side project of mine. Specifically, I was trying to recreate the arrows with a few requirements.

  1. I wanted the arrows to be crisp and clean looking regardless of the size.
  2. The arrows should be able to scale to fill their container without me specifying a size.
  3. The ability to change the arrow color based on its position (every 6th arrow should be white).
  4. Bonus: the ability to animate the path on hover or click.

Scalable Vector Graphics

Right away, I assumed that SVGs would be an easy answer. I didn’t have a vector of the arrow so I fired up trusty Sketch, took a screenshot of Mike’s poster and traced the arrow’s shape. I didn’t pay any attention to the size of my artboard or the precise location of the points in my shape, because hey SVGs scale. I mean it’s in the name right? Well kinda… Turns out SVGs have a few gotchas that made them less than ideal for my needs.

Creating an SVG in Sketch. Notice the Export type? That parts easy.

First, SVG is awesome because it uses an XML structure similar to HTML. You can then use CSS to modify the appearance of individual items in the code. However, you can only modify an SVG with CSS if you embed the SVG inline in the HTML. That’s great but in my case, I’ll have 36 copies of the same SVG. Yes, I could have used an include but that’s still a lot of excess file size and its difficult to tweak/change due to the number copies. But sure, lets try it.

Raw SVG code generated by Sketch

Here, I ran into the second problem. The “viewbox” property of an SVG. Remember how I didn’t pay a lot of attention to the size of my work in Sketch? Well here’s the first indication it was a problem. Notice the height and width (43px & 44px)? Well those aren’t going to scale. No problem, I’ll just convert those to 100%. And now my SVG scales. Except it doesn’t because while the SVG container (represented by the width and height) scales perfectly happily the artwork doesn’t. The artwork references the viewbox attribute as a way to understand how to place points on the screen. Notice that the viewbox points don’t have units but we can assume they’re pixels since they match the height/width property of the SVG. I can’t simply convert the viewbox to percentages because the points in the polygon element below (what actually makes up the arrow) are pixel locations relative to the viewbox. Ok maybe SVG isn’t such a great idea.

What if I could just create a mask in CSS?

Ok this is taking a lot longer than I expected. What if I could just change the background color of a div and then only show the arrow shape? Enter CSS clip-path (SVGs can be masks but with similar problems as above). Ok clip-path will take a predefined shape (circle, rectangle) or a polygon. Polygon’s don’t get Bezier Curves but my arrow is made up of straight lines.

Ok so where do I get a Polygon? Wait there’s one in the SVG, I bet I could just use that. So I copied the polygon points into my css clip-path.

Seems pretty simple, but that didn’t work. Oh wait, according to the clip-path documentation points are comma separated and need units. Ok I can change that.

Right away I can see a problem though. Monitors can’t display decimal point pixels so its going to be blurry. But it works! Unfortunately, my masked area is only 43px x 44px… Just like the size of my SVG. Maybe if I just change all the px to % I’ll get what I want. Wait that’s worse now my arrow is only 43% wide and 44% tall. I wanted it to be full size… At this point I wondered briefly if I could figure out how to convert all the points (x,y coordinates) to be 100% but that seems really complicated.

Let’s see if I can get Sketch to give me the right sizes. Back to Sketch and I notice another problem. The first point I select is X: 527.27 and Y: 334.84. Neither of those numbers are anywhere in my SVG. That’s odd. That’s right positions in Sketch are based on the document’s origin. I could either figure out how to get my arrow to align on the 0,0 of the document, or I could simply add an artboard because elements on an artboard are relative to that board.

And the artboard will solve my math problem too. If I make the artboard 100px x 100px then I’ll be able to easily convert to percentages and have everything work. One last thing (more for my sanity than anything else) is to make sure that each point in Sketch is on a full pixel.

That looks much better. Notice how the height, width and viewbox are all based on 100px? We can easily convert that to a percentage by simply adding % unit. And what do you know. It works and scales as I expect based on the size of the container.

To recap

While not as elegant as an SVG, the clip-path allows me to apply the same style to every instance of the arrow element meaning minimal extra code. I can also easily change the background of every 6th arrow to be white. And I’m able to animate* to a different clip-path polygon on hover.

*Only in Chrome and only if the two shapes have the exact same number of points.

Here’s the final result:

--

--

I’m a designer, I help teams create great customer experiences in the digital space. Google Expert (UI/UX/Prod) http://brycehowitson.com