<h1 class="parallax">The &lsquo;root element&rsquo; parallax technique</h1>

<img src="https://source.unsplash.com/category/nature/600x600" class="parallax  skyline">
<img src="https://source.unsplash.com/category/buildings/600x600" class="parallax  blimp">
<img src="https://source.unsplash.com/category/food/600x600" class="parallax  gherkin">
<img src="https://source.unsplash.com/category/people/600x600" class="parallax  dino">
<img src="https://source.unsplash.com/category/technology/600x600" class="parallax  bull">

<div class="parallax  box">
  <p>By combining the power of <code>`rem`</code> and a small JavaScript function to manipulate the <code>`font-size`</code> of the <code>`html`</code> element, we can achieve simple but effective parallaxing.</p>
  <p>Setting all our positions and parallax movements in CSS, this technique minimises DOM manipulations to just one &ndash; on the <code>`html`</code> element &ndash; boosting overall performance, although certainly not better than using 3D transforms.</p>
  <p>The speed and direction of each element is set using margins (for this demo I've used <code>`margin-top`</code>). Play around with the numbers yourself to get a better understanding.</p>
  <p>This is just a proof of concept and hasn't been fully tested, though it should work everywhere that supports <code>`rem`</code> (<a href="http://caniuse.com/#feat=rem">see here</a>). Using <code>`rem`</code> in this way does have its pitfalls &ndash; most notably the loss of its traditional usage &ndash; but it's a nice trick.</p>
</div>

<a href="https://codepen.io/amustill/full/aoFIm" target="_blank" class="parallax  btn">View full screen</a>
/**
 * Parallax styles
 *
 * All aesthetic styles are at the bottom for the benefit of better understanding the demo.
 */
html {
  /* Give our document a fake height for demo purposes. */
	height: 1500px;
  /* We must set out root `font-size` to 0 to prevent our parallax margins being calculated */
  font-size: 0;
	}

body {
  /* Reset the `font-size` to 16px/1em/100% */
  font-size: 16px;
}

/* Fix our parallax elements in position */
.parallax {
	position: fixed;
	}

/**
 * Position our elements
 *
 * Firstly we set our element position using `top`, `right`, `bottom`, `left`, then set your parallax movement using `margin` and `rem`.
 *
 * In this demo our 'base factor' is 50 (see JavaScript). Our 'factor' calculation never exceeds this number (when the viewport is fully scrolled), which means our `rem` calculations are based on this number.
 *
 * Example:
 *
 * If I set my `top` to `200px` and want this element to shift up by `400px`, our `rem` value is 400/10, or `-40em` (using negative margins to shift upwards).
 */
h1 {
	top: 75px;
	margin-top: -3rem;
	z-index: 5;
}

.box {
	top: -1400px;
  /* Positive margin, so it appears from above */
	margin-top: 29rem;
  /* Center and pad */
  left: 50%;
  margin-left: -27%;
	padding: 2%;
	width: 50%;
}

.btn {
  top: 5em;
  right: -800px;
  margin-right: 18.5rem;
}

.skyline {
	top: 240px;
	margin-top: -28rem;
	left: 8%;
}

.blimp {
	top: 320px;
	margin-top: -55rem;
	left: 24%;
}

.gherkin {
	top: 200px;
	margin-top: -20rem;
	left: 40%;
	z-index: 10;
}

.bull {
	top: 360px;
	margin-top: -47rem;
	left: 56%;
}

.dino {
	top: 260px;
	margin-top: -33rem;
	left: 72%;
}


/**
 * Demo aesthetics
 */

html {
	background: #233c38 url('https://goo.gl/5wJBu') 50% 50%;
	background-attachment: fixed;
	background-size: cover;
  font-family: sans-serif;
  font-weight: 300;
  line-height: 1.5;
	color: #fff;
}

a {
  color: #fff;
  text-decoration: none;
  border-bottom: 1px dotted;
}

a:hover {
  color: magenta;
}

h1 {
  width: 100%;
  font: 3em/1 'Oswald', sans-serif;
	text-align: center;
	text-transform: uppercase;
	text-shadow: 3px 3px 20px rgba(0, 0, 0, 0.5);
}

p {
  margin: 0 0 1.5em;
}

p:last-child {
  margin-bottom: 0;
}

img {
	width: 20%;
	box-shadow: 0 0 20px rgba(0, 0, 0, 0.4);
}

code {
  background-color: #222;
  color: magenta;
}

.box {
	background-color: rgba(255, 255, 255, 0.1);
}

.btn {
  display: inline-block;
  padding: 0.5em 1em;
  background-color: rgba(0, 0, 0, 0.5);
  border-bottom: 0;
}
// Variables
var viewport = $(window),
    root = $('html'),
    maxScroll;

// Bind events to window
viewport.on({
  scroll: function() {
    // Grab scroll position
    var scrolled = viewport.scrollTop();

    /**
     * Calculate our factor, setting it as the root `font-size`.
     *
     * Our factor is calculated by multiplying the ratio of the page scrolled by our base factor. The higher the base factor, the larger the parallax effect.
     */
    root.css({ fontSize: (scrolled / maxScroll) * 50 });
  },
  resize: function() {
    // Calculate the maximum scroll position
    maxScroll = root.height() - viewport.height();
  }
}).trigger('resize');

External CSS

  1. https://fonts.googleapis.com/css?family=Oswald:400,300,700

External JavaScript

  1. //cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js