Build Multiple Stacking Sticky Sidebars with Pure CSS and Bootstrap 4

Nicholas Cerminara
👁️ 4,865 views
💬 comments

Making high performant, pure CSS sticky sidebars that stack with Bootstrap 4.

This will be a quick and pretty cool tutorial on a neat trick on how to have multiple sticky sidebars that stack without using any JavaScript!

I figured this out the other day brain storming ideas with @chrisoncode for the new Scotch website sidebar. As fun and cool as JavaScript is, it's just not as snappy and way more bloated than say a pure CSS implementation of stuff like this (which is one of our main goals for our Scotch.io redesign).

Table of Contents

    In this tutorial I will discuss:

    • What the heck I mean by this mouthful: "Multiple Stacking Sticky Sidebars.
    • Reasons that you would want to do this.
    • General beefs devs have with doing it with JavaScript or plugins.
    • The technique with CSS3 (position: sticky).
    • That same technique with a simple trick I figured out to make it stackable with pure CSS.
    • Bunch of Bootstrap 4 Demos with Dead-Simple Sticky Sidebars

    I'll also have many code samples and demos throughout this post. Then some simple demos / templates to show you how easy it is with Bootstrap 4.

    It's so easy, in a lot of ways I am scared to publish this post because of the abuse of the design pattern by websites. You'll see more as you scroll and read below, but let's just roll with the future punches and begin.

    What the heck does "Multiple Stacking Sticky Sidebars" even mean?

    I'm talking specifically about this design pattern where there's a single sidebar with multiple sticky content in it:

    The word "multiple" regerences if we do more than one of these on page. Stacking references to the single sidebar having multiple stacking pieces.. I know that this is kind-of confusing and weird, but I couldn't find or think up a better term for the concept.

    Don't worry though, we'll get to that craziness soon enough of "Multiple Stacking Sticky Sidebars"...

    So why do this?

    There's a couple reason why you would probably want to do this. Just to name a few:

    1. It's cool, different.
    2. Maybe make a subnav easily accessible.
    3. You want to highlight your content in your sidebar more as a user scrolls.
    4. You want (have to?) increase your ad impressions, clicks, and page views.

    The Old Way (with JavaScript)

    There's a ton of plugins for doing this.

    You've probably even done this before with maybe Boostrap Affix. I find plugins like this ALWAYS are a pain to get working and / or make scrolling feel slower, laggier. Or, maybe something weird happens on resize.

    It's also pretty easy to write a small script for it yourself, but I know most people likely use plugins.

    How the JS Basically Works

    The general idea is your script or plugin will do something like this with JavaScript:

    1. Calculate offset of a position: relative; element from top of window on scroll.
    2. When that element's offset is or is about 0, calculate the position (left, top, or right) of that element against the window.
    3. switch it to position: fixed; with the calculated top, left, or right.
    4. Optionally, have your JS redo these calculations when it reaches the bottom of it's parent.
    5. Switch it to position: absolute; and position it at bottom of the container.

    It's not too bad of work, but it's definitely a lot to juggle even if you have a plugin trying to figure this all out for you.

    That Usual Snag #1: Content Jumping

    I'm sure you've dealt with this before! There's always a couple bugs you hit.

    When you switch from position: relative; to position: fixed; the height is removed from the parent element. This means there might be a "jump" up of content.

    Check out this quick and dirty demo of what I mean:

    That Usual Snag #2: Width 100% vs Width Auto

    If the element you want to be "sticky" is currently position: relative; and responsive to the container (width: 100%) and then you swap to position: fixed, you'll get an element that is 100% of the window or at width auto depending on your CSS.

    Here's our demo again. This time watch the width of the "sticky" element.

    Just another thing you'd have to calculate.

    That Usual Snag #3: Performance Hell

    Finally, if you have a crazy website, with multiple sticky sidebars on a single page, that's a lot of JavaScript calculations happening on scroll. You are definitely going to nuke your user's CPU.

    "I love it when I visit a webpage and my computer fan starts howling, Chrome eats all my computer RAM, and my CPU goes nuclear. Lag is cool!" - Said no one ever

    CSS-Tricks has a great tutorial explaining through this issue, but, to be frank and honest in my personal opinion, dealing with this "just sucks in general". There's got to be some serious good reason to be in this territory for most simple projects.

    Here's a codepen showing that off, forked from Chris Coyier's Codepen in that article:

    Easy Sticky Sidebars without JavaScript using "Position Sticky"

    This is no big secret if you've been paying attention to CSS3. You can now make any element essentially behave this way with pure CSS. It's easy.

    You just need to have the CSS applied and at least one direction property which is usually the top:

    .make-me-sticky {
        position: sticky;
        top: 0;
    }

    Now, all elements with the class will be "sticky to the top of the container, all the way to the bottom.

    Below are some cool demos with Bootstrap 4:

    Basic Sticky Sidebar with Bootstrap 4

    Browser Support

    Like anything fun with CSS3 its browser support is all over the place, but honestly this one is not that bad. I'm surprised it's not used more.

    If you can afford to have IE users not have a sticky elements, it's perfect. You wouldn't have to worry about any complex fallback because it just will simply not be sticky.

    /* Cross-Browser Support */
    .make-me-sticky {
        position: relative; /* I am the fallback */
    
        /* Give it everything you got! (use an auto prefixer...) */
        position: -webkit-sticky;
        position: -moz-sticky;
        position: -ms-sticky;
        position: -o-sticky;
        position: sticky;
    
        top: 0; /* Required  */
    }

    Stacking Sticky Sidebars without JavaScript

    Now onto the thing that prompted me to write this entire article in the first place! Let's use this cool new CSS3 thing with a clever technique to have a dynamic "stacking sticky sidebar" design pattern as I discussed at the top of the article.

    To reiterate, our goal is:

    • Have a sticky sidebar.
    • Space out a bunch of them to show multiple items sticky separately.
    • Don't use JavaScript.
    • Keep it simple.

    Multiple Stacking Sticky Sidebar Demo with Pure CSS

    First, here's a demo of it working in action. This is 100% CSS:

    How it Works

    It's actually pretty simple. We know how to use CSS sticky and that it just affixes an element to the top and the bottom of it's container based on scroll position.

    So all we need to do, is add a bunch of containers evenly split and put sticky content inside of them. See this blueprint of what we want to create with them marked container 1, container 2, container 3, and container n:

    Multiple Sticky Sidebars

    An easy trick to simulate the equal heights would be using position: absolute;. Here's some sample code:

    /* "TITLE HERE" from above */
    .title-section {
        width: 100%;
        height: auto
    }
    
    /* "CONTENT" From Above */
    .content-section {
    
         /* size of my container minus sidebar width */
        width: calc(100% - 300px);
    
        /* Estimated height of largest sidebar in case of short content */
        min-height: 800px;
    }
    
    /* SIDEBAR */
    .sidebar-section {
        position: absolute;
        right: 0;
        top: 0;
        width: 300px;
        height: 100%; /* Super important! */
    }
    
    /* "SIDEBAR CONTAINER" in the blueprint */
    .sidebar-item {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 25%;
    
        /* Position the items */
        &:nth-child(2) { top: 25%; }
        &:nth-child(3) { top: 50%; }
        &:nth-child(4) { top: 75%; }
    }

    Then, the HTML:

    <article>
        <div class="container-fluid">
            <div class="col-md-12">
    
                <div class="title-section">
                    <h1>TITLE HERE</h1>
                </div>
    
                <div class="inner-guts">
    
                    <div class="content-section">
                        <h2>Content Section</h2>
                    </div>
    
                    <div class="sidebar-section">
    
                        <div class="sidebar-item">
                            <h3>Item 1</h3>
                        </div>
                        <div class="sidebar-item">
                            <h3>Item 2</h3>
                        </div>
                        <div class="sidebar-item">
                            <h3>Item 3</h3>
                        </div>
                        <div class="sidebar-item">
                            <h3>Item 4</h3>
                        </div>
    
                    </div>
    
                </div>
            </div>
        </div>
    </article>

    And finally a demo:

    Next, Let's Make it Sticky

    Now, to make it sticky we just need that CSS property in each of the sidebar-items. So, add these lines of code:

    .make-me-sticky {
        position: sticky;
        top: 0;
    }
    <!-- Repeat this -->
    <div class="sidebar-item">
        <div class="make-me-sticky">
    
        </div>
    </div>
    
    ...
    
    ...

    That is it! Pretty simple. We're just using position: absolute; to build the layout we want then position: sticky; to do all our work. Here is the blueprint live:

    Issues you'll face

    Real quick, I just want to flag issues you'll face with this approach:

    • Maybe the content container is shorter than the sidebar.
    • Maybe the sidebar items height are taller than their individual container
    • Maybe you have different quantities than 4 for your sidebar.

    Nothing is perfect, but this is pretty great technique. You'll have to adjust the CSS here for your individual use case, but it's not a bad solution at all given we have no javascript.

    Make it a Helper Library / Mixin for Different Quantities

    For dynamically changing content, I would do something like this in CSS. You could probably make a smarter SASS / LESS mixin though:

    .sidebar-item {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
    }
    .has-4-items .sidebar-item {
        height: 25%;
        &:nth-child(2) { top: 25%; }
        &:nth-child(3) { top: 50%; }
        &:nth-child(4) { top: 75%; }
    }
    .has-3-items .sidebar-item {
        height: 33.333333%;
        &:nth-child(2) { top: 33.333333%; }
        &:nth-child(3) { top: 66.666666%; }
    }
    .has-2-items .sidebar-item {
        height: 50%;
        &:nth-child(2) { top: 50%; }
    }

    A Better Way? Bootstrap and Flexbox?

    Okay, so in the above example you'll notice I used calc of exactly 300px for the sidebar width. I did this because ad units are that size, so it's basically become the standard on the web for a sidebar.

    With Bootstrap 4, their grid system is flexbox by default. This means a bunch of awsome things! The biggest for most users are columns are equal height by default with Bootstrap 4.

    Check out this demo proving that:

    Pretty amazing right? Now, let's leverage that with sticky sidebars.

    Instead of doing the calc trick, we'll just use a Bootstrap 4 grid system. The only thing we need to update on our code is to make sure the .make-me-sticky has left and right padding that matches your column gutters.

    So default setup will be:

    .make-me-sticky {
        position: sticky;
        top: 0;
    
        /* For Bootstrap 4 */
        padding: 0 15px;
    }

    Check out the demo below:

    Multiple Stacking Sticky Sidebars

    Okay, let's just get crazy with this Bootstrap example. Let's do this in multiple columns at different quantities with just CSS.

    This is super performant, snappy, and super easy! This however does scare me a bit for the future of a lot of website designs if this type of pattern catches on and is abused. Ads galore...

    Responsive

    I didn't handle for responsive here or that much in any of the demos, but you can probably figure that one out on your own. You simply need to just disable sticky whenever you have your columns stack.

    "Wait... Couldn't I just use Flexbox to split the sidebar sections automatically instead of calculating them?" - You

    So the most annoying thing about this is having to configure the has-4-items, has-3-items, etc. What if you could use Flexbox to space the .sidebar-item evenly and automatically regardless of count?

    It would be something like this:

    .sidebar-section {
        display: flexbox;
        flex-direction: column;
        justify-content: space-between;
    }

    This would have been a game changer, but despite this great idea, sticky just doesn't work well with Flexbox. Give it a try if you'd like. If anyone can figure it out, please let me know!

    Conclusion

    This was a fun, quick tutorial on how to use position: sticky; with Bootstrap 4 to create multiple sticky sidebars or multiple multiple sticky sidebars.

    It's really easy to do. I have no doubt that most if not all ad based websites will start doing this more and more. I just hope it's not abused.

    Either way, thank you for following this tut. I hope you enjoyed it as much as I did!