This is a technique I used recently to create a flexible,
repeating pattern using SVG with CSS mask. There are live
examples in this post and they’re available as a
CodePen.

The need came from something I designed at work.
I gave the bottom of the header on each page a little squiggle to make it a bit
more interesting than a straight line. Along with that, I designed a color system
for each type of page. Common pages parade in purple, company content is true blue,
blog posts read in red, and contests own orange. With StreetCred I’ve been
throwing a rainbow of bright colors to see what’s fun and what sticks.

A screenshot of a purple, blue, red, and orange banner from the StreetCred website.
fig 1: Banner squiggles from streetcred.co

Along with the current colors, we’re getting ready to ship new features that
use even more colors. We’ll make pages for those and they’ll need themed banner
squiggles. We need a flexible way to do that.

How Should This Work?

For the design, I needed a seamless, horizontally repeating pattern. I needed
SVG so it was crisp at any size. I wanted a single SVG image that
I could color using CSS. Because we could have n-number of colors, creating
a new SVG for every color wasn’t a future-facing option. The squiggly banner using this
approach is live on streetcred.co. Let’s
look at an isolated demo to see how the pieces fit together.

Creating the Demo SVG

First, I made a quick doodle of a squiggly line in Sketch using the vector tool.
The important part was to make sure the line was seamless when repeated horizontally.
I did that by making sure
the first point and last point were at the same location on the y-axis. And
that the bezier curves leading to them was flat when it reached the
edge of the artboard. This is a simple shape, so it was easy to eyeball.
A more complex design would involve more work.

A screenshot in Sketch app showing the demo squiggle vector being created.
fig 2: Creating the demo SVG in Sketch

To test if the shape is seamless I just duplicated the artboard and dragged it
into place. Nothing fancy again, a quick eyeball is enough for this.

When I create graphics for SVG use, I simplify them as much as possible.
For this, I expanded the stroke. That way instead of exporting a path
with a stroke, I end up with a path with a fill.
In my experience, filled shapes are more flexible when you’re working with them
in browsers. Helps avoid scaling issues with strokes. SVG prep is a
deep topic that I plan to write more about in the future.

Exporting SVG from Sketch leaves a lot of crud in that we don’t need. My next
stop is SVGOMG. After
optimizing we end up with an SVG with a single path element. I
truncated the value of the d attribute for brevity.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 250 75">
  <path id="squiggle" d="M12 19.778C12 1.318..." />
</svg>

We’ll use the id attribute as a reference in CSS. Notice we don’t need
presentational attributes like fill. SVG provides the
shape. CSS handles the color.

The Code

The HTML is a single div. The CSS is three properties.

<div class="repeater"></div>
.repeater {
  background-color: red;
  /* This fixed height is only for demo, your use cases might not need it */
  height: 75px;
  mask-image: url("/path/to/repeater.svg#squiggle");
}

As of this writing, this CSS works in Firefox, but not in Chrome, Safari,
or Edge. WebKit/Blink browsers still require vendor-prefixed mask
properties. We don’t want to have to repeat the path to the image. Let’s store
it in a custom property to make it reusable.

.repeater {
  background-color: red;
  /* This fixed height is only for demo, your use cases might not need it */
  height: 75px;
  --svg: url("/path/to/repeater.svg#squiggle");
  -webkit-mask-image: var(--svg);
  mask-image: var(--svg);
}

With the prefixed version of mask-image in place, this works in all current
versions of Chrome, Firefox, Safari, and Edge.

fig 3: A live demo of the technique. The CSS is inline for inspection.

What did we do?

mask-image
is doing the heavy lifting here. We’re giving it a reference to an external
SVG file and the id attribute of the path we want.
Mask is hiding anything in our div that doesn’t intersect
with that shape. So we see the background-color only where the
path is.

mask is like CSS bacground. It has a mask-repeat
property that defaults to repeat. Check the MDN docs
for more that.

What else?

With a functional foundation in place, we can have fun with it. We can style the underlying div any
way we want to create different effects. We can change the color.

.repeater--orange {
  background-color: orange;
}
fig 4: A live demo of the technique showing that we can change the color with CSS.

We can take that further by setting a background image.

.repeater--gradient {
  background: transparent linear-gradient(90deg, red, purple, blue, green);
}
fig 5: A live demo of the technique showing that we can use a background image

Changing the background color and image is fun, we can also change the height
of the div to produce something different.

.repeater--sized {
  height: 18px;
}
fig 6: A live demo of the technique showing that we can change the size

We can change the color, bacground-image, and height. Let’s introduce the
mask-size property to squish the SVG into a type
of texture.

.repeater--textured {
  --texture-lines: 10;
  --mask-size: calc(250px / var(--texture-lines));
  -webkit-mask-size: var(--mask-size);
  mask-size: var(--mask-size);
}
fig 7: A live demo of the technique showing that we can use mask-size to produce a texture

Again, we’re using a custom property to not have to repeat the size value. We’re
using calc here as a convenience. 250px is the height
of the viewbox of the SVG. We divide that by the number of rows
we want to make sure we don’t have partial rows.

We can also change the position of the mask using mask-position.
And, if we can change a value with CSS, that means we can change it over time.
Change over time equals motion.

@keyframes move {
  to {
    --pos: 150%;
    -webkit-mask-position: var(--pos);
    mask-position: var(--pos);
  }
}
.repeater--animated {
  animation: move 0.6s infinite linear alternate;
}
fig 8: A live demo of the technique showing that we can animate the mask position.

These are toy examples, but they show that with a few building blocks we can
construct a lot of variations. With more complex SVG images and more CSS, we can use this
technique to produce all sorts of fun stuff. And be future-flexible while
doing it.

Thanks for reading



Source link

Write A Comment