- clip-path is awesome for quickly changing the colors of simple graphics.
- clip-path doesn’t work on all browsers.
- clip-path using an SVG as the mask doesn’t work on most browsers.
- Any SVG you want to use as a clip-path needs to be built from shapes, not
I’m not good with color schemes. I can pick out things on Kuler that I think might look good, but when it comes time to implement them, I have no idea what to put where or how to arrange them.
When I’m sketching out a site like this, SASS is a godsend because introducing variables into CSS lets me change an entire color scheme by changing only a few lines of code, so I can plow through a whole bunch of different ideas in mere minutes.
Certain things in this design, however - namely Flying Umbrella Dude Man Guy on the lefthand side of the main menu and the hamburger button on the right (on mobile) - do not lend themselves well to this approach. I noticed that Bootstrap’s
standard hamburger menu button
is just an SVG, and that got me thinking: is it possible to use SASS variables to change the stroke-color of an SVG path? Would
<path stroke="$theme-primary-color;"> [...] work?
Nope. It didn’t. It might just be that I’m doing something wrong, but it didn’t work.
When I want to separate shape and color in something like GIMP , I use a layer mask to define the shape of the object and then apply that mask to a solid color. Would the same thing be possible in CSS somehow?
Before we go too much further, I need to point out that - at the time of writing -
clip-path is only
in Firefox. I didn’t find any major issues in Chrome, but IE and Edge both display the element as a solid box of color. Rendering in Safari on iOS was a complete, hopeless mess with the SVG being stretched all around the page and re-scaling itself apparently at will.
The general idea here is that I’m going to be creating a
<div> with a solid
background-color that I can define using a SASS variable and then using an SVG as sort of a cookie cutter to cut out part of that
<div> and leave the rest of it transparent:
I already had a SVG version of Flying Umbrella Dude Man Guy ready to go, it was just a matter of cleaning out all the extra crap that Inkscape adds in order to get the filesize down as much as possible.
For some reason, even if
width="0" are both specified in the SVG tag, the SVG will still have some height applied to it, and will take up space on the page. For me, this space is 24px tall, and I can’t figure out where that value comes from. Setting
display: none; on the SVG will cause it to appear invisible to
clip-path as well, rendering that approach useless. To solve this, I stuck each SVG I would be pulling paths from into a
max-height: 0px; applied to it in order to hide them from the user.
In order for the SVG to scale correctly, we’ll need to add a few attributes to the
clipPath, as described by
. The renderer is expecting all points in the SVG to be somewhere between (0,0) and (1,1). Since this is generally never going to be the case unless you’re building your SVG from scratch with this requirement in mind, we need to scale the image down to fit into that unit square.
We do this by first adding the
clipPathUnits="objectBoundingBox" attribute to the
After that, make a note of the coordinates in the
viewBox attribute all the way up in the root
<svg> tag. These coords are in the sequence xstart ystart xend yend. Back in the
transform="scale(x y)" after
clipPathUnits and assign x and y the following values: x = 1/viewBoxx, y = 1/viewBoxy.
For instance, my Flying Umbrella Dude Man Guy SVG tag is
<svg height="0" width="0" viewBox="0 0 63.5 63.5">, so the scale factor for both x and y would be 1 / 63.5 = 0.0157480, and the resulting
clipPath tag looks like
<clipPath clipPathUnits="objectBoundingBox" transform="scale(0.0157480 0.0157480)">.
After that’s all done, it’s a simple matter of assigning an
id to each
<clipPath> you’ll be using, and referencing that
id in your
I didn’t want to jump back into Inkscape just for my hamburger menu’s icon, so I decided I would be clever and create it by hand. This turned out to take a whole lot more time and energy than I thought it would.
I figured that since the icon is just three lines, I could use just three
<line>s, but no matter what combination of positions or coordinates or
stroke-widths I used, nothing showed up.
Frustrated, I tried three
<path>s instead, with equally invisible results.
It took a while, but I eventually tried making a sort of hockey stick shaped path, and then added
z to the end, telling the renderer to connect the last specified point to the first specified point. I got a triangle.
Now I understood something that I probably should have gotten from the beginning:
clip-path requires a path with a nonzero area as an input.
Thinking of it as a cookie cutter is a really good analogy: if you have a straight line for a cookie cutter, it doesn’t matter how you jam that straight line into the dough, you’re not going to get a cookie out of it because your cutter is one-dimensional. Even with two non-parallel lines sharing some vertex, you’re still not going to get a cookie. It’s only when you have points joined in such a way that they form a two-dimensional shape with some area >0 that you’ll get a cookie.
For my hamburger menu, I ended up using three
<rect>s with an
ry specified so that their ends would be a little rounded. Visually, this is identical to using a
<svg height="0" width="0" viewBox="0 0 32 32"> <defs> <clipPath id="hamburger-clip-path" clipPathUnits="objectBoundingBox" transform="scale(0.03125 0.03125)"> <rect width="22" height="1.8" x="5" y="8.1" ry="0.9" /> <rect width="22" height="1.8" x="5" y="15.1" ry="0.9" /> <rect width="22" height="1.8" x="5" y="22.1" ry="0.9" /> </clipPath> </defs> </svg>
As mentioned earlier,
clip-path isn’t very widely supported yet.
On mobile Firefox, clip-path works excatly how you would expect it to:
However, on any iOS browser (Safari is shown here)…
To be clear, this isn’t a problem with how these browsers handle
clip-path itself, but the combination of an SVG and
Because of the strange Safari behavior and the fact that both IE and Edge render the icon as just a solid block of color, I decided I had to scrap the idea for now. I’d love to be able to come back to this in the future because of how elegant of a solution it is, but too many browsers just aren’t ready yet.