JSFiddle - React, Tailwind, and code Playground

Sticky header and footer with shadow when above content.

by Travis Almand

HTML

<header></header>
<main>
  <div class="top-buffer"></div>
  <div class="content"></div>
  <div class="content"></div>
  <div class="content"></div>
  <div class="content"></div>
  <div class="content"></div>
  <div class="content"></div>
  <div class="content"></div>
  <div class="bottom-buffer"></div>
</main>
<footer></footer>

SCSS

html {
  box-sizing: border-box;
}
*, *:before, *:after {
  box-sizing: inherit;
}
html,
body {
  margin: 0;
  padding: 0;
}
body {
  display: flex;
  flex-direction: column;
  height: 100vh;
}

header {
  background-color: rgba(255, 255, 255, 0.5);
  border-bottom: 1px solid gainsboro;
  box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.5);
  height: 64px;
  left: 0;
  position: fixed;
  top: 0;
  transition: box-shadow 250ms;
  width: 100%;
  
  &.shadow {
    box-shadow: 0 0 16px 0 rgba(0, 0, 0, 0.5);
  }
}

main {
  flex-grow: 1;
  
  .top-buffer {
    height: 64px;
  }
  .content {
    background-color: gainsboro;
    height: 300px;
    margin: 20px;
  }
  .bottom-buffer {
    height: 64px;
  }
}

footer {
  background-color: rgba(255, 255, 255, 0.5);
  border-top: 1px solid gainsboro;
  bottom: 0;
  box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.5);
  height: 64px;
  left: 0;
  position: fixed;
  transition: box-shadow 250ms;
  width: 100%;
  
  &.shadow {
    box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.5);
  }
}

JavaScript

const header = document.querySelector('header');
const footer = document.querySelector('footer');
const topBuffer = document.querySelector('.top-buffer');
const bottomBuffer = document.querySelector('.bottom-buffer');
const options = {
  root: null,
  rootMargin: '0px',
  threshold: 1
}
const observer = new IntersectionObserver(callback, options);

observer.observe(topBuffer);
observer.observe(bottomBuffer);

function callback (entries, observer) {
	entries.forEach(entry => {
  	const target = entry.target;
    
    if (entry.target === topBuffer) {
    	header.classList.toggle('shadow', !entry.isIntersecting);
    }
    
    if (entry.target === bottomBuffer) {
    	footer.classList.toggle('shadow', !entry.isIntersecting);
    }
  });
}