35

How is it possible to create a hole in an overlay where you can see through to the actual website?

#underground {
  background-color: #725;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
}

#overlay {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  overflow: hidden;
}

#overlay #center {
  width: 400px;
  height: 200px;
  background-color: #ABD;
  position: absolute;
  top: 50%;
  left: 50%;
  margin-top: -100px;
  margin-left: -200px;
  border-width: 100%;
  border-color: #FFF;
  border-style: solid;
}
<div id="underground"></div>
<div id="overlay">
  <div id="center"></div>
</div>

I want the <div id="center"> to be transparent so that you can see the <div id="underground">. Is it possible to do this only with CSS or do I have to use some JavaScript?

Penny Liu
  • 15,447
  • 5
  • 79
  • 98
user3019653
  • 465
  • 1
  • 4
  • 7

6 Answers6

109

Yes, this effect is possible.

I would use the css box-shadow with a very large spread radius.

box-shadow: 0 0 0 9999px rgba(0, 0, 255, 0.2);

.hole {
  position: absolute;
  top: 20px;
  left: 20px;
  width: 200px;
  height: 150px;
  box-shadow: 0 0 0 9999px rgba(0, 0, 255, 0.2);
}
<p>Lorem ipsum dolor sit amet, ocurreret tincidunt philosophia in sea, at pro postea ullamcorper. Mea ei aeque feugiat, cum ut utinam conceptam, in pro partem veritus molestiae. Omnis efficiantur an eum, te mel quot perpetua. Ad duo autem dolore, vocent lucilius te cum, ut duo quem singulis.</p>
<p>Has ex idque repudiandae, an mei munere philosophia. Sale molestie id eos, eam ne blandit adipisci. Ei eam ipsum dissentiunt. Ei vel accusam dolores efficiantur.</p>

<div class="hole"></div>
chowey
  • 9,138
  • 6
  • 54
  • 84
  • 5
    Great! If shadow must be restricted to some parent lement, just add `overflow: hidden` for that element. – vladimir83 Oct 11 '18 at 09:37
  • This is great, but the problem is you can only do this once. If you wanted to have multiple of these, the top level one would cover the others. Is there any way to do this with multiple items? – danthegoodman Nov 25 '20 at 20:03
  • Perfect! I would also suggest adding `border-radius: 50%;` if you want a circle shape, works great! – user169771 May 18 '21 at 20:28
  • 1
    replacing the box-shadow property with outline: 9999px solid rgba(0,0,255,0.2) has the same affect and is arguably slightly cleaner – killdash9 Sep 08 '21 at 21:03
19

Thank to CSS clip-path, it is now possible, even drilling any content of the overlay and its background-images / backdrop-filters and even pointer-events.

To do this, we need to define a path made of a first rectangle covering all the viewport, and then an inner shape that will represent our hole. Thanks to the even-odd fill-rule, only our inner shape will actually be part of the clipping-area.

That should have been easy to draw any shape with the path() <basic-shape>, but unfortunately, only Firefox supports it for clip-path, so we have to fallback using the polygon() function which means we have to define every points as vectors.

While it's still readable for a simple square hole:

.hole {
  position: fixed;
  width: 100vw;
  height: 100vh;
  top: 0px;
  left: 0px;
  --rect-size: 75px;
  clip-path: polygon( evenodd,
    /* outer rect */
    0 0, /* top - left */
    100% 0, /* top - right */
    100% 100%, /* bottom - right */
    0% 100%, /* bottom - left */
    0 0, /* and top - left again */
    /* do the same with inner rect */
    calc(50% - var(--rect-size) / 2) calc(50% - var(--rect-size) / 2),
    calc(50% + var(--rect-size) / 2) calc(50% - var(--rect-size) / 2),
    calc(50% + var(--rect-size) / 2) calc(50% + var(--rect-size) / 2),
    calc(50% - var(--rect-size) / 2) calc(50% + var(--rect-size) / 2),
    calc(50% - var(--rect-size) / 2) calc(50% - var(--rect-size) / 2)
  );
  /* can cut through all this */
  background-color: rgba(10, 161, 232, 0.3);
  background-image: url(https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png);
  background-size: 40px;
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  display: flex;
  justify-content: center;
}
.hole > p {
  align-self: center;
  font-size: 18px;
  font-weight: bold;
}
.hole-text {
  font-size: 100px;
}
body { color: black; }
<p>Lorem ipsum dolor sit amet, ocurreret tincidunt philosophia in sea, at pro postea ullamcorper. Mea ei aeque feugiat, cum ut utinam conceptam, in pro partem veritus molestiae. Omnis efficiantur an eum, te mel quot perpetua. Ad duo autem dolore, vocent
  lucilius te cum, ut duo quem singulis.</p>
<p>Has ex idque repudiandae, an mei munere philosophia. Sale molestie id eos, eam ne blandit adipisci. Ei eam ipsum dissentiunt. Ei vel accusam dolores efficiantur.</p>

<div class="hole">
  <p>There is an <span class="hole-text">HOLE</span> here</p>
</div>

Having all the points of a circle for instance would make for a far bigger rule, so if you don't need to cut through pointer-events, then the mask-image solution by Ed Hinchliffe should probably be preferred.

Anyway, here is a JS generator for a circle (the generated rule could still be hard-coded in a CSS file if needed).

function makeCircleHoleClipPathRule( radius ) {

  const inner_path = [];
  const circumference = Math.PI * radius;
  const step = Math.PI * 2 / circumference;
  // we are coming from top-left corner
  const start_step = circumference * (5 / 8);
  for( let i = start_step; i < circumference + start_step; i++ ) {
    const angle = step * i;
    const x = radius * Math.cos( angle );
    const y = radius * Math.sin( angle );
    const str = `calc( 50% + ${ x }px ) calc( 50% + ${ y }px )`;
    inner_path.push( str );
  }
  // avoid rounding issues
  inner_path.push( inner_path[ 0 ] );

  return `polygon( evenodd,
    /* outer rect */
    0 0,       /* top - left */
    100% 0,    /* top - right */
    100% 100%, /* bottom - right */
    0% 100%,   /* bottom - left */
    0 0,       /* and top - left again */
    ${ inner_path.join( "," ) }
   )`;

}

const hole_elem = document.querySelector( ".hole" );
// set the clip-path rule
hole_elem.style.clipPath = makeCircleHoleClipPathRule( 50 );
.hole {
  position: fixed;
  width: 100vw;
  height: 100vh;
  top: 0px;
  left: 0px;
  /* clip-path is set by JS */  
  
  background-color: rgba(10, 161, 232, 0.3);
  background-image: url(https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png);
  background-size: 40px;
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  display: flex;
  justify-content: center;
}
.hole > p {
  align-self: center;
  font-size: 18px;
  font-weight: bold;
}
.hole-text {
  font-size: 100px;
}
body { color: black; }
<p>Lorem ipsum dolor sit amet, ocurreret tincidunt philosophia in sea, at pro postea ullamcorper. Mea ei aeque feugiat, cum ut utinam conceptam, in pro partem veritus molestiae. Omnis efficiantur an eum, te mel quot perpetua. Ad duo autem dolore, vocent
  lucilius te cum, ut duo quem singulis.</p>
<p>Has ex idque repudiandae, an mei munere philosophia. Sale molestie id eos, eam ne blandit adipisci. Ei eam ipsum dissentiunt. Ei vel accusam dolores efficiantur.</p>

<div class="hole">
  <p>There is an <span class="hole-text">HOLE</span> here</p>
</div>

And for other shapes, I'll let the reader handle it ;-)

Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • hey kaiido, i am trying to set the diameter of the circle, but `calc(10000vw / 25vh)` does not seem to return anything? – oldboy Aug 29 '21 at 22:08
  • @oldboy it's quite unclear how you tried to set this value and what you did expect. For the circle you'd have to pass a different `radius` value to the function. However this function only works for radii in `px` because we need to determine the circumference of the circle. You could easily make it work for `vh` or `vw` units (simply multiply the value by innerHeight or innerWidth to get back the value in `px`), but for units like `%` or even `em` we'd need to pass the target element in to be able to compute the correct values in `px`. Doable, but too much for this simple demo. – Kaiido Aug 30 '21 at 01:29
  • hmm i thought viewport units were converted to `px` behind the scenes. i wanted to do it in a pure CSS manner without JS :( thanks anyways – oldboy Aug 30 '21 at 09:11
  • @oldboy The JS is only used to to generate the rule, so you *could* make it only in CSS. However with relative units you need to define how many points your circle will be made of. If you wish you could use an extra big number that would work in most of the cases, here my generator tries to make it as optimal as possible, but I can do so because I did set constraints (using `px`). – Kaiido Aug 30 '21 at 09:17
  • Is it possible to have rounded borders with the polygon approach? – Harshil Sharma Jan 27 '22 at 07:33
  • @HarshilSharma Do you mean to have a rounded rectangular hole? Sure it's possible, but it's part of the "other shapes" I let you handle. But if you just want to make an element have rounded border, then it's simply `border-radius`. – Kaiido Jan 27 '22 at 08:06
  • @Kaiido yes, I wanted to have rectangular hole with rounded corners. – Harshil Sharma Jan 27 '22 at 11:08
8

This is possible, to a degree.

Option 1: Covering element with semi-transparent border

body, html{
    height:100%;
    width:100%;
    padding:0;
    margin:0;
    background:blue;
}
#overlay{
    height:100%;
    width:100%;
    position:fixed;
    border:50px solid rgba(255,255,255,.3);
    box-sizing:border-box;
    top:0;
    left:0;
}
<div id='overlay'></div>

content content content contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent

Option 2: 3x3 grid with the central element fully transparent

body,
html {
  height: 100%;
  width: 100%;
  padding: 0;
  margin: 0;
  position: fixed;
  background: blue;
}
#overlay {
  display: table;
  height: 100%;
  width: 100%;
  position: absolute;
  top: 0;
  left: 0;
}
.row {
  display: table-row;
}
.cell {
  display: table-cell;
  opacity: 0.9;
  background: grey;
}
.row:nth-child(2) .cell:nth-child(2) {
  opacity: 0;
}
<div id='overlay'>
  <div class='row'>
    <div class='cell'></div>
    <div class='cell'></div>
    <div class='cell'></div>
  </div>
  <div class='row'>
    <div class='cell'>&nbsp;</div>
    <div class='cell'>&nbsp;</div>
    <div class='cell'>&nbsp;</div>
  </div>
  <div class='row'>
    <div class='cell'></div>
    <div class='cell'></div>
    <div class='cell'></div>
  </div>
</div>

content content content contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent
contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent
contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent contentcontent
SW4
  • 69,876
  • 20
  • 132
  • 137
  • 1
    Thanks for your code, it almost looks like what i had in mind, the only problem is that the entire second row is transparent and not only the cell in the middle... – user3019653 Nov 27 '13 at 13:03
  • so why don't you just make the other two cells on the sides transparent as well? – Alex Under Nov 28 '14 at 12:48
8

You can now achieve this with relatively good support in new webkit browsers using css clipping (see here for compatability).

For example, the following code would cut a hole with a radius of 100px (so 200px wide) in the center of your element (with a slightly feathered edge).

-webkit-mask-image radial-gradient(100px at 50% 50% , transparent 95%, black 100%)

Here's a codepen to demonstrate.

Ed_
  • 18,798
  • 8
  • 45
  • 71
2

This is not possible. But anyways you can do the border trick: The #overlay itself is transparent but The borders are not. See the fiddle: http://jsfiddle.net/qaXRp/2/

luca
  • 137
  • 1
  • 8
1

No. This is not possible, not in most browsers.

CSS Masking

You can use masking, if you are interested only in new browsers:
Specs: http://www.w3.org/TR/css-masking/
Compatibility: http://caniuse.com/css-masks

Border / Outline

You can also use border or outline css properties if you want to create simular effect and set color of them to transparent so it looks simular.

Position Absolute

You can also use position:

<div z-index:20></div>
<div z-index:10>
    <div z-index:30> // top div is over child of this one
</div>

Transparency and elements

http://css-tricks.com/non-transparent-elements-inside-transparent-elements/
http://css-tricks.com/examples/NonTransparentOverTransparent/
-- this is not what are you asking for, but it can helps you :)

Samuel O
  • 2,258
  • 1
  • 16
  • 26