How to Create Wavy Boxes Using CSS

Written by Web Developer

February 6, 2025
How to Create Wavy Boxes Using CSS

After the Zig-Zag boxes, it’s time to create wavy boxes! More cool shapes that you can use to decorate your images or any content. We will study two types of shapes illustrated below:

Wavy boxes using CSS

As usual, you can find the codes for the different shapes within my online collection but follow along to understand the logic behind creating them.

As for the HTML, it’s a single element like most of the CSS shapes I create. It’s either an image:

<img src="" alt="">

Or any element with text content (or any kind of content):

<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi quam sem, tincidunt a enim sed, suscipit feugiat diam. Cras mollis ligula orci, a rhoncus dolor volutpat eget.</div>
wavy boxes with text content

Creating the First Wavy box

I will start with the shape on the right which I also call a Wiggly box. We will be using CSS mask and gradients. I think it’s no more a surprise if it comes to CSS shapes. It’s either clip-path or a mask with gradients.

The result is obtained with only three simple gradients and let’s start with the first one:

Overview of the first gradient

It’s a basic radial-gradient clipped to the padding area to leave the border area empty.

.wiggly-box {
  --s: 30px;  /* control the size of the wave */
  --w: 400px; /* preferred image width */
  
  width: round(var(--w),4*var(--s));
  aspect-ratio: 1;
  border: calc(2*var(--s)) solid #0000;
  box-sizing: border-box;
  mask: 
    radial-gradient(var(--s),#0000 100%,#000) padding-box
     var(--s) var(--s)/calc(2*var(--s)) calc(2*var(--s));
}

Nothing complex so far. We are using a variable --s to easily control the shape by adjusting one value. It controls the size/dimension of this first gradient (and the others as well) and also the width that needs to be a multiplier of 4*S.

Then we add a second gradient that will fill the inner area. We fill the inside circles while leaving the outside curves to get the following.

Overview of the second gradient

We define a single-color gradient placed at the center with a dimension that leaves a space equal to 3*S around it.

.wiggly-box {
  /* same as before */
  mask: 
    conic-gradient(#000 0 0) no-repeat
     50%/calc(100% - 6*var(--s)) calc(100% - 6*var(--s)),
    radial-gradient(var(--s),#0000 100%,#000) padding-box
     var(--s) var(--s)/calc(2*var(--s)) calc(2*var(--s));
}

The final gradient is another radial-gradient that will complete the puzzle!

Overview of the third gradient
.wiggly-box {
  /* same as before */
  mask: 
    radial-gradient(var(--s),#000 100%,#0000) 
     0 0/calc(4*var(--s)) calc(4*var(--s)),
    conic-gradient(#000 0 0) no-repeat
     50%/calc(100% - 6*var(--s)) calc(100% - 6*var(--s)),
    radial-gradient(var(--s),#0000 100%,#000) padding-box
     var(--s) var(--s)/calc(2*var(--s)) calc(2*var(--s));
}

Now, you apply this to your images and you get a fancy decoration:

Note how I am able to easily use a gradient coloration. I am also adding some padding (in addition to the existing border) to make sure the waves don’t overlap the image. There is no particular logic with the padding. It simply needs to be bigger than 1*S to avoid the overlap.

We can also consider a slightly different implementation if we want to have rounded corners on the images like below:

The first implementation won’t work with rounded corners because the radius will affect the first gradient layer (the one clipped to the padding area). To overcome this, I replace it with two layers that give the same result.

overview  of the intersect composition

Instead of clipping the area of the gradient using padding or border, I consider another gradient and I perform an intersect composition between them. In other words, we take only the visible part of both of them.

img {
  mask: 
    radial-gradient(var(--s),#000 100%,#0000) 
     0 0/calc(4*var(--s)) calc(4*var(--s)),
    conic-gradient(#000 0 0) no-repeat 
     50%/calc(100% - 6*var(--s)) calc(100% - 6*var(--s)),
    /* the new gradient layer */
    conic-gradient(#000 0 0) no-repeat 
     50%/calc(100% - 4*var(--s)) calc(100% - 4*var(--s)) intersect,
    /* will intersect with the below layer*/
    radial-gradient(var(--s),#0000 100%,#000) 
     var(--s) var(--s)/calc(2*var(--s)) calc(2*var(--s));
}

Now, we have two radial gradients and two conic gradients. The conic gradients are almost identical so we can simplify the code by adding a variable to avoid repetition:

img {
  --_g:conic-gradient(#000 0 0) no-repeat 50%/;
  mask: 
    radial-gradient(var(--s),#000 100%,#0000 calc(100% + 1px)) 
     0 0/calc(4*var(--s)) calc(4*var(--s)),
    var(--_g) calc(100% - 6*var(--s)) calc(100% - 6*var(--s)),
    var(--_g) calc(100% - 4*var(--s)) calc(100% - 4*var(--s)) intersect,
    radial-gradient(var(--s),#0000 calc(100% - 1px),#000) 
     var(--s) var(--s)/calc(2*var(--s)) calc(2*var(--s));
}

Creating the Second Wavy box

The second wavy box relies on the same gradient configuration: two radial gradients and one conic gradient. Here is a figure to illustrate all of them.

Overview of all the gradients

A first radial gradient will create the outside curves but will also have those inner circles that we hide with the second gradient (a conic gradient). Then, the third gradient will complete the puzzle.

The three gradients above each other

The code will be as follows:

.wavy-box {
  --s: 30px;  /* control the size of the wave */
  --w: 400px; /* preferred  width */
  
  width: round(var(--w),4*var(--s)); 
  aspect-ratio: 1;
  padding: var(--s);
  border: var(--s) solid #0000;
  box-sizing: border-box;
  mask: 
    radial-gradient(calc(sqrt(2)*var(--s)),#000 100%,#0000),
    conic-gradient(#0000 0 0) content-box,
    radial-gradient(calc(sqrt(2)*var(--s)),#0000 100%,#000) 
     var(--s) var(--s) padding-box;
  mask-size: calc(var(--s)*4) calc(var(--s)*4);
}

This time, two gradients are clipped. The radial-gradient is clipped to the padding area like the previous shape and the conic-gradient is clipped to the content area.

With this implementation, we can also have rounded corners for images:

But if you want the shape to apply to the whole image, we have to rely on a different implementation where we can remove the padding and border.

We introduce a fourth gradient and we perform the same intersect composition we did with the previous shape. I let you dissect the code alone as a small exercise.

Conclusion

As I did with the Zig-Zag boxes, I will end with a last demo showing an image gallery of wavy images!

Don’t forget to bookmark my collection of CSS Shapes and check my other articles if you want to learn how to create more CSS shapes:

Discount

Enter Discount Code MOVEME During The SIGN UP Process To Get 90% Off Your First Month

with the discount code

MOVEME

Use Code Now
Jivo Live Chat