Monday, December 4, 2017

Hide overflow behind stacked div with transparent background on top of body with SVG background

Leave a Comment

I have a layout that contains a fixed div (#navigation) that has buttons. The layout also includes scrollable content (.card).

#navigation currently has a green background for demo purposes. Like so:

#navigation {    position: fixed;    top: 20px;    left: 50%;    transform: translate(-50%, 0%);    background: green;    padding: 25px;  }
<div id="navigation"><button id="btn1">Button</button>    <button id="btn2">Button</button>    <button id="btn3">Button</button>    <button id="btn4">Button</button>  </div>

I would like to hide the any part of any .card element as soon as it goes behind the green background. So, I use z-index stacking order and it works well. Like so:

#card-wrapper {    width: 250px;    margin: 100px auto;  }    .card {    height: 200px;    width: 200px;    background: #131418;    margin: 1em auto;    display: inline-block  }    #navigation {    position: fixed;    top: 20px;    left: 50%;    transform: translate(-50%, 0%);    z-index: 1;    background: green;    padding: 25px;  }    #main {    text-align: center;  }
<div id="main">      <div id="navigation"><button id="btn1">Button</button>      <button id="btn2">Button</button>      <button id="btn3">Button</button>      <button id="btn4">Button</button>    </div>      <div id="card-wrapper">      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>    </div>    </div>

However, I would also like not to use the green background in production. This means that #navigation should not have a background and only the buttons inside should be visible.

So my question is how do I hide the top-side overflow from #card-wrapper as soon as it reaches the hypothetical green background?

#card-wrapper {    width: 250px;    margin: 100px auto;  }    .card {    height: 200px;    width: 200px;    background: #131418;    margin: 1em auto;    display: inline-block  }    #navigation {    position: fixed;    top: 20px;    left: 50%;    transform: translate(-50%, 0%);    z-index: 1;    padding: 25px;    border: 1px solid;    background: transparent  }    #main {    text-align: center;  }    body {    margin: 0 auto;    background: url(http://svgur.com/i/42T.svg);    background-attachment:fixed;    background-size:cover;  }
<div id="main">      <div id="navigation"><button id="btn1">Button</button>      <button id="btn2">Button</button>      <button id="btn3">Button</button>      <button id="btn4">Button</button>    </div>      <div id="card-wrapper">      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>    </div>    </div>

Note, the body element has an SVG background, I cannot add any background to #navigation as it would look bad.

I am open to all solutions CSS/JS/jQuery as long as they do not involve hard-coded values

6 Answers

Answers 1

Try to set same background for #navigation also, with same background-position (see example below)

#card-wrapper {    width: 250px;    margin: 100px auto;  }    .card {    height: 200px;    width: 200px;    background: #131418;    margin: 1em auto;    display: inline-block  }    #navigation {    position: fixed;    top: 0;    left: 0;    right: 0;    z-index: 1;    padding: 40px 25px 25px 25px;    background: #ffffff url(http://svgur.com/i/42T.svg);    background-attachment: fixed;    background-size: cover;    background-position: 0 0;  }    #main {    text-align: center;  }    body {    margin: 0 auto;    background: #ffffff url(http://svgur.com/i/42T.svg);    background-attachment: fixed;    background-size: cover;    background-position: 0 0;  }
<div id="main">      <div id="navigation">      <button id="btn1">Button</button>      <button id="btn2">Button</button>      <button id="btn3">Button</button>      <button id="btn4">Button</button>    </div>      <div id="card-wrapper">      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>    </div>    </div>

Answers 2

A JavaScript Solution

The spirit of the solution:

Tested on CH62, FF57 and IE11

While kastriotcunaku's answer is good if you don't mind the formatting, This solution maintain same parameters as those given, here I'll attempt to give the #navigation a background that looks and is positioned as the body's to give the illusion of transparency using JavaScript to make it responsive.

Background-size:cover on the #navigation div will make it looks denser. So I'll try to emulate cover by stretching the background to cover the whole body and still appearing only on the navigation element by :

navigationElement.style.backgroundSize=(document.body.clientHeight/imgH)*imgW+"px"; 

/* imgW image width, imgH original image height. A trick to maintain the width/height ratio of the SVG. You can programmaticallyget size of the image
For some reason I've needed that even on the body for IE11 who doesn't seem to handle "cover" properly */

For the background positioning however, there are browser differences. Surprisingly enough IE11 is the most straightforward

    n=document.getElementById('navigation');          var isChrome = !!window.chrome && !!window.chrome.webstore;      var isIE = /*@cc_on!@*/false || !!document.documentMode;      var imgH=647.7;      var imgW= 2351.2;           function resizeHandler(){               r=n.getBoundingClientRect();              n.style.backgroundSize=(innerHeight/imgH)*imgW+"px";              if (isChrome){                  n.style.backgroundPosition=r.width/2+"px 0px"              } else if (isIE){                  n.style.backgroundPosition=0          document.body.style.backgroundSize=(innerHeight/imgH)*imgW+"px";              } else{                  n.style.backgroundPosition=-r.x+"px -20px"              }         }             addEventListener("resize",resizeHandler);         document.addEventListener ("DOMContentLoaded",resizeHandler);                  
#card-wrapper {    width: 250px;    margin: 100px auto;  }    .card {    height: 200px;    width: 200px;    background: #131418;    margin: 1em auto;    display: inline-block  }    #navigation {    position: fixed;    top: 20px;    left: 50%;    transform: translate(-50%, 0%);    z-index: 1;    padding: 25px;    border: 0.1px dotted;         background: #ffffff url(http://svgur.com/i/42T.svg);    background-attachment: fixed;  }    #main {    text-align: center;  }    body {    margin: 0 auto;    background:#ffffff  url(http://svgur.com/i/42T.svg);    background-attachment:fixed;    background-size:cover;    background-position:0;  }
<div id="main">      <div id="navigation">      <button id="btn1">Button</button>      <button id="btn2">Button</button>      <button id="btn3">Button</button>      <button id="btn4">Button</button>    </div>      <div id="card-wrapper">      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>    </div>    </div>

Answers 3

I really, really, really, really wanted to find a solution using some new CSS features like clip or backdrop-filter. In the end, I found a solution but it definitely comes up short, and isn't as elegant as kastriotcunaku's solution.

The solution below uses the experimental backdrop-filter property to make the content behind a #screen element have opacity(0). Unfortunately, there are downsides to this:

  • Requires a few lines of JavaScript to set the dimensions of the #screen element to match the #navigation element - this could be avoided if the width/height/position of the #screen element was known in advance
  • Will only work in browsers supporting the backdrop-filter property - mainly Safari, or in Chrome with the Experimental Web Platform features flag enabled (which you can set by going to chrome://flags/#enable-experimental-web-platform-features in Chrome)

The end result is:

enter image description here

If you're using a browser with support for backdrop-filter you can view it in action below:

var screen = document.getElementById("screen");  var navigation = document.getElementById("navigation");  var navigationStyle = window.getComputedStyle(navigation, null);    screen.style.height = navigationStyle.getPropertyValue('height');  screen.style.width = navigationStyle.getPropertyValue('width');  screen.style.border = navigationStyle.getPropertyValue('border');  screen.style.borderColor = "transparent";
#card-wrapper {    width: 250px;    margin: 100px auto;  }    .card {    height: 200px;    width: 200px;    background: #131418;    margin: 1em auto;    display: inline-block  }    #screen,  #navigation {    position: fixed;    top: 20px;    left: 50%;    transform: translate(-50%, 0%);    padding: 25px;  }    #screen {    backdrop-filter: opacity(0%);    -webkit-backdrop-filter: opacity(0%);  }    #navigation {    z-index: 10;    border: 1px solid;  }    #main {    text-align: center;  }    body {    margin: 0 auto;  }    .bkg {    position: fixed;    z-index: 10;    top: 0;    left: 0;    width: 100%;    height: 100%;    background: url(http://svgur.com/i/42T.svg);    background-attachment:fixed;    background-size:cover;      }
<div id="main">      <div id="screen"></div>        <div class="bkg"></div>        <div id="navigation">      <button id="btn1" onclick="alert()">Button</button>      <button id="btn2">Button</button>      <button id="btn3">Button</button>      <button id="btn4">Button</button>    </div>      <div id="card-wrapper">      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>    </div>        </div>

If the width/height of the #screen and #navigation are fixed we do away with the JavaScript.

#card-wrapper {    width: 250px;    margin: 100px auto;  }    .card {    height: 200px;    width: 200px;    background: #131418;    margin: 1em auto;    display: inline-block  }    #screen,  #navigation {    position: fixed;    top: 20px;    left: 50%;    transform: translate(-50%, 0%);    padding: 25px;    width: 280px;    height: 71px;    box-sizing: border-box;  }    #screen {    backdrop-filter: opacity(0%);    -webkit-backdrop-filter: opacity(0%);  }    #navigation {    z-index: 10;    border: 1px solid;  }    #main {    text-align: center;  }    body {    margin: 0 auto;  }    .bkg {    position: fixed;    z-index: 10;    top: 0;    left: 0;    width: 100%;    height: 100%;    background: url(http://svgur.com/i/42T.svg);    background-attachment:fixed;    background-size:cover;      }
<div id="main">      <div id="screen"></div>        <div class="bkg"></div>        <div id="navigation">      <button id="btn1" onclick="alert()">Button</button>      <button id="btn2">Button</button>      <button id="btn3">Button</button>      <button id="btn4">Button</button>    </div>      <div id="card-wrapper">      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>    </div>        </div>

Answers 4

You can use your navigation as fixed navigation and only allow scroll to your content area. For that you have to add position: absolute; to #card-wrapper and remove display:inline-block from .card .

#card-wrapper {    position: absolute;    top: 100px;    bottom: 0;    left: 0;    right: 0;    overflow: auto;  }    .card {    height: 200px;    width: 200px;    background: #131418;    margin: 1em auto;    clear:both;  }    #navigation {    position: fixed;    top: 20px;    left: 50%;    transform: translate(-50%, 0%);    z-index: 1;    padding: 25px;    border: 1px solid;    background: transparent  }    #main {    text-align: center;  }    body {    margin: 0 auto;    background: url(http://svgur.com/i/42T.svg);    background-attachment:fixed;    background-size:cover;  }
<div id="main">      <div id="navigation"><button id="btn1">Button</button>      <button id="btn2">Button</button>      <button id="btn3">Button</button>      <button id="btn4">Button</button>    </div>      <div id="card-wrapper">      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>    </div>    </div>

Answers 5

After a nice bit of trial and errors, I've got this solution that I think it's the best so far

#card-wrapper {    width: 250px;    margin: 100px auto;  }    .card {    height: 200px;    width: 200px;    background: #131418;    margin: 1em auto;    display: inline-block  }    #navigation {    position: fixed;    top: 20px;    left: 25%;    right:25%;    z-index: 1;    padding: 25px;    border-top: 0.1px dotted;        border-bottom: 0.1px dotted;    background: white url(http://svgur.com/i/42T.svg) top left/cover no-repeat fixed;  }    #main {    text-align: center;  }    body {    margin: 0 auto;    background: white url(http://svgur.com/i/42T.svg) top left/cover no-repeat fixed;  }
<div id="main">      <div id="navigation">      <button id="btn1">Button</button>      <button id="btn2">Button</button>      <button id="btn3">Button</button>      <button id="btn4">Button</button>    </div>      <div id="card-wrapper">      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>    </div>    </div>

Answers 6

If you don't mind to have fixed value for the height of the buttons - then your task can be solved through background-clip style. In example below I've also added click handlers for cards and buttons to demonstrate that cards are still clickable.

$('.card').on('click',function(){    $(this).addClass('clicked');  })  $('button').on('click',function(){    $('.card').removeClass('clicked');  })
#card-wrapper {    width: 250px;    margin: 100px auto;  }    .card {    height: 200px;    width: 200px;    background: #131418;    margin: 1em auto;    display: inline-block  }    #navigation {    background-image: url(http://svgur.com/i/42T.svg);    background-attachment: fixed;    background-size: cover;    background-clip: content-box;    border: 0 none;    padding: 0;    top: 0;    left: 0;    bottom: 0;    right: 0;    position: fixed;    z-index: 1;    box-sizing: border-box;  /**  calc() calculation values:   25px - .buttons-list padding     20px - top offset of #navigation (it is also padding-top value)   30px - button height;  */    padding: 20px 0 calc(100vh - 25px * 2 - 20px - 30px);    pointer-events: none;  }    button {    height: 30px  }    #main {    text-align: center;  }    body {    margin: 0 auto;    background: url(http://svgur.com/i/42T.svg);    background-attachment:fixed;    background-size:cover;  }    .buttons-list {    padding: 25px;    pointer-events: initial;  }    /* Just for click events demonstration */  .card.clicked{    background-color: green;  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>  <div id="main">      <div id="navigation">      <div class="buttons-list">        <button id="btn1">Button</button>        <button id="btn2">Button</button>        <button id="btn3">Button</button>        <button id="btn4">Button</button>      </div>    </div>      <div id="card-wrapper">      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>      <div class="card"></div>    </div>    </div>

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment