loading...
Cover image for Simple wave pattern with CSS gradients

Simple wave pattern with CSS gradients

rossangus profile image Ross Angus ・3 min read

Beach image by Chedi

I've been noodling around with CSS gradients again. This time, it's a wiggly wave pattern which you can mess about with using the sliders in this Codepen:

The original version works using your grandfather's SCSS variables, rather than the new hotness of native CSS variables. You can see that version here:

That pen takes you through how the pattern was made, step by step. Beginning with:

The simplest gradient

.step1 {
    background-image: radial-gradient(
    circle at top center,
    red,
    green
  ); 
  background-size: $wave-width $wave-height;
}

This'll draw an ugly radial gradient starting from the top center of the gradient tile and travelling vertically down to the bottom. How far? That's determined by the $wave-height variable. By default, it's 100px.

The best gradient is no gradient at all

The second step removes all the blending between two colours which gradients are famous for. What's the point of the W3C inventing a new technique if we can't immediately subvert it to do something completely different? (tables looks sheepish in the corner of the bar). The SCSS looks like this:

.step2 {
    background-image: radial-gradient(
    circle at top center,
    red,
    red $radius,
    green $radius,
    green
  );
  background-size: $wave-width $wave-height;
}

What's $radius? That's half of the diameter of the circle on the outside of the line. Here's how I work out that amount:

The radius is the width of the gradient tile, plus two line widths, then this figure divided by four.

Or, to express that in SCSS:

$radius: ($wave-width + $line-thickness + $line-thickness) / 4;

Even though the radius is from the top to the bottom of the gradient tile, I use $wave-width because the wiggles go from left to right, not from top to bottom. This means that we need all of the different circles to join up horizontally, not vertically. But we'll come to that bit in a few steps.

We need this radius to be large enough so that the circles overlap, to achieve the wave effect.

Lucky Pierre

(Please don't Google that)

Now we add a third colour, neatly sandwiched between the red and green:

.step3 {
  background-image: radial-gradient(
    circle at top center,
    red,
    red $radius - $line-thickness,
    $line-color $radius - $line-thickness,
    $line-color $radius,
    green $radius,
    green
  ); 
  background-size: $wave-width $wave-height;
}

Sorry the SCSS variables make it hard to read. Remember you can look at the compiled CSS in CodePen by clicking on the little v in the top right hand corner of the CSS pane. It doesn't format the code as nicely as a human does, though.

$line-color is just a colour value and the maths after it results in a pixel value. So the gradient is basically a bit of red, a tiny bit of the user's colour, then the rest is green.

Transparent water

Step four is just to make the debug colours transparent, so we can see that lovely line snaking through the water:

.step4 {
    background-image: radial-gradient(
    circle at top center,
    transparent,
    transparent $radius - $line-thickness,
    $line-color $radius - $line-thickness,
    $line-color $radius,
    transparent $radius,
    transparent
  ); 
  background-size: $wave-width $wave-height;
}

Apart from that, it's the same as step 3.

New circles

Things start to get a bit more complicated here as we add a second background image, which is nearly identical to the old one. Look:

.step5 {
  background-image:
    radial-gradient(
      circle at center top,
      transparent,
      transparent $radius - $line-thickness,
      $line-color $radius - $line-thickness,
      $line-color $radius,
      transparent $radius,
      transparent
    ),
    radial-gradient(
      circle at right bottom,
      transparent,
      transparent $radius - $line-thickness,
      $line-color $radius - $line-thickness,
      $line-color $radius,
      transparent $radius,
      transparent
    );
  background-size: $wave-width $wave-height;
}

The only difference is the new circle is at the right bottom corner of the tile, rather than the top center. The plan is that this new circle will partially join up with the adjacent tiles.

Three ring circus

Finally, we add a third circle / gradient to complete the wave, this time in the bottom left corner of the tile:

.step6 {
  background-image:
    radial-gradient(
      circle at center top,
      transparent,
      transparent $radius - $line-thickness,
      $line-color $radius - $line-thickness,
      $line-color $radius,
      transparent $radius,
      transparent
    ),
    radial-gradient(
      circle at right bottom,
      transparent,
      transparent $radius - $line-thickness,
      $line-color $radius - $line-thickness,
      $line-color $radius,
      transparent $radius,
      transparent
    ),
    radial-gradient(
      circle at left bottom,
      transparent,
      transparent $radius - $line-thickness,
      $line-color $radius - $line-thickness,
      $line-color $radius,
      transparent $radius,
      transparent
    );
  background-size: $wave-width $wave-height;
}

Phew. That's a lot of SCSS.

Posted on by:

rossangus profile

Ross Angus

@rossangus

My interests are quirks of CSS, accessibility and "doing interesting things with old technology", apparently.

Discussion

markdown guide