Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
<head>
<script src="https://rawgithub.com/ai/autoprefixer-rails/master/vendor/autoprefixer.js"></script>
<meta name="description" content="pull off stack css">
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, width=device-width, maximum-scale=1">
  <title>pull off stack css</title>
<style type="unprocessed" id="AutoprefixerIn">%css%</style>
<style id="AutoprefixerOut"></style>
<script>
AutoprefixerSettings = { browsers: ['last 2 versions'] };
document.getElementById("AutoprefixerOut").innerHTML = autoprefixer.process(document.getElementById("AutoprefixerIn").innerHTML, AutoprefixerSettings).css;
</script>
</head>
<body>
<div class="nav"></div>
<div class="stacks">
  <div class="stack">
    <div class="stack-intro">intro</div>
    <div class="stack-pages">
      <div class="stack-page">page 1</div>
      <div class="stack-page">page 2</div>
      <div class="stack-page stack-page--has-fixed">
        <div class="stack-page-fixed">
          <div class="stack-page-fixed-content">
            <div style="text-align: right"><a href="#">page</a> 3 fixed</div>
          </div>
        </div>
        <div class="stack-page-content">
          <span>page 3</span>
        </div>
      </div>
      <div class="stack-page">page 4</div>
      <div class="stack-page">page 5</div>
    </div>
    <div class="stack-after">rest of stuff in site</div>
  </div>
</div>
</body>
</html>
 
@import url('https://necolas.github.io/normalize.css/7.0.0/normalize.css');
:root {
  --viewportHeight: 100vh;
  --navHeight: 3rem;
  /* should also make variant class for fixed foot */
  --stackHeight: calc(var(--viewportHeight) - var(--navHeight));
  
}
*,
*::before,
*::after {
  box-sizing: inherit;
  -webkit-flex-shrink: 0;
  flex-shrink: 0;
}
html {
  box-sizing: border-box;
}
.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: var(--navHeight);
  background: red;
  z-index: 9999;
}
.stack > :first-child {
  margin-top: var(--navHeight);
}
/* only use this if there is 'stack-after' block */
.stack::after {
  content: '';
  display: block;
  height: var(--stackHeight);
  visibility: hidden;
}
.stack-pages {
  position: relative;
  z-index: 999;
  box-shadow: 0 -3px 1px rgba(0,0,0,.2);
}
.stack-page {
  height: var(--stackHeight);
  background: #f60 linear-gradient(to bottom, #f60, #f93);
  
  position: sticky;
  bottom: 0;
  /* this would break fixed inner panel */
  will-change: transform;
  
  
  /* skin */
  display: flex;
  flex-flow: column;
  justify-content: center;
  border-bottom: 20px solid rgba(255,255,255,.1);
  background-size: 100% calc(100% + 20px);
  /* border: 1rem solid #fff; */
  color: #fff;
  padding: 1rem;
  box-shadow: 0 3px 1px rgba(0,0,0,.2);
}
.stack-page--has-fixed {
  /* really can't tell if this helps with the paint at all :/ */
  will-change: initial;
}
/* used so that semantics of document sequence doesn't need to be flipped and using flex-flow: column-reverse on flex container */
.stack-page:nth-child(1) {z-index: 999;}
.stack-page:nth-child(2) {z-index: 998;}
.stack-page:nth-child(3) {z-index: 997;}
.stack-page:nth-child(4) {z-index: 996;}
.stack-page:nth-child(5) {z-index: 995;}
.stack-page:nth-child(6) {z-index: 994;}
.stack-page:nth-child(7) {z-index: 993;}
.stack-page:nth-child(8) {z-index: 992;}
.stack-page:nth-child(9) {z-index: 991;}
/* ... */
/* normal stack is using reveal */
.stack-pages--wipe {
  box-shadow: 0 3px 1px rgba(0,0,0,.2);
}
.stack-pages--wipe .stack-page {
  top: 0;
  /* must set bottom auto, or some browsers try to stick it */
  bottom: auto;
  z-index: auto;
  box-shadow: 0 -3px 1px rgba(0,0,0,.2);
}
.stack-page-content {
  position:relative;
  z-index:1;
  pointer-events: none;
  transform: translate3d(0,0,0);
}
.stack-page-fixed {
  clip: rect(auto,auto,auto,auto);
  clip-path: content-box;
  clip-path: polygon(0% 100%, 0% 0%, 100% 0%, 100% 100%);
  z-index: 0;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  overflow: hidden;
  pointer-events: none;
}
.stack-page-fixed-content {
  position: fixed;
  top: var(--navHeight);
  height: var(--stackHeight); bottom: 0;
  left: 0;
  right: 0;
  /* set to match border on page */
  border-bottom: 20px solid transparent;
  padding: calc(1rem + 20px);
  transform: translate3d(0,0,0);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: stretch;
  
  /* height: calc(var(--stackHeight)  - 20px); */
  /* left: 50%; */
  background: url('//placekitten.com/456/678') center / cover;
}
.stack-page-fixed-content > *,
.stack-page-content > * {
  pointer-events: auto;
}
.stack-intro {
  height: var(--stackHeight);
  background: #09f;
  position: sticky;
  top: var(--navHeight);
  
  /* if nav is transparent and/or movable and want initial intro/page to show through/behind, change here to offset flow/stick */
}
.stack-after {
  position: sticky;
  top: var(--navHeight);
  min-height: var(--stackHeight);
  margin-top: calc(-1 * var(--stackHeight));
  
  /* skin */
  /* padding-top only demo pushin text to bottom */
  padding-top: calc(var(--stackHeight) - 1.25em);
  min-height: calc(1.875 * var(--stackHeight));
  background: #ddd;
}
/* garbage feeing with proximity, but wouldnt want to use manditory because of free scrolling after the stack pages end */
@supports (scroll-snap-type: y proximitys) {
  html, body,
  .stacks {
    scroll-behavior: smooth;
    scroll-snap-type: y proximity;
  }
  /* sentinel nodes that have since been removed */
  .stack-stick {
    display: block;
    scroll-snap-align: start;
  }
}
/* this tries to isolate mobile browsers that resize on scroll, and would be cool applying this to all browsers, but if stacks is the scrolling node on desktop, it makes pointer-events on the fixed block inside a stack-page not scroll the stacks element itself */
/* in other words, on desktop you must scroll the html/body in order for scrolling to work on top of the fixed inner pages */
/* this is suuuuuubpar for hasFixed pages because pointer-events for the scrolling context are completely lost, so clickable/scrollable are completely at odds. But if you ARE NOT USING FIXED then feel free to use this non viewport scroller */
@supports (-webkit-overflow-scrolling: touch) or (display:block-) {
  
  html,
  body {
    height: var(--stackHeight);
    overflow: hidden;
  }
  
  .stacks {
    position: absolute;
    top: var(--navHeight);
    left: 0;
    right: 0;
    height: var(--stackHeight);
    -webkit-overflow-scrolling: touch;
    overflow-x: hidden;
    overflow-y: auto;
  }
  
  .stack > :first-child {
    margin-top: 0;
    top: 0;
  }
  
  .stack-after {
    top: 0;
  }
}
@media
  (max-height:20em),
  (max-width:20em),
  (max-height:30em) and (max-aspect-ratio: 3/4) {
  .stack-page-fixed-content,
  .stack-intro,
  .stack-page,
  .stack-after {
    position: relative;
    height: auto;
    min-height: var(--stackHeight);
  }
  .stack > :first-child {
    top: auto;
  }
  .stack-page-fixed-content {
    position: static;
  }
  .stack-after {
    margin-top: 0;
    top: 0;
  }
  .stack::after {
    display: none;
  }
}
 
// refactored this to not need resize, but just finds vh value in px and sees if it doesn't match window.innerHeight, then setting value to be % of vh
// 100vh = 500, w.iH = 400, --v = 80vh
// all none resize/scrolling browsers will stay 100vh, so resize should be handled
function setHeight() {
  var HTML = document.documentElement;
  var heightEl = document.createElement('div');
  heightEl.style.visibility = 'hidden';
  heightEl.style.position = 'absolute';
  heightEl.style.height = '100vh';
  HTML.appendChild(heightEl);
  var heightElHeight = window.getComputedStyle(heightEl).height;
  var ratioHeight = window.innerHeight / parseInt(heightElHeight,10);
  if (ratioHeight !== 1) {
    var newHeight = (ratioHeight * 100) + 'vh';
    HTML.style.setProperty('--viewportHeight', newHeight);
  }
  HTML.removeChild(heightEl);
}
function ripstop(el) {
  el.addEventListener('touchstart',function(){
    var fromTop = el.scrollTop;
    var fromBot = el.scrollHeight - el.clientHeight - fromTop;
    if (fromBot === 0) {
      el.scrollTop = el.scrollHeight - el.clientHeight - 1;
      return;
    } else if (fromTop > 0) {
      return;
    }
    el.scrollTop = 1;
  });
};
document.addEventListener('DOMContentLoaded', function() {
  setHeight();
  
  var isTouch = CSS.supports('( -webkit-overflow-scrolling: touch )');
  
  if (isTouch){
    ripstop(document.querySelector('.stacks'));
  }
  
});
window.addEventListener("orientationchange", function() {
  setHeight();
});
Output

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
jonjohnjohnsonpro
0viewers