Getting fancy with CSS’s Position Sticky

Yesterday we published this ambitious story on Esquire about competitive yoga, which apparently is a thing that exists.

It’s the result of an experimental collaboration between a few different departments within Hearst Magazines, each possessing vastly different skillsets but all sharing a love for quality storytelling. The goal: Abandon the various elements on a modern webpage that compete for your attention (ads for instance – of which this article has none) and just tell a damn good story.

I’m proud of how it turned out, and wanted to share some learnings about the nerdy CSS stuff featured in the story.

One of my favorite parts of the story are these little parallaxy breaker images. Parallax can be a dirty word in web design because it’s so easy to screw up, but we developed this method that adds some slick visual candy to the page without performance costs on modern browsers, so I thought I’d walk you through how it’s done.

Ready? Ok, let’s go.

The first step is to get CSS “position:sticky” working in a basic way. In this example, the blue box is sticky and the image element is its sibling. They’re wrapped in a div, which is important to get sticky positioning to work.

Now that the basics are working (and we have silky-smooth browser-native scroll behavior with no JavaScript!) we just need to move things around and adjust margins and padding. Here we go…

First we want the text to be positioned in the center of the screen. We change it from “top:0px” to “top:50%”, and we get this:

The blue box gets sticky at around the right place, but it leaves an ugly gap; that’s where it “lives” in the document flow. So how do we fix that? Easy! Just put a negative margin on the top of the parent wrapper!

To visually clean this up, you can put a white background on whatever div you’re invading with your top negative margin. Then you’ll have something like this…

Ok, so now the text enters the image smoothly but exits lamely. Let’s make the bottom as smooth as the top.

If we add some extra height below the image, it will scoop up the text instead of the image itself. Badass.

To hide that text below the image, we can apply a negative margin just like we did at the top of the parent element.

Sweet! But we’re not quite done… What happens if the user resizes the browser and the height of the box changes? Since the size of the top/bottom margins depends on the height of the text box, this would throw off the vertical alignment. For example:

So how can we fix this?

Who gives a shit!

Kidding… Of course you give a shit. That’s why you’re still here.

Here’s where some sparingly-used JavaScript can come in handy. When the page is resized, you can dynamically calculate the height of the text area, and use that value to set the negative margins we hard-coded above. (Just make sure to “debounce” your function otherwise you might crash someone’s browser and look like a fool.)

So the only thing left is the transparency part. The trick here is to bust out Photoshop and replace the main image with a semi-transparent image, which will become the “foreground”. Then you use the regular image and absolutely position it in the parent container as the “background”.

After some z-index fiddling, you’ve got your effect. The more you mask out of your foreground image, the cooler the effect will be.

(I was lucky to work with Mike Kim, whose attention to detail is amazing. Anyone that can silo hair wins at Photoshop.)

I hope this was helpful. If you have any questions or want to show off anything cool you’re making with CSS position:sticky, hit me up on Twitter.