-1

The question is very strange indeed, so let me explain with a snippet :

.container {
  width: 200px;
  height: 200px;
  background: rgba(200, 200, 200, .87);
}

.pin {
  position: absolute;
  left: 50px;
  top: 20px;
}

.overlay {
  position: absolute;
  left: 25px;
  top: 40px;
  background: grey;
  border: 1px solid white;
  padding: 12px;
  padding-top: 30px;
}

.overlay:before {
  content: '';
  position: absolute;
  border: 1px solid white;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  top: -30px;
  left: 10px;
}
<div class="container">
  <div class="pin">
    <svg width="24" height="36" viewBox="0 0 192 290" xmlns="http://www.w3.org/2000/svg">
      <path d="
        M11 138
        a 94 94 0 1 1 170 0
        l -85 150
        l -85 -150
      " fill="white" stroke="black" stroke-width="2" stroke-opacity="0.9" opacity="0.9" />
    </svg>
  </div>
  <div class="overlay">
    Content
  </div>
</div>

As you can see in this snippet, there is a pin, with some content in front of it.

I would like the pin to be contained in the white circle, without the content overlapping this circle. Like, if the circle had punched through the content, and removed a little part of it.

I thought about creating a SVG instead of a DIV for the content container (so that the top part of that container has half a circle less), but I'm not sure if it is suited for this case (the content will be dynamic and the width of the content can change).

Is there a way to achieve what I want, with CSS only ?

Thank you in advance.

  • 1
    make the backgroud of the circle the same as the main background? – Temani Afif Jan 08 '19 at 14:28
  • @TemaniAfif I thought I didn't have to state the obvious ... The pin is on a map, and I would like it to be transparent. –  Jan 08 '19 at 14:40
  • is this suitable https://stackoverflow.com/questions/8503636/transparent-half-circle-cut-out-of-a-div ? – Temani Afif Jan 08 '19 at 14:46
  • @TemaniAfif No, if you increase the rectangle width on the JSFIddle, you will see it expends under the circle : I need it to expland at half the circle (Visually, half the circle should be in the content box, the other half should be outside the content box). –  Jan 08 '19 at 14:50
  • I see what you were going for here, but you need a much better example. Instead of starting like a murder mystery ("quite curious indeed"), start with a rendering of what you wanted, map and all. It will clarify to readers that white circle on thing appears to punch through to white background, isn't a sufficient solution. – Chris Moschini May 13 '21 at 03:44

3 Answers3

2

This is possible with an image mask using a radial-gradient (not supported by IE or Edge < 18)

.container {
  width: 200px;
  height: 200px;
  background: rgba(200, 200, 200, .87);
}

.pin {
  position: absolute;
  left: 50px;
  top: 20px;
}

.overlay {
  position: absolute;
  left: 25px;
  top: 40px;
  background: grey;
  border: 1px solid white;
  padding: 12px;
  padding-top: 30px;
}

.overlay:before {
  content: '';
  position: absolute;
  border: 1px solid white;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  top: 0;
  transform: translateY(-50%);
}

.masked-circle {
  -webkit-mask-image: radial-gradient(circle at 50% 0%, transparent 0, transparent 25px, black 25px);
}
<div class="container">
  <div class="pin">
    <svg width="24" height="36" viewBox="0 0 192 290" xmlns="http://www.w3.org/2000/svg">
      <path d="
        M11 138
        a 94 94 0 1 1 170 0
        l -85 150
        l -85 -150
      " fill="white" stroke="black" stroke-width="2" stroke-opacity="0.9" opacity="0.9" />
    </svg>
  </div>
  <div class="overlay masked-circle">
    Content
  </div>
</div>
ksav
  • 20,015
  • 6
  • 46
  • 66
  • in this case, you can simply use the gradient as background instead of mask and you will have better support – Temani Afif Jan 08 '19 at 14:37
  • On my screen, it renders very poorly (all blurry, like removing the background on a picture) and removes the white circle. is that normal ? –  Jan 08 '19 at 14:41
2

You can give the ::before a grey box-shadow to fill the .overlay. The .overlay have overflow:hidden;so the shadow stays inside.

.container {
  width: 200px;
  height: 200px;
  background: rgba(200, 200, 200, .87);

}

.pin::before{
  content: '';
  position: absolute;
  border: 1px solid white;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  top: -10px;
  left: -12px;
}

.pin {
  position: absolute;
  left: 50px;
  top: 20px;
}

.overlay {
  position: absolute;
  left: 25px;
  top: 40px;
  border: 1px solid white;
  border-top:none;
  padding: 12px;
  padding-top: 30px;
  overflow:hidden;
  z-index:1
}

.overlay:before {
  content: '';
  position: absolute;
  border: 1px solid white;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  top: -30px;
  left: 12.3345px;
  background:transparent;
  box-shadow: 0 0 0 100px grey;
  z-index:-1;
}
<div class="container">
  <div class="overlay">
    Content
  </div>
  <div class="pin">
    <svg width="24" height="36" viewBox="0 0 192 290" xmlns="http://www.w3.org/2000/svg">
      <path d="
        M11 138
        a 94 94 0 1 1 170 0
        l -85 150
        l -85 -150
      " fill="white" stroke="black" stroke-width="2" stroke-opacity="0.9" opacity="0.9" />
    </svg>
  </div>
  
</div>
enxaneta
  • 31,608
  • 5
  • 29
  • 42
  • This is also a workaround : I don't need to match the color of the background, I need the circle to **punch through** the content box (meaning the background should be transparent). –  Jan 08 '19 at 15:18
  • 1
    @trichetriche the background is transparent with this solution, change it and you will see – Temani Afif Jan 08 '19 at 15:19
  • Ineed, my bad, sorry :) but the border of the `before` is gone, is there a way to fix it ? –  Jan 09 '19 at 08:14
  • I'm afraid you can't since the the parent have `overflow:hidden`. However you can add a `:before` to the `. container ` to simulate the border – enxaneta Jan 09 '19 at 08:38
  • I've updated my answer. I've added the white border as a `::before` to the `.pin` – enxaneta Jan 09 '19 at 08:57
2

Since the overlay belong to the element you can consider radial gradient and CSS variable in order to have something dynamic (without the border).

I removed the pin for simplicity but you can easily adjust its position to make it inside the circle or use it as background of overlay element:

.container {
  width: 200px;
  height: 120px;
  background: linear-gradient(red,yellow);
  display: inline-block;
  position: relative;
}

.overlay {
  --top: -3px;
  --left: 35px;
  --radius: 24px;
  position: absolute;
  left: 25px;
  top: 40px;
  background: radial-gradient(circle at var(--left) var(--top), transparent 0, transparent var(--radius), grey var(--radius));
  padding: 12px;
  padding-top: 30px;
}

.overlay:before {
  content: '';
  position: absolute;
  border: 1px solid white;
  width: calc(2*var(--radius));
  height: calc(2*var(--radius));
  border-radius: 50%;
  top: var(--top);
  left: var(--left);
  transform: translate(-50%, -50%);/*to keep the same origin*/
  background:url('data:image/svg+xml;utf8,<svg width="24" height="36" viewBox="0 0 192 290" xmlns="http://www.w3.org/2000/svg"><path d=" M11 138 a 94 94 0 1 1 170 0 l -85 150 l -85 -150" fill="white" stroke="black" stroke-width="2" stroke-opacity="0.9" opacity="0.9" /></svg>') center no-repeat;
}
<div class="container">
  <div class="overlay">
    Content
  </div>
</div>
<div class="container">
  <div class="overlay" style="--top:10px;--left:5px">
    Content
  </div>
</div>

<div class="container">
  <div class="overlay" style="--top:50px;--left:5px;--radius:30px;">
    Content
  </div>
</div>
<div class="container">
  <div class="overlay" style="--top:50px;--left:50px;--radius:40px;">
    Content
  </div>
</div>
<div class="container">
  <div class="overlay" style="--top:50px;--left:120px">
    Content
  </div>
</div>

You can also use mask in case you want any background:

.container {
  width: 200px;
  height: 120px;
  background: linear-gradient(red,yellow);
  display: inline-block;
  vertical-align:top;
  position: relative;
  --top: -3px;
  --left: 35px;
  --radius: 24px;
}
.pin {
  position:absolute;
  text-align:center;
  border: 1px solid white;
  width: calc(2*var(--radius));
  height: calc(2*var(--radius));
  border-radius: 50%;
  top: calc(40px + var(--top));
  left: calc(25px + var(--left));
  transform: translate(-50%, -50%);/*to keep the same origin*/
}
svg {
 margin-top:10px;
}
.overlay {
  position: absolute;
  left: 25px;
  top: 40px;
  -webkit-mask-image: radial-gradient(circle at var(--left) var(--top),     transparent 0, transparent var(--radius), black var(--radius));
  background: linear-gradient(yellow,blue);
  padding: 12px;
  padding-top: 30px;
}

.overlay:before {
  content: '';
  position: absolute;
  border-radius: 50%;
}
<div class="container">
  <div class="pin">
    <svg width="24" height="36" viewBox="0 0 192 290" xmlns="http://www.w3.org/2000/svg">
      <path d="
        M11 138
        a 94 94 0 1 1 170 0
        l -85 150
        l -85 -150
      " fill="white" stroke="black" stroke-width="2" stroke-opacity="0.9" opacity="0.9" />
    </svg>
  </div>
  <div class="overlay">
    Content
  </div>
</div>
<div class="container" style="--top:50px;--left:5px;--radius:30px;">
<div class="pin">
    <svg width="24" height="36" viewBox="0 0 192 290" xmlns="http://www.w3.org/2000/svg">
      <path d="
        M11 138
        a 94 94 0 1 1 170 0
        l -85 150
        l -85 -150
      " fill="white" stroke="black" stroke-width="2" stroke-opacity="0.9" opacity="0.9" />
    </svg>
  </div>
  <div class="overlay" >
    Content
  </div>
</div>

<div class="container" style="--top:50px;--left:5px;--radius:30px;">
<div class="pin">
    <svg width="24" height="36" viewBox="0 0 192 290" xmlns="http://www.w3.org/2000/svg">
      <path d="
        M11 138
        a 94 94 0 1 1 170 0
        l -85 150
        l -85 -150
      " fill="white" stroke="black" stroke-width="2" stroke-opacity="0.9" opacity="0.9" />
    </svg>
  </div>
  <div class="overlay" >
    Content
  </div>
</div>
<div class="container" style="--top:50px;--left:50px;--radius:40px;">
<div class="pin">
    <svg width="24" height="36" viewBox="0 0 192 290" xmlns="http://www.w3.org/2000/svg">
      <path d="
        M11 138
        a 94 94 0 1 1 170 0
        l -85 150
        l -85 -150
      " fill="white" stroke="black" stroke-width="2" stroke-opacity="0.9" opacity="0.9" />
    </svg>
  </div>
  <div class="overlay" >
    Content
  </div>
</div>
<div class="container" style="--top:50px;--left:120px">
<div class="pin">
    <svg width="24" height="36" viewBox="0 0 192 290" xmlns="http://www.w3.org/2000/svg">
      <path d="
        M11 138
        a 94 94 0 1 1 170 0
        l -85 150
        l -85 -150
      " fill="white" stroke="black" stroke-width="2" stroke-opacity="0.9" opacity="0.9" />
    </svg>
  </div>
  <div class="overlay" >
    Content
  </div>
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • 1
    You're not really answering my question : my use-case is that I have a pin on a map (which is in a canvas, so I can't touch it). The content is placed at the exact same pixel the pin is on : with some CSS, I can adjust it, and I would like to circle around the pine with a **transparent** background. Like if the circle had **punched through** the content box. Although I have learned something, I can't use this, because it is a workaround not suited for me, and not a solution :/ –  Jan 08 '19 at 15:17
  • @trichetriche in this case edit your question with the real use-case. I am answering based on your question and what you described, not based on the use-case I am not seeing. – Temani Afif Jan 08 '19 at 15:18
  • I have described a punch-trhough use-case. If I wanted the same color, I would have bothered and used a variable to set it to the same color ... And also, if I had wanted a gradient, I would have stated so ... –  Jan 09 '19 at 08:04
  • 1
    @trichetriche the gradient is simply here to illustrate the transparency, It can be an image or a map, so the gradeint has nothing to do with the solution. And where you see any color here? the circle is transparent and *punching* through the background of the content element like you described – Temani Afif Jan 09 '19 at 08:06
  • If you remove the background properties, it stops working. This solution is based on colors, which isn't asked or suited for my case (hence why I didn't talk about it). Furthermore, the pin is immutable, meaning it can't be moved around or used as a CSS property : it is in a **canvas**, so I can't manipulate it. It is again a workaround, not a solution (but thank you, I still learn something though). –  Jan 09 '19 at 08:12
  • 1
    @trichetriche edit your question to include al these information then, There is nothing said that you will not use a solid color for your content, so I provided a solution based on this. And for the pin I simply made *the effort* to reduce the overal code (again based on the question not the use case). You can easily keep the pin outside. Check the update, I have added another method based on your requirement but again, they need to be reflected in your question. There is nothing generic in CSS and everything is workaround based on each situation. – Temani Afif Jan 09 '19 at 08:25
  • And webkit can't be used on all browsers ... This is again a workaround that I can't use :/ and I don't need to state that I don't need a workaround, or that some properties are forbidden. I have asked for a solution and described it the best I could (I'm not a native speaker). If I had to describe everything you can't use, you wouldn't even read it ... And yes, there are generic things in CSS, of course ! Not everything is a workaround. And if I wanted one, I would have asked that, not a solution ... I'm a grown up, I can hear there's no solution to my issue, I won't cry :) –  Jan 09 '19 at 08:30
  • 1
    @trichetriche *all browsers*: again something not said in your question. Well, I will leave this one, because I am pretty sure if I add another way you will then told me you want to support old IE, etc ;) . by the way a solution and workaround are the same and yes there is nothing generic in CSS. Each situation we will lead to a particular solution. Anyway, good luck, probably someone else will anwer with *the* generic solution you are looking for (as a side note, mask can be used with no webkit (https://developer.mozilla.org/en-US/docs/Web/CSS/mask-image), it can also be changed with SVG) – Temani Afif Jan 09 '19 at 08:38
  • BUT IT IS OBVIOUS ! Unless specified otherwise, you should always code with all browsers in mind ... And no, solutions and workarounds aren't the same. I don't feel like explaining it to you, it's again pretty obvious ... I agree I'm a pain in the ass, sorry about that, and thank you for your input anyway, because I've learnt something new. I will look into the mask and its support and see if it fits my need. I can't accept your answer (yet ?), but I gave you an upvote already for the trouble. Thank you again for your help ! –  Jan 09 '19 at 09:10
  • Sorry @trichetriche but I have to disagree with your comment `you should always code with all browsers in mind`. If you need to support older browsers, it is your responsibility to state it as a requirement up front in your question. – ksav Jan 10 '19 at 08:18
  • @ksav not all browser versions, all **browsers**. If you target only chrome, that's already 1/3 of major browsers. If I want support on IE6, I agree that it is specific enough to be mentioned. But webkit ? [Come on ...](https://en.wikipedia.org/wiki/List_of_web_browsers#WebKit-based) –  Jan 10 '19 at 08:25