Skip to content

Instantly share code, notes, and snippets.

@amelieykw
Last active April 10, 2018 08:06
Show Gist options
  • Save amelieykw/616fac51ce31c30398fe486d13be277d to your computer and use it in GitHub Desktop.
Save amelieykw/616fac51ce31c30398fe486d13be277d to your computer and use it in GitHub Desktop.
[BOOTSTRAP SIDEBAR] #Bootstrap #Sidebar #Collapsible

Original Website

Here is what we're going to build:

Demos :

  1. DEMO #1
  2. DEMO #2
  3. DEMO #3
  4. DEMO #4
  5. DEMO - BONUS SECTION

Download sources

0 - Basic files

Before we dig into coding, we should first set up our workflow with the necessary files.

In all four cases we'll go through today, we will need Bootstrap CSS and JS files, jQuery library, and our custom stylesheet.

So, our startup markup should be as following:

<html !DOCTYPE>
<head>
    <title>Bootstrap 3 Collapsible Sidebar</title>
    <!-- Bootstrap CSS CDN -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <!-- Our Custom CSS -->
    <link rel="stylesheet" href="css/custom.css">
</head>
<body>


    <!-- jQuery CDN -->
    <script src="https://code.jquery.com/jquery-1.12.0.min.js"></script>
    <!-- Bootstrap Js CDN -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

In this part, we are going to build a simple Bootstrap sidebar that vertically scrolls along with the page.

HTML

1 - Basic document structure

We'll wrap everything in .wrapper box to make advantage of CSS flex property.

In this case, we'll give .wrapper the CSS property align-items: stretch. (By doing this, the sidebar will take the height of the page content. As the content increases, sidebar height dynamically increases.)

<div class="wrapper">

    <!-- Sidebar -->
    <nav id="sidebar">
        ...
    </nav>

    <!-- Page Content -->
    <div id="content">
        <!-- We'll fill this with dummy content -->
    </div>

</div>       

2 - Sidebar and its content

Now let's fill our sidebar with some content.

Here I'll place there a sidebar navigation menu that will contain demo navigation links.

Some of them will also contain drop-down menus.

<div class="wrapper">

    <nav id="sidebar">
        <!-- Sidebar Header -->
        <div class="sidebar-header">
            <h3>Collapsible Sidebar</div>
        </div>

        <!-- Sidebar Links -->
        <ul class="list-unstyled components">
            <li class="active"><a href="#">Home</a></li>
            <li><a href="#">About</a></li>

            <li><!-- Link with dropdown items -->
                <a href="#homeSubmenu" data-toggle="collapse" aria-expanded="false">Pages</a>
                <ul class="collapse list-unstyled" id="homeSubmenu">
                    <li><a href="#">Page</a></li>
                    <li><a href="#">Page</a></li>
                    <li><a href="#">Page</a></li>
                </ul>

            <li><a href="#">Portfolio</a></li>
            <li><a href="#">Contact</a></li>
        </ul>
    </nav>

</div>

We also added .active class to the first item to mark that it is the current page.

3 - Making dropdowns work (data-toggle="collapse" & .collapse & aria-expanded)

  1. To make a dropdown menu collapsible, data-toggle="collapse" should be added to the link holding the dropdown.

  2. The link's href attribute must contain the id of the dropdown menu preceded by a hash.

In this case, I used #homeSubmenu for this approach.

  1. The dropdown menu itself also should have .collapse class too.
<a href="#homeSubmenu" data-toggle="collapse" aria-expanded="false">Pages</a>
  1. Be sure to add aria-expanded to the dropdown's control element <a>.

This attribute explicitly defines the current state of the collapsible element to screen readers and similar assistive technologies.

  • If the collapsible element is closed by default, it should have a value of aria-expanded="false".
  • If you've set the collapsible element to be open by default using an .in class, set aria-expanded="true" on the control instead.

By clicking the anchor, the dropdown will slide up or slide down according to the aria-expanded="" value.

4 - Sidebar toggle button

Now it's time to add the sidebar toggle button, this button will handle opening/closing the sidebar.

We place it outside the sidebar itself. It doesn't matter where it is located as long as it's outside the sidebar, i.e. visible all the time.

<div id="content">
    <button type="button" id="sidebarCollapse" class="btn btn-info navbar-btn">
        <i class="glyphicon glyphicon-align-left"></i>
        Toggle Sidebar
    </button>
<div>

CSS

Now, let's finally apply some styling to our sidebar.

1. The most important thing for this approach is to use the flex property for the .wrapper div.

As I mentioned above, stretch value will equalize both page content and sidebar height.

.wrapper {
    display: flex; // the most important thing for this approach
    align-items: stretch; // will equalize both page content and sidebar height
} 

2. give the sidebar a fixed width of 250px.

As a result of using flexbox properties, page content #content will take the remaining width of .wrapper. (as long as we don't use flex-wrap: wrap property)

3. we'll use sidebar's width to push the element out of the screen when we don't need it.

This behaviour will be applied when the sidebar has an .active class.

This class has a negative margin left value that equals to the sidebar width. This will be all for the moment and we will work more with this class We'll use this class later on in the JavaScript part.

.wrapper {
    display: flex;  // the most important thing for this approach
    align-items: stretch; // will equalize both page content and sidebar height
}

#sidebar {
	// sidebar fixed width 250px
    min-width: 250px;
    max-width: 250px;
}

#sidebar.active {
	/* 
    ** use sidebar's width to push the element out of the screen when we don't need it.
    ** This behaviour will be applied when the sidebar has an .active class.
    ** This class has a negative margin left value that equals to the sidebar width.
    */
    margin-left: -250px;
}

As we do not know if the content will vertically fill the entire screen, we will set the minimum height of the sidebar to 100vh.

vh = an unit that refers to the viewport height.

This means that :

  • this rule makes the initial height of the sidebar at least equal to the screen height.
  • Also, its height will increase when the page content would increase.
#sidebar {
    min-width: 250px;
    max-width: 250px;
    min-height: 100vh; // set an initial height at least equal to the screen height
}

4. Styling dropdowns

style the expandable dropdown items in the navigation based on their aria-expanded value.

We'll use ::before pseudo elements to add an arrow for these items and depending on its aria-expanded value, the arrow direction will change from up to down and vice versa.

a[data-toggle="collapse"] {
    position: relative;
}

a[aria-expanded="false"]::before, a[aria-expanded="true"]::before {
    content: '\e259';
    display: block;
    position: absolute;
    right: 20px;
    font-family: 'Glyphicons Halflings';
    font-size: 0.6em;
}

a[aria-expanded="true"]::before {
    content: '\e260';
}

5. Media query - styling for smaller screens

We need a slightly different behaviour for the sidebar on the smaller screens.

Instead of appearing by default, it'll be hidden and appear only after clicking the toggle button. Like this, we will save valuable space for your content and show the navigation to the user only when needed.

Basically, what we need to do here is to reverse the .active style:

@media (max-width: 768px) {
    #sidebar {
        margin-left: -250px;
    }
    #sidebar.active {
        margin-left: 0;
    }
}

6. Additional styling

To give it a fancy look, I have styled it a bit more.

/*
    ADDITIONAL DEMO STYLE, NOT IMPORTANT TO MAKE THINGS WORK BUT TO MAKE IT A BIT NICER :)
*/
@import "https://fonts.googleapis.com/css?family=Poppins:300,400,500,600,700";


body {
    font-family: 'Poppins', sans-serif;
    background: #fafafa;
}

p {
    font-family: 'Poppins', sans-serif;
    font-size: 1.1em;
    font-weight: 300;
    line-height: 1.7em;
    color: #999;
}

a, a:hover, a:focus {
    color: inherit;
    text-decoration: none;
    transition: all 0.3s;
}

#sidebar {
    /* don't forget to add all the previously mentioned styles here too */
    background: #7386D5;
    color: #fff;
    transition: all 0.3s;
}

#sidebar .sidebar-header {
    padding: 20px;
    background: #6d7fcc;
}

#sidebar ul.components {
    padding: 20px 0;
    border-bottom: 1px solid #47748b;
}

#sidebar ul p {
    color: #fff;
    padding: 10px;
}

#sidebar ul li a {
    padding: 10px;
    font-size: 1.1em;
    display: block;
}
#sidebar ul li a:hover {
    color: #7386D5;
    background: #fff;
}

#sidebar ul li.active > a, a[aria-expanded="true"] {
    color: #fff;
    background: #6d7fcc;
}
ul ul a {
    font-size: 0.9em !important;
    padding-left: 30px !important;
    background: #6d7fcc;
}

Javascript

The idea here is to toggle the .active class to the sidebar on clicking the toggle button.

  • By default, the sidebar will appear, i.e. it hasn't got .active class yet.
  • After clicking the toggle button, the sidebar will be given an .active class, and pushed out from the screen. The page content will take the full-screen width too.
  • Reclicking the toggle button will remove the .active class and the sidebar reappears again.
$(document).ready(function () {

    $('#sidebarCollapse').on('click', function () {
        $('#sidebar').toggleClass('active');
    });

});

The sidebar will cover the left part of the page content when it's open and the rest of the content will be covered by dark transparent overlay.

HTML

We'll use the same markup above and (1)add a #dismiss button to the sidebar. This button will be responsible for the closing of the sidebar when it's open.

And since we'll apply a transparent overlay, let's (2)insert an .overlay div at the very bottom of our page for that purpose too.

<div class="wrapper">

        <nav id="sidebar">
            <!-- Close Sidebar Button -->
            <div id="dismiss">
                <i class="glyphicon glyphicon-arrow-left"></i>
            </div>

            <!-- Sidebar Header -->
            <div class="sidebar-header">
                <h3>Collapsible Sidebar</div>
            </div>

            <!-- Sidebar Links -->
            <ul class="list-unstyled components">
                <li class="active"><a href="#">Home</a></li>
                <li><a href="#">About</a></li>
                <li><!-- Link with dropdown items -->
                    <a href="#homeSubmenu" data-toggle="collapse" aria-expanded="false">Pages</a>
                    <ul class="collapse list-unstyled" id="homeSubmenu">
                        <li><a href="#">Page</a></li>
                        <li><a href="#">Page</a></li>
                        <li><a href="#">Page</a></li>
                    </ul>
                <li><a href="#">Portfolio</a></li>
                <li><a href="#">Contact</a></li>
            </ul>
        </nav>

        <div id="content">
            <button type="button" id="sidebarCollapse" class="btn btn-info navbar-btn">
                <i class="glyphicon glyphicon-align-left"></i>
                Toggle Sidebar
            </button>
        <div>

        <div class="overlay"><div>
</div>

CSS

In this approach, we'll get rid of the flex property and we won't even need .wrapper anymore. We'll use position property instead.

The sidebar will have a fixed positioning at the left part of the screen and content will take the full screen all the time.

By default, the sidebar will be hidden. When the toggle button is clicked, both .overlay and the sidebar will appear above the content.

Let's imagine it as layers :

  • the page content will be the back layer.
  • .overlay will be the middle layer with a transparent black colour.
    • The overlay will cover the content of the page to allow user's eye easily focus on the sidebar itself.
  • Finally, the sidebar will be the front layer.

To achieve this layering behaviour, we will simply use z-index property.

As mentioned before, we will be also adding a #dismiss button to the sidebar. It will be positioned absolutely at its top right part.

.wrapper {
    display: block;
}

#sidebar {
    min-width: 250px;
    max-width: 250px;
    height: 100vh;
    position: fixed;
    top: 0;
    left: 0;
    /* top layer */
    z-index: 9999;
}

.overlay {
    /* full screen */
    width: 100vw;
    height: 100vh;
    /* transparent black */
    background: rgba(0, 0, 0, 0.8);
    position: fixed;
    top: 0;
    left: 0;
    display: none;
    /* middle layer, i.e. appears below the sidebar */
    z-index: 9998;
}

#dismiss {
    width: 35px;
    height: 35px;
    position: absolute;
    /* top right corner of the sidebar */
    top: 10px;
    right: 10px;
}

Javascript

In two previous sidebars, the toggle button functionality was to open/close the sidebar. Here, we already have a close button inserted in our sidebar, so the toggle button function is only to open the sidebar.

To clarify the mechanism, by clicking the toggle button both overlay and sidebar appear, and by clicking the sidebar close button, both overlay and sidebar disappear.

$(document).ready(function () {

    $("#sidebar").mCustomScrollbar({
        theme: "minimal"
    });

    // when opening the sidebar
    $('#sidebarCollapse').on('click', function () {
        // open sidebar
        $('#sidebar').addClass('active');
        // fade in the overlay
        $('.overlay').fadeIn();
        $('.collapse.in').toggleClass('in');
        $('a[aria-expanded=true]').attr('aria-expanded', 'false');
    });

   
    // if dismiss or overlay was clicked
    $('#dismiss, .overlay').on('click', function () {
      // hide the sidebar
      $('#sidebar').removeClass('active');
      // fade out the overlay
      $('.overlay').fadeOut();
    });
});

In this example, we will, instead of building a sidebar that collapses entirely, build a Partially collapsing sidebar. The sidebar will convert into a compressed version after toggle button click.

Let's use the first approach's markup as a starting point.

HTML

To further enhance it, we will insert an icon into the navigation links.

We will use Glyphicons that are a part of the Bootstrap distribution.

<div class="wrapper">

        <nav id="sidebar">
            <!-- Sidebar Header -->
            <div class="sidebar-header">
                <h3>Collapsible Sidebar</div>
                <strong>BS</strong>
            </div>

            <!-- Sidebar Links -->
            <ul class="list-unstyled components">
                <li class="active">
                    <a href="#homeSubmenu" data-toggle="collapse" aria-expanded="false">
                        <i class="glyphicon glyphicon-home"></i>
                        Home
                    </a>
                </li>
                <li>
                    <a href="#">
                        <i class="glyphicon glyphicon-briefcase"></i>
                        About
                    </a>
                    <a href="#pageSubmenu" data-toggle="collapse" aria-expanded="false">
                        <i class="glyphicon glyphicon-duplicate"></i>
                        Pages
                    </a>
                    <ul class="collapse list-unstyled" id="pageSubmenu">
                        <li><a href="#">Page 1</a></li>
                        <li><a href="#">Page 2</a></li>
                        <li><a href="#">Page 3</a></li>
                    </ul>
                </li>
                <li>
                    <a href="#">
                        <i class="glyphicon glyphicon-link"></i>
                        Portfolio
                    </a>
                </li>
                <li>
                    <a href="#">
                        <i class="glyphicon glyphicon-send"></i>
                        Contact
                    </a>
                </li>
            </ul>
        </nav>

        <div id="content">
            <button type="button" id="sidebarCollapse" class="btn btn-info navbar-btn">
                <i class="glyphicon glyphicon-align-left"></i>
                Toggle Sidebar
            </button>
        <div>

</div>

CSS

Instead of pushing the sidebar entirely out of the screen, we'll just shrink its width, and restyle its content to fit this new width. The styles of the compressed version will be added to the class .active.

For example, we will :

  • downsize the font size of the anchors' text
  • align it to centre
  • make it render below the icon
  • move the arrow to the very bottom of every anchor
  • adjust the padding around the dropdown links.

The code will be as follows:

/* Shrinking the sidebar from 250px to 80px and center aligining its content*/
#sidebar.active {
    min-width: 80px;
    max-width: 80px;
    text-align: center;
}

/* Toggling the sidebar header content, hide the big heading [h3] and showing the small heading [strong] and vice versa*/
#sidebar .sidebar-header strong {
    display: none;
}
#sidebar.active .sidebar-header h3 {
    display: none;
}
#sidebar.active .sidebar-header strong {
    display: block;
}

#sidebar ul li a {
    text-align: left;
}

#sidebar.active ul li a {
    padding: 20px 10px;
    text-align: center;
    font-size: 0.85em;
}

#sidebar.active ul li a i {
    margin-right:  0;
    display: block;
    font-size: 1.8em;
    margin-bottom: 5px;
}

/* Same dropdown links padding*/
#sidebar.active ul ul a {
    padding: 10px !important;
}

/* Changing the arrow position to bottom center position, 
   translateX(50%) works with right: 50% 
   to accurately  center the arrow */
#sidebar.active a[aria-expanded="false"]::before, #sidebar.active a[aria-expanded="true"]::before {
    top: auto;
    bottom: 5px;
    right: 50%;
    transform: translateX(50%);
}

Media queries

On smaller screens, we'll keep the compressed version as a default active state of the sidebar. I.e., the uncompressed version will not be used on mobiles at all and the compressed version will become visible after clicking the toggle button.

To achieve this, we can only copy the styles from .active to our mobile media query @media (max-width: 768px) and add a margin-left functionality to it.

For mobiles, #sidebar.active sidebar will have a negative left margin (it will be off the canvas) and the #sidebar without the .active class will have margin-left set to 0.

@media (max-width: 768px) {
    /* 80px and its content aligned to centre. Pushing it off the screen with the
       negative left margin
    */
    #sidebar.active {
        min-width: 80px;
        max-width: 80px;
        text-align: center;
        margin-left: -80px !important;
    }


    /* Reappearing the sidebar on toggle button click */
    #sidebar {
        margin-left: 0; 
    }


    /* Toggling the sidebar header content, 
       hide the big heading [h3] and showing the small heading [strong] and vice versa
    */
    #sidebar .sidebar-header strong {
        display: none;
    }
    #sidebar.active .sidebar-header h3 {
        display: none;
    }
    #sidebar.active .sidebar-header strong {
        display: block;
    }

    /* Downsize the navigation links font size */
    #sidebar.active ul li a {
        padding: 20px 10px;
        font-size: 0.85em;
    }

    #sidebar.active ul li a i {
        margin-right:  0;
        display: block;
        font-size: 1.8em;
        margin-bottom: 5px;
    }

    /* Adjust the dropdown links padding*/
    #sidebar.active ul ul a {
        padding: 10px !important;
    }

    /* Changing the arrow position to bottom center position, 
      translateX(50%) works with right: 50% 
      to accurately  center the arrow */
    #sidebar.active a[aria-expanded="false"]::before, #sidebar.active a[aria-expanded="true"]::before {
        top: auto;
        bottom: 5px;
        right: 50%;
        transform: translateX(50%);
    }
}

Javascript

We will not include any additional lines of JavaScript and we'll just use the same function did use in the first sidebar.

$(document).ready(function () {

    $('#sidebarCollapse').on('click', function () {
        $('#sidebar').toggleClass('active');
    });

});

That will be all for now. We should have a nicely working partially collapsing sidebar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment