2

I'm trying to create a block with a smooth, curved cut-out in the top-right corner, similar to the image below:

enter image description here

Currently, my implementation looks like this:

enter image description here

As you can see, the current curve is not as smooth as I'd like it to be - It's essentially not a curve, but a square with straight angles. Here's my current code:

body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  margin: 0;
  background-color: #f0f0f0;
  font-family: Arial, sans-serif;
}

.curved-block {
  width: 300px;
  background-color: white;
  border-radius: 10px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
  position: relative;
  overflow: hidden;
  padding: 5px;
}

.curved-block::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  width: 40px;
  height: 40px;
  background-color: #f0f0f0;
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E%3Cpath d='M0 0 h25 c5 0, 10 0, 13 3 s2 7, 2 12 v25 h-40 z' fill='%23000000'/%3E%3C/svg%3E");
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E%3Cpath d='M0 0 h25 c5 0, 10 0, 13 3 s2 7, 2 12 v25 h-40 z' fill='%23000000'/%3E%3C/svg%3E");
  mask-size: cover;
  -webkit-mask-size: cover;
}

.icon {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 23px;
  height: 23px;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 16px;
  font-weight: bold;
  background-color: #0056b3;
  border-radius: 50%;
  color: white;
  z-index: 2;
}
<div class="curved-block">
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
  <span class="icon">+</span>
</div>

I'm struggling to achieve a smoother, more natural-looking curve in the SVG mask. How can I modify the SVG path to create a curve that looks more like the one in the desired result image? Thank you in advance for your suggestions.

0

2 Answers 2

5

I did it here: https://css-shape.com/inverted-radius/

You can easily copy the code and adjust the variables:

.inverted-radius {
  --r: 15px; /* the radius */
  --s: 30px; /* the size of the corner*/
  
  height: 150px;
  margin: 10px;
  background: #3FB8AF;
  border-radius: var(--r);
  --_m:/calc(2*var(--r)) calc(2*var(--r))
    radial-gradient(#000 70%,#0000 72%) no-repeat;
  mask:
    right calc(var(--s) + var(--r)) top 0 var(--_m),
    right calc(var(--s) + var(--r)) var(--_m),
    radial-gradient(var(--s) at 100% 0,#0000 99%,#000 101%) 
     calc(-1*var(--r)) var(--r) no-repeat,
    conic-gradient(at calc(100% - var(--s) - 2*var(--r)) calc(var(--s) + 2*var(--r)),
     #0000 25%,#000 0);
}
<div class="inverted-radius"></div>

Sign up to request clarification or add additional context in comments.

4 Comments

I've changed my mind and marked your answer as the best because although it's more confusing in terms of calculations, it has less problems on different browsers.
@temani-afif Heads-up: snippet's outcome here has all corners uniform in Firefox, like the mask is ignored or something. (Generator at your page works OK, though.)
@myf it's fixed now
is there a way to add border that doesn't get cut out?
2

Since you're using uniformly rounded corners you may also combine a CSS clip-path with a SVG "goo" filter.

body {
  font-family: Arial, sans-serif;
  margin: 0;
  background-color: #f0f0f0;
  padding: 3em;
}

:root{
  --icon-space: 2em;
  --drop-shadow: drop-shadow(2px 2px 2px rgba(0, 0, 0, 0.75))
}

.curved-block-outer {
  position: relative;
}

.curved-block {
  background-color: #fff;
  padding: 0.5em 2.5em 0.5em 0.5em;
  clip-path: polygon( 0% 0%, calc(100% - var(--icon-space)) 0%, calc(100% - var(--icon-space)) var(--icon-space), 100% var(--icon-space), 100% 100%, 0% 100%);
}

/* round corners via SVG filter; add drop shadow */
.curved-block-filter {
  filter: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='goo'><feGaussianBlur in='SourceGraphic' stdDeviation='7.5' result='blur'/><feColorMatrix in='blur' mode='matrix' values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 19 -9' result='goo'/><feComposite in='SourceGraphic' in2='goo' operator='atop'/></filter></svg>#goo") var(--drop-shadow);
}

.icon {
  position: absolute;
  right: 0;
  top: 0;
  z-index: 10;
  width: 1.5em;
  height: 1.5em;
  display: block;
  text-align: center;
  font-size: 1em;
  line-height: 1.6em;
  font-weight: bold;
  background-color: #0056b3;
  border-radius: 50%;
  color: #fff;
  filter: var(--drop-shadow);
}
<div class="curved-block-outer ">
  <span class="icon">+</span>
  <div class="curved-block-filter">
    <div class="curved-block">
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
    </div>
  </div>
</div>

<!-- hidden SVG filter for rounded corners -->
<svg style="position:absolute;width:0;height:0">
    <filter id="goo">
      <feGaussianBlur in="SourceGraphic" stdDeviation="7.5" result="blur" />
      <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 19 -9" result="goo" />
      <feComposite in="SourceGraphic" in2="goo" operator="atop" />
    </filter>
</svg>

We're basically

  • drawing a polygon with 6 vertices with relative units
  • apply a SVG filter to round the rendering of this polygon

Unfortunately we need some additional wrapping elements otherwise we can't apply a drop shadow or the plus-button is also affected by the filters.

Goo filter

You can either reference the filter by inlining a SVG in your HTML.
**Update: ** It seems like iOS safari struggles with filters inlined as data-URLs. For a reliable cross-browser rendering better append a hidden SVG element in the HTML body.

<svg style="position:absolute;width:0;height:0">
    <filter id="goo">
      <feGaussianBlur in="SourceGraphic" stdDeviation="7.5" result="blur" />
      <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 19 -9" result="goo" />
      <feComposite in="SourceGraphic" in2="goo" operator="atop" />
    </filter>
</svg>

and apply it in CSS like so:

filter: url("#goo")

or you can encode the SVG to a dataURL and embed it directly in CSS

filter: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='goo'><feGaussianBlur in='SourceGraphic' stdDeviation='7.5' result='blur'/><feColorMatrix in='blur' mode='matrix' values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 19 -9' result='goo'/><feComposite in='SourceGraphic' in2='goo' operator='atop'/></filter></svg>#goo")

The stdDeviation='7.5' value specifies the border/rounding radius.

See also

2 Comments

Thank you, great answer and very educational. Thanks for the articles and explanation, I will be able to do similar tricks in the future.
@Alex Nikolsky: I just discovered iOS safari can't use CSS data url filters. So I recommend to append the SVG to your body. See updated example

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.