We recently covered creating fancy borders with CSS mask
properties, and now we are going to cut the corners with CSS mask
and clippath
! A lot of techniques exist to cut different shapes from the corners of any element. In this article, we will consider modern techniques to create unique corner shapes while trying to work from reusable code that allows us to produce different results by adjusting variables.
Check this online tool to get an idea of what we are building. It’s a CSS generator where you select the shape, the corners, and the size then you get the code in no time!
We mainly have two types of cuts: a circular one and an angled one. For each, we can get the full shape or the borderonly shape, not to mention that we can select the corners we want to cut. A lot of combinations!
Like in the previous article, we will make lots of use of the CSS mask property. So, if you are not familiar with it, I recommend reading the quick primer I wrote before continuing.
Circular cutout
For a circular or rounded cut, we will use radialgradient()
. To cut four corners, the logical solution is to create four gradients, one for each corner:
Each gradient is taking a quarter of the element’s dimensions. The syntax of the gradient is selfexplanatory:
radialgradient(circle 30px at top left, #0000 98%, red) top left;
Translated, this renders a circle at the topleft corner with a 30px
radius. The main color is transparent (#0000
) and the remaining is red
. The whole gradient is also placed so that it starts at the element’s topleft corner. Same logic for the three other gradients. The keyword circle
can be omitted since we explicitly specified one value for the radius.
Like I did in the previous article, I will be using slightly bigger or smaller values this time around in order to avoid bad visual result. Here, I am using 98%
instead of 100%
to avoid jagged edges and 51%
instead of 50%
to create an overlap between gradients and avoid white spaces. This logic will follow throughout this article. In fact, you will find that adding or removing 1%
or 1deg
typically results in a nice visual.
We apply this to the CSS mask
property and we are done!
We can actually optimize that code a little:
g: #0000 98%,#000;r: 30px;mask: radialgradient(var(r) at 0 0 ,var(g)) 0 0, radialgradient(var(r) at 100% 0 ,var(g)) 100% 0, radialgradient(var(r) at 0 100%,var(g)) 0 100%, radialgradient(var(r) at 100% 100%,var(g)) 100% 100%;masksize: 51% 51%;maskrepeat: norepeat;
This way, we use custom properties for the redundant values and, as a personal preference, I am using numeric values for the positions instead of keywords.
In the generator, I will use the following syntax:
g: #0000 98%,#000;r: 30px;mask: radialgradient(var(r) at 0 0 ,var(g)) 0 0 /51% 51% norepeat, radialgradient(var(r) at 100% 0 ,var(g)) 100% 0 /51% 51% norepeat, radialgradient(var(r) at 0 100%,var(g)) 0 100%/51% 51% norepeat, radialgradient(var(r) at 100% 100%,var(g)) 100% 100%/51% 51% norepeat;
The shorthand syntax is easier to generate plus the whole value can be used as one custom property.
Can we use fewer gradients if we want?
Sure! One gradient can do the job. Hover the below to see the trick:
Here, we define one radialgradient()
with no size (by default it is 100%
height and 100%
width). This gives us a hole in the center. We translate/move the gradient by half the width and height of the image to move the hole to one corner. Since, by default, the CSS mask repeats, we get the same on each corner. We have four cut corners with only one gradient!
The only drawback of this method is that we need to know the width and height of the element in advance.
Can’t we use 50%
instead of half the width and height?
Unfortunately, we’re unable to do that here because percentages doesn’t behave the same as pixel values when used with the CSS maskposition
property. They’re tricky.
I have a detailed Stack Overflow answer that explains the difference. It deals with backgroundposition
but the same logic applies to the CSS maskposition
property.
However, we can use some tricks to make it work with percentage values and without the need to know the width or the height. When a gradient (or a background layer) has a width and height equal to the element, we cannot move it using percentage values. So we need to change its size!
I will define a size equal to 99.5% 99.5%
. I am reducing 0.5%
from the width and the height to have a value different from 100%
and at the same time keep the same visual result since we won’t notice a big difference between 100%
and 99.5%
. Now that our gradient has a size different from 100%
we can move it using percentage values.
I will not detail all the math, but to move it by half the width and the height we need to use this equation:
100% * (50/(100  99.5)) = 100% * 100 = 10000%
It’s a strange value but it does the job:
mask: radialgradient(30px,#0000 98%,#000) 10000% 10000%/99.5% 99.5%
As you can see, the trick works just fine. Whatever the size of the element is, we can cut four corners using only one gradient. However, this method has a small drawback when the width or the height of the element is a decimal value. Here is an example with an image having a width equal to 150.5px
:
The use of 99.5%
combined with 150.5px
will create rounding issues that will break the calculation, resulting in the mask being misaligned. So, use this method with caution.
Guess what? There is a solution with one gradient and no rounding issue. Using the following code:
mask: radialgradient(30px at 30px 30px,#0000 98%,#000) 30px 30px
The trick is to create a hole placed at the top left corner and by moving it with a negative offset we cover the four corners. Hover the below to see the trick.
This method is perfect as it uses one gradient and has no rounding issue but it has one drawback. The value of the radius is used 5 times. Not a big deal as we can use a custom property for it:
r: 30px;mask: radialgradient(var(r) at var(r) var(r),#0000 98%,#000) calc(1*var(r)) calc(1*var(r))
Let’s quickly recap the three methods we just covered:
 The first method uses four gradients and has no drawbacks as far as usage. Sure, it’s verbose but it works with any kind of element and size.
 The second method uses one gradient, but it can break in some particular cases. It’s suitable with fixedsize elements. It’s ok to use, but maybe less frequently.
 The third method uses one gradient and has no rounding issue. It’s the perfect method among all of them but it requires using the radius many time within the gradient value.
The generator only supports the first and third methods.
Now that we saw the case with all the corners, let’s disable some of them. Using the first method, any corner we want to keep uncut we simply remove its gradient and adjust the size of what remains.
To disable the topright corner:
 We remove the topright gradient (the blue one).
 We have an empty corner, so we increase the size of the red gradient (or the purple one) to cover that leftover space.
Done!
You probably see just how many possibilities and combinations we can do here. If we want to cut N
corners (where N
ranges from 1
to 4
), we use N
gradients. All we need is to correctly set the size of each one to leave no space.
What about the other methods where there’s only one gradient? We will need another gradient! Those two methods use only one radialgradient()
to cut the corners, so we will rely on another gradient to “hide” the cut. We can use a conicgradient()
with four sections for this task:
conicgradient(red 25%, blue 0 50%, green 0 75%, purple 0)
We add it on the top of the radial gradient to get the following:
The conicgradient()
covers the radialgradient()
and no corner is cut. Let’s change one color in the conicgradient()
to transparent. The one at the topright, for example:
Did you see that? We revealed one corner of the radialgradient()
and we end with one cut corner!
Now let’s do the same thing, but for the bottomleft corner.
I think you probably get the trick by now. By changing the colors of the conicgradient()
from opaque to transparent, we reveal the corners we want to cut and gain all kinds of possible combinations.
Now you are probably wondering which method you have to use and when. As I said, I don’t recommend the second method a lot due to the rounding issue but you have to use the two others based on the number of cutouts.
To cut four corners, the first method requires four gradients while the third one requires only one gradient so we use the latter. To cut one corner, the first method requires one gradient while the third one requires two gradients so you use the first one. To cut two corners, both use two gradients, and to cut three corners one method will use three gradients and the other one only two.
By picking the adequate method for each case, we don’t need more than two gradients in total.I have detailed all the methods but in the end, you should pick the optimized code for each case.
Circular borderonly cutout
Let’s make the borderonly version of the previous shape. In other words, we achieve the same shape but knock out the fill so all we’re left with is a border of the shape.
This is a bit tricky because we have different cases with different code. Fair warning, I will be using a lot of gradients here while finding opportunities to trim the number of them.
It should be noted that we will consider a pseudoelement in this case. Showing only the border means we need to hide the inner “fill” of the shape. Applying this to the main element will also hide the content — that’s why this is a nice use case for a pseudoelement.
One cut corner
This one needs one radial gradient and two conic gradients:
The first example illustrates the radial gradient (in red) and both conic gradients (in blue and green). In the second example, we apply all of them inside the CSS mask
property to create the borderonly shape with one cut corner.
As the diagram shows, the radialgradient()
creates the quarter of a circle and each conicgradient()
creates two perpendicular segments to cover two sides. It should be noted that overlapping gradients is not an issue since we are not going to change the CSS maskcomposite
property value.
Using the same code an adjusting a few variables, we can get the shape for the other corners.
Two cut corners
For the twocorner configuration we have two situations taking place.
In the first situation, there are two opposite corners where we need two radial gradients and two conic gradients.
The configuration is almost the same as cutting only one corner: we add an extra gradient and update a few variables.
In the second situation, there are two adjacent corners and, in this case, we need one radial gradient, one conic gradient, and one linear gradient.
“Wait!” you might exclaim. “How come the conic gradient covers three sides?” If you check the code, notice the repeaty
. In all of the examples, we always used norepeat
, but for this we can repeat one of them to cover more sides and reduce the number of gradients we use.
Here is an example with only the conicgradient()
to understand the repetition. The trick is to have a height equal to 100%
minus the border size so that the gradient fills that space when repeating, which covers the third side in the process.
You are probably wondering how one radial gradient is cutting two corners. To do this, we create half a circle that we place at the top left corner. Then by using a negative offset we cut two adjacent corners. Hover the below to understand the trick.
Three cut corners
For this configuration, we need two radial gradients, one conic gradient, and two linear gradients.
Four corners cut
It takes one radial gradient and two linear gradients to cut all four corners.
I can hear you screaming, “How the heck am I supposed to memorize all these cases?!” You don’t need to memorize anything since you can easily generate the code for each case using the online generator. All you need is to understand the overall trick rather than each individual case. That’s why I’ve only gone into fine detail on the first configurations — the rest are merely iterations that tweak the initial foundation of the trick.
Notice there’s a general pattern we’ve been following throughout the examples:
 We add a
radialgradient()
on the corners we want to cut.  We fill the sides using either a
conicgradient()
or alineargradient()
to create the final shape.
It should be noted that we can find different ways to create the same shape. What I am showing in this post are the methods I found to be best after trying lots of other ideas. You may have a different approach you consider to be better! If so, definitely share it in the comments!
Angled cutout
Let’s tackle another type of cut shape: the angled cut.
We have two parameters: the size and angle of the cut. To get the shape, we need a conicgradient()
for each corner. This configuration is very similar to the example that kicked off this article.
Here is an illustration of one corner to understand the trick:
The difference between each corner is an extra offset of 90deg
in from
and the at
position. The full code is like below:
size: 30px;angle: 130deg;g: #0000 var(angle), #000 0;mask: conicgradient(from calc(var(angle)/2  45deg) at top var(size) left var(size),var(g)) top left, conicgradient(from calc(var(angle)/2 + 45deg) at top var(size) right var(size),var(g)) top right, conicgradient(from calc(var(angle)/2  135deg) at bottom var(size) left var(size),var(g)) bottom left, conicgradient(from calc(var(angle)/2 + 135deg) at bottom var(size) right var(size),var(g)) bottom right;masksize: 51% 51%;maskrepeat: norepeat;
If we want to disable one corner, we remove the conicgradient()
for that corner and update the size of another one to fill the remaining space exactly like we did with the circular cut. Here’s how that looks for one corner:
We can do the exact same thing for all the other corners to get the same effect.
In addition to CSS mask, we can also use the CSS clippath property to cut the corners. Each corner can be defined with three points.
The other corners will have the same value with an offset of 100%
. This gives us the final code with a total of 12 points — three per corner.
/* I will define T = [1tan((angle90)/2)]*size */clippath: polygon( /* Topleft corner */ 0 T, size size,0 T, /* OR 0 0 */ /* Topright corner */ calc(100%  T) 0,calc(100%  size) size,100% T, /* OR 100% 0 */ /* Bottomright corner*/ 100% calc(100%  T),calc(100%  size) calc(100%  size), calc(100%  T) 100%, /* OR 100% 100% */ /* Bottomleft corner */ T 100%, size calc(100%  size),0 calc(100%  T) /* OR 0 100% */)
Notice the OR
comments in that code. It defines the code we have to consider if we want to disable a particular corner. To cut a corner, we use three points. To uncut a corner, we use one point — which is nothing but the coordinate of that corner.
The 90deg
special case
When the angle is equal to 90deg
, we can optimize the code of the gradient version and rely on fewer gradients. To cut four corners we can use only one gradient:
size: 30px;mask: conicgradient(at var(size) var(size),#000 75%,#0000 0) 0 0/calc(100%  var(size)) calc(100%  var(size))
This doesn’t remind you of something? It’s exactly similar to the circular cutout! For the 90deg
we have two gradient methods, the first one we detailed previously where each corner is cut with one gradient, and this last method where we cut all the corners using one gradient. I think you know the rest of the story: to uncut some corners we combine the last method with a conic gradient
Two methods with gradients, one with clippath, we have to add a conicgradient?! I am lost …
As I said, no need to remember all the methods and tricks. The generator will do the job of generating the code for you. I simply try to make this article as detailed as possible to cover all the possible cases.
Borderonly angled cut
Oof, we have reached the last and trickiest shape at last! This one can be achieved with either gradients or clippath
, but let’s go with the clippath
approach.
Things would get complex and verbose if we go with the gradient approach. Here’s a demo that illustrates that point:
There are nine gradients total, and I am still not done with the calculation. As you can tell, the thickness of the border is incorrect, plus the final result is unsatisfying due to the nature of gradients and their antialiasing issues. This approach might be a good exercise to push the limit of gradients, but I don’t recommend it in a production environment.
So, back to the clippath
method. We will still wind up with verbose code, but less of a big deal since the generator can do the job for us with a cleaner end result.
Here is an overview of the path. I am adding a small gap to better see the different points but we should have an overlap of points instead.
The way we calculate the outer points is the same as how we did it for the regular angled cut. For the inner points, however, we need more math. Don’t worry, I’ll spare you some “boring” geometry explanation for this one. I know most of you don’t want it, but in case you need to dig into this, you can check the JavaScript file of the generator to find the code and the math I am using to generate the shape.
The 180deg
special case
There’s a special case for the angle cut I want to call out. It’s where we use an angle equal to 180deg
. Here’s what that produces:
We have a straight line on the corner so we can optimize the clippath
code. For the full shape, we can use eight points (two points per corner) instead of 12. And for the borderonly version, we can use 18 points (nine inner points and outer points) instead of 26. In other words, we can remove the middle point.
The borderonly shape can also be made using gradients. But rather than using nine gradients like we did before, we can get away with only four linear gradients and a clean result.
Conclusion
We just combined CSS masks with gradients to create some fancy shapes without resorting to hacks and a lot of code! We also experienced just how much it takes to strike the right balance of code to get the right results. We even learned a few tricks along the way, like changing values by one or even half a unit. CSS is super powerful!
But, as we discussed, the online generator I made is a great place to get the code you need rather than writing it out by hand. I mean, I went through all the work of figuring out how all of this works and I would likely still need to reference this very article to remember how it’s all put together. If you can memorize all of this, kudos! But it’s nice to have a generator to fall back on.
Given the comprehensive article provided, let's break down the concepts and techniques mentioned regarding the use of CSS mask and clippath properties for creating fancy borders and shapes:
1. CSS Mask Property:

Radial Gradient with CSS Mask:
 The
radialgradient()
function creates a gradient that starts from a central point and radiates outward in a circular or elliptical manner.  Multiple radial gradients can be used to create cutout effects on the corners of an element.
 The
mask
property can be set withradialgradient()
values to create shapes or cutout effects on an element.
 The

Optimizing with Custom Properties:
 CSS custom properties (or variables) can be used to store values that are reused across the stylesheet.
 This allows for cleaner and more maintainable code.

Single Radial Gradient Technique:
 Using a single radial gradient without specific dimensions, and then translating/moving it by percentages or specific pixel values to cut corners.
2. Cutting Corners with Different Techniques:

Circular Cutout:
 Utilizes
radialgradient()
with specific sizes and positions to create circular cutouts at corners.  Different techniques were provided based on multiple gradients or a single gradient with specific calculations.
 Utilizes

Corner Disabling:
 To keep a specific corner intact, one can remove the respective gradient and adjust other gradients to fill the void.

Multiple Methods Based on Corner Count:
 Depending on the number of corners to cut, different methods (like using conic gradients) are employed to achieve desired shapes.
3. Angled Cutout:
 Using Conic Gradient for Angled Cuts:
 The
conicgradient()
function creates gradients that radiate outward in a specific angle, allowing for angled cutout effects on corners.  Different angles and sizes can be adjusted to achieve various angled cutout designs.
 The
4. Clippath Property:
 Defining Shapes with Clippath:
 The
clippath
property allows for defining a visible region of an element.  Shapes, including complex ones like cut corners, can be defined using the
polygon()
function with specific points.  Points can be adjusted to create various shapes, and angles can be modified to achieve specific cutout effects.
 The
5. Generator Tool:
 Online CSS Generator:
 An online tool is mentioned that allows users to input parameters like shape, corners, and size to generate corresponding CSS code.
 This tool simplifies the process of creating complex shapes by providing pregenerated code snippets based on user inputs.
Conclusion:
The article provides an indepth exploration of techniques for creating unique and intricate shapes and borders using CSS properties like mask and clippath. From circular cutouts to angled shapes, various methods, optimizations, and considerations are discussed. The use of CSS custom properties, multiple gradients, and mathematical calculations showcases the flexibility and power of CSS for styling and design purposes. Additionally, the mention of an online generator emphasizes the practical application and ease of generating complex CSS shapes without manually writing extensive code.