Building a Sassy Navigation Drawer from Scratch.

C.S Weekly 4 — ::before, ::after, :checked

Gregory ‘Grey’ Barkans
8 min readAug 10, 2018

This is my 4th C.S Weekly project. For the inspiration behind the effort, read Code Something Weekly: How and Why.

Last week’s project explored managing a PostgreSQL Database from a bash CLI:

I come from a traditional territory. In school, I worked with everything from x86 assembly to hardware description language with FPGAs.

However I gradually discovered that my soul thrives on full-stack web development.

But I’m not going to lie. I tend to always leave web design and CSS on the back burner. Perhaps that doesn’t come as a surprise given my background. Programmers coming from traditional means often treat topics like design and CSS in one of two ways:

  • CSS is one of those things you think you know, it’s “easy”
  • It’s someone else’s job

But eventually, we must face that this line of reasoning is a coping mechanism.

When faced with various languages and an overabundance of information, one tends to prioritize, box and categorize. If you lack a background with photoshop, illustrator and general web design, chances are you don’t move too far past your bubble as a ‘developer’.

But we need to stop making these excuses, especially if we want to consider ourselves proficient across the stack. Face it, there’s a wealth of technology out there and you could stand to improve across the board. And knowing how things work never disadvantages you while debugging.

Aside: I was on the phone with a college software admin earlier today and they were telling a story of how a traditional engineering prof refused to teach perl because ‘C is the engineer’s language’. I’m willing to bet that prof also thinks they know CSS.

This week I decided to get dirty and dare I say, sassy. In an attempt to better understand topics that I normally take for granted from vendors, I built a Navigation Drawer and Hamburger icon from scratch. No materialize, bootstrap or yadayada.min.css. No icon fonts. Nothing. Pure Sassy CSS.

Check out that Hamburger in action

The major topics I learned and will discuss in this article:

  • Checkbox Inputs and Labels
  • The Basics of SCSS
  • WTF ::before and ::after really are and how to use them
  • Sibling Selectors

Building A Hamburger Icon in Pure CSS

The menu (aka ‘hamburger’) icon uses a few tricks:

  • a checkbox input with an associated label
  • ::before and ::after pseudo classes

I’ve set up a codepen for the menu icon so you can refer to it and tinker away as you wish. It’s likely best to refer to after reading this entire section, but I digress.

Vegan Hamburgers For All

Inputs and Labels

An input of type checkbox <input type="checkbox"> has exactly two states

  • :checked
  • :not(:checked)

which makes it a prime candidate for toggling an entity. Furthermore, clicking on the associated <label> toggles the checked value. To be convinced of this, check out the MDN page.

Let’s start off with an input and label:

It’s worth noting here that <label>s are just inline elements. There is nothing special about them other than the for attribute which binds them to an <input>with a matching id.

Let’s add another inline element that we’ll use to form the hamburger: <span>. The final markup and all that’s needed to make a hamburger is:

SCSS Scaffold

If you’re new to CSS pre-processors (like I was), it can seem like yet another learning curve. But hear it from me, if not everyone else: it truly makes your life easier. I’ll keep it simple and am only going to take advantage of two things:

  • variables
  • nesting

In case you’re wondering what’s going on with the __ class notation, it’s BEM. We’ll see shortly how nicely this plays with SCSS nesting.

The & directive simply copies the selector of the current level.

For example, if we’re currently inside .bob, then &__turner selects .bob__turner

Given the above, we see that everything is prefixed with .navigation. This yields the following scaffold:

.navigation {
&__cbox {
}

&__hamburger-box {
}

&__hamburger {
}
}

which translates to:

.navigation { }
.navigation__cbox { }
.navigation__hamburger-box {}
.navigation__hamburger {}

In other words, the SCSS style gives us better encapsulation and reasoning about our structure.

Ok, now that we’re all setup, let’s get a few of the basics out of the way:

  • Hide the input
  • Give the hamburger box some positioning (we’ll do top left corner) and style. Make sure to give it a height and width.

Styling the Hamburger with ::before and ::after

The ::before and ::after pseudo classes might seem mysterious at first, but they’re really not too bad once you start to play with them.

As the names imply, ::before adds content before the selected element and ::after after the selected element.

This is best demonstrated with a simple example. Let’s change the standard bullet points to emojis using ::before and for the hell of it, add some sparkle ::after

Notice the role of the content property which defines the content of the element that gets inserted before/after the selected element.

In the case of our hamburger, we don’t really want to insert text content but rather we want to style the pseudo elements as lines. However, without defining content, the elements don’t render (as they are empty). To get around this, we use an empty string: content: “”;.

You can read more about the content property on the MDN page.

The first line that we position is actually the middle line (or otherwise considered the (vegan) meat of the burger).

Using the hamburger box as a reference, we can define a margin on the hamburger in order to position it vertically. We don’t want to use absolute positioning as will become clear shortly.

NOTE: Be careful what values you use here, they have to match up with the hamburger box size.

margin-top: 2rem; /* 50% of 4rem */

Additionally, we’re going to set its position to relative so that we can adjust the position of the pseudo elements relative to this middle line:

Next, let’s set the style that will be used by all three of the lines:

Now, positioning the ::before and ::after lines is just a matter of absolute positioning. But be careful, as I stated prior we need to make sure we set the content property, even though it’s an empty string. Failure to do so will cause the top and bottom lines to not rendered them as they’re considered empty and the properties we’re defining are just the background box.

Note: Notice that the top are 2rem each. That’s because we set the hamburger box height to 4rem and the middle line at 2/4 . Be careful of these sizes — using variables and calc can help manage everything as we’ll see in the next section.

Using Variables

Alright, this design works — but we can do much better.

Since the height and positioning of the pseudo-elements, hamburger box and the hamburger are all related, let’s define a single variable that can be used as a basis for calculation.

$hamburger-height: 4rem;

Now, we can use this variable as well as calc to remove all of the hard-coded values.

Note: When using calc with a variable, use #{} around it

  • The hamburger box is a square — set width and height to the hamburger height variable
  • The middle hamburger line has a margin-top of half of the height
  • All three lines have the same width as the hamburger height
  • The before line is positioned half the height up from the middle line
  • The after line is positioned half the height down from the middle line

Here’s the final code:

Nicely done!

Again, there’s a codepen for reference to tinker with.

Opening And Closing The Drawer

Alright, we’ve got a hamburger menu. Now how do we react to it?

I won’t go into styling the drawer itself (it’s just a nav positioned absolutely with a high z-index). But let’s review how to setup a slide-in transition that reacts to the menu being pressed.

Utilizing :checked and Sibling Selectors

To recap we have a hidden <input> that changes values when you click on the <label>, which appears on the screen as a hamburger. The class for the <input> is .navigation__cbox.

The state of the <input> is determined by the following selectors:

  • :checked
  • :not(:checked)

Therefore if we combine the :checked selector with a selector for an element that we want to react, we can toggle its behaviour!

But how might we combine those selectors?

As it stands, the <input> has no children and is on its own. The navigation drawer would be defined further down the markup. Therefore, we have to leverage the sibling selectors + and ~ which as stated on MDN do the following:

The adjacent sibling combinator (+) separates two selectors and matches the second element only if it immediately follows the first element, and both are children of the same parent element.

The general sibling combinator (~) separates two selectors and matches the second element only if it follows the first element (though not necessarily immediately), and both are children of the same parent element.

In both cases, it’s important that everything is a child of the same parent.

That’s why we put everything within the .navigation div:

Selecting Drawer States

Putting everything together, let’s initially define the navigation drawer as hidden. I opted to set its opacity, position and width, although this can be done in other ways.

Then, change those property when selecting with :checked and the general sibling (~) selector.

For a slide effect, simply add a transition property in __nav. Furthermore, to make it look on top of everything, I’d suggest adding a box-shadow.

&__nav {
...
transition: all .5s ease-in-out;
box-shadow: 1rem 0 1rem rgba($color-black, .15);
}

Wrap Up

This article explored how to make a navigation drawer with a menu button in pure css. The hamburger was composed of a hidden input of type checkbox alongside a label utilizing pseudo classes. The sibling selector along with the checked state of the input was used to toggle behaviour of the drawer.

There’s still a lot in the original design not covered here.

You can check out a full live demo here:

https://vapurrmaid.github.io/sassy-nav-drawer/

And, as always, the source code is available on Github:

Happy coding :)

--

--

Gregory ‘Grey’ Barkans

I’m a software engineer between Hamont ← → ATX that’s mainly interested in technology and philosophy. I used to spin DJ mixes as well. vapurrmaid.ca