Cutting Corners With CSS Masks and Clip-Path

CSS mask properties can do far more than create fancy borders. The same technique extends to cutting shapes from an element's corners. Combined with clip-path, you can produce circular cuts, angled cuts, and border-only versions of both — all by adjusting a handful of variables rather than hard-coding each shape.

Two cut types dominate: circular and angled. For each, you can render the full shape or a border-only outline, and you can select which corners to cut. The code stays reusable: you define the shape once and toggle corners on or off.

Circular Cut-Outs

Circular cuts rely on radial-gradient(). To cut all four corners, the straightforward approach is four gradients, each covering a quarter of the element:

radial-gradient(circle 30px at top left, #0000 98%, red) top left;

Each gradient draws a circle at its assigned corner. The main color is transparent (#0000), the background color fills the rest, and the gradient is anchored to the corner. The circle keyword can be omitted since a single radius value is supplied.

Values are fudged slightly in practice — 98% instead of 100% to avoid jagged edges, 51% instead of 50% to create gradient overlap and prevent white gaps. These small offsets make a meaningful visual difference.

The code compresses well with custom properties:

--g: #0000 98%,#000;
--r: 30px;
mask:
  radial-gradient(var(--r) at 0    0   ,var(--g)) 0    0,
  radial-gradient(var(--r) at 100% 0   ,var(--g)) 100% 0,
  radial-gradient(var(--r) at 0    100%,var(--g)) 0    100%,
  radial-gradient(var(--r) at 100% 100%,var(--g)) 100% 100%;
mask-size: 51% 51%;
mask-repeat: no-repeat;

The shorthand form works nicely as a single custom property:

--g: #0000 98%,#000;
--r: 30px;
mask:
  radial-gradient(var(--r) at 0    0   ,var(--g)) 0    0   /51% 51% no-repeat,
  radial-gradient(var(--r) at 100% 0   ,var(--g)) 100% 0   /51% 51% no-repeat,
  radial-gradient(var(--r) at 0    100%,var(--g)) 0    100%/51% 51% no-repeat,
  radial-gradient(var(--r) at 100% 100%,var(--g)) 100% 100%/51% 51% no-repeat;

Can One Gradient Do the Job?

Yes. A single radial-gradient() with default sizing creates a hole in the center. Translate it by half the element's width and height to move the hole to one corner. Because masks repeat by default, the hole appears on all four corners. The catch: you must know the element's dimensions in advance.

Percentage values won't work here because percentages behave differently from pixel values in mask-position calculations. A workaround is to size the gradient to 99.5% 99.5% — just enough below 100% to make percentage positioning legal, while being visually indistinguishable. The positioning math requires a formula with a strange coefficient:

100% * (50/(100 - 99.5)) = 100% * 100 = 10000%
mask: radial-gradient(30px,#0000 98%,#000) 10000% 10000%/99.5% 99.5%

This works for most cases but breaks when the element's width or height is a decimal value like 150.5px. Rounding errors misalign the mask.

A cleaner single-gradient approach avoids rounding entirely by creating a hole at the top-left corner and offsetting it negatively to cover all four corners:

mask: radial-gradient(30px at 30px 30px,#0000 98%,#000) -30px -30px

The radius value repeats five times within the gradient, but a custom property resolves that:

--r: 30px;
mask: radial-gradient(var(--r) at var(--r) var(--r),#0000 98%,#000) calc(-1*var(--r)) calc(-1*var(--r))

Which Method to Use

  • Four gradients: No drawbacks, works with any element and size. Verbose but reliable.
  • One gradient with 99.5% sizing: Breaks with decimal dimensions. Suitable mainly for fixed-size elements.
  • One gradient with negative offsets: No rounding issues, but repeats the radius value. The most versatile single-gradient option.

Cutting Fewer Corners

With the four-gradient method, disabling a corner is simple: remove its gradient and enlarge the adjacent gradients to fill the leftover space. Cutting N corners requires N gradients.

The single-gradient methods need a different strategy. Pair the radial-gradient() with a conic-gradient() divided into four sections:

conic-gradient(red 25%, blue 0 50%, green 0 75%, purple 0)

Set all four conic sections to an opaque color and no corners are cut. Change one section to transparent to reveal that corner's radial cut. Toggle sections individually to control each corner independently.

Choosing between methods comes down to the number of cut corners. For four cut corners, the single-gradient method wins with one gradient. For one cut corner, the four-gradient method wins with one gradient. For two or three corners, both methods require two gradients. You never need more than two gradients total when you pick the appropriate method per case.

Border-Only Circular Cut-Outs

Producing just a border of the circular-cut shape is trickier, because hiding the fill also hides the element's content. This is where a pseudo-element earns its keep.

One Cut Corner

One radial gradient and two conic gradients create the shape. The radial gradient produces the quarter-circle cut, while each conic gradient forms two perpendicular segments covering the sides:

Here’s a diagram of the game plan.

Overlapping gradients present no issue since mask-composite remains unchanged. Adjusting variables produces the shape for different corners.

Two Cut Corners

Opposite corners need two radial gradients plus two conic gradients — an extension of the single-corner case. Adjacent corners improve on this: one radial gradient, one conic gradient, and one linear gradient suffice. The conic gradient uses repeat-y to cover a third side, since its height is 100% minus the border size. The single radial gradient creates half a circle positioned with a negative offset to cut both adjacent corners.

Three and Four Cut Corners

Three cut corners need two radial gradients, one conic gradient, and two linear gradients. Four cut corners take one radial gradient plus two linear gradients.

These configurations are variations on a pattern rather than isolated tricks. Every case follows the same two-step recipe:

  1. Place a radial-gradient() on each corner to cut.
  2. Fill the sides with a conic-gradient() or linear-gradient() to complete the shape.

The individual cases are best generated rather than memorized — an online generator exists precisely for this purpose.

Angled Cut-Outs

Angled cuts add a second parameter beyond size: the cut's angle. Each corner gets its own conic-gradient():

Corners differ by an extra 90deg offset in the from clause and a shifted at position:

--size: 30px;
--angle: 130deg;

--g: #0000 var(--angle), #000 0;
mask:
  conic-gradient(from calc(var(--angle)/-2 -  45deg) 
    at top    var(--size) left  var(--size),var(--g)) top left,
  conic-gradient(from calc(var(--angle)/-2 + 45deg) 
    at top    var(--size) right var(--size),var(--g)) top right,
  conic-gradient(from calc(var(--angle)/-2 - 135deg) 
    at bottom var(--size) left  var(--size),var(--g)) bottom left,
  conic-gradient(from calc(var(--angle)/-2 + 135deg) 
    at bottom var(--size) right var(--size),var(--g)) bottom right;
mask-size: 51% 51%;
mask-repeat: no-repeat;

Disabling a corner means removing its gradient and adjusting another to fill the space — identical to the circular-cut workflow.

Angled cuts also work with clip-path. Each corner is defined by three points:

Zooming in on a corner of the shape showing the three points that form the angled cut.
The shape consists of two points at each end of the cut, and one between them to form the angle.

Other corners offset the same values by 100%, yielding 12 points total:

/* I will define T = [1-tan((angle-90)/2)]*size */
clip-path: polygon(
  /* Top-left corner */
  0 T, size size,0 T, /* OR 0 0 */
  /* Top-right corner */
  calc(100% - T) 0,calc(100% - size) size,100% T, /* OR  100% 0 */
  /* Bottom-right corner*/
  100% calc(100% - T),calc(100% - size) calc(100% - size), calc(100% - T) 100%, /* OR 100% 100% */
  /* Bottom-left corner */ 
  T 100%, size calc(100% - size),0 calc(100% - T) /* OR 0 100% */
)

To exclude a corner, replace its three points with a single point at that corner's coordinate.

The 90deg Special Case

At exactly 90deg, the gradient version compresses dramatically. Four corners need just one gradient:

--size: 30px;
mask: 
  conic-gradient(at var(--size) var(--size),#000 75%,#0000 0) 
  0 0/calc(100% - var(--size)) calc(100% - var(--size))

This mirrors the circular-cut single-gradient approach. For partial cuts, the same conic-gradient overlay method applies.

Border-Only Angled Cut-Outs

The gradient approach to border-only angled cuts escalates quickly — one working version uses nine gradients and still produces an incorrect border thickness with anti-aliasing artifacts. It's a useful exercise in gradient limits but not production-ready.

clip-path offers a cleaner result. The path uses outer points computed identically to the full-shape angled cut, plus inner points that require additional geometry:

We have 13 outer points (the ones in black) and 13 inner points (the ones in blue).

The inner-point math is tedious, which makes a generator the practical way to produce these shapes reliably.

The 180deg Special Case

An angle of 180deg produces a straight line on the corner:

The straight edge eliminates a middle point. The full shape drops from 12 to eight points, and the border-only version drops from 26 to 18. Gradients become viable here too — four linear gradients produce a clean result, unlike the nine-gradient mess required for arbitrary angles.

A Practical Note

The combinations multiply quickly: two cut types, full or border-only, four selectable corners, and optional angle parameters. The underlying pattern remains consistent, but the code for each case differs in gradient counts and point coordinates. Rather than keeping every variant in your head, the online custom-corner generator translates the visual configuration into the correct CSS.