Welcome to the second article about how I completely rewrote this blog’s theme from scratch. In the previous article, I only scratched the surface by describing how I used CSS Grids to effortlessly build the basic layout of the website.
However, at the time, I was still quite apprehensive about undertaking this “ambitious” (for my skills) project. In fact, I had yet to face what I believed would be my most challenging enemy: responsive design.
For some reason, I always regarded implementing a proper responsive layout as something out of my league. It always scared me. Remember: I grew up with table-based layouts, probably the less-flexible layout ever known to humankind (well… at least for what concerns web design). The idea of a sidebar vanishing and morphing into a pop-in menu based on the user’s device size seemed like magic to me.
However, once I had a proper desktop layout, I had to confront my most challenging obstacle. Facing the feature, which could potentially jeopardize my project, became imperative. And I needed to do it soon, before investing too much time in the project.
And so I did.
The reality? It was much simpler than expected. Modern CSS can solve these problems quickly without the bloat of including external CSS frameworks (at least, in the case of my simple requirements). But let’s start from the basis.
My plan included two steps:
- Changing the desktop layout from the “two sidebars” design to a simple single-column layout for mobile.
- Incorporating a hamburger menu to toggle the website navigation.
The first step was to hide the two sidebars on mobile (shifting the layout into a single column) while making a top header appear (including the website logo and the customary hamburger menu button). I was well aware of media queries, so I had a pretty clear idea of how to proceed.
Initially, I had to input the HTML for both elements: the sidebar and the mobile header.
Then, on the CSS side, I only had to work with media queries to ensure that only one of them appeared on the appropriate device.
It is a simple but effective solution.
Likewise, I employed media queries to modify various other website elements, including the footer and other aspects of the post.
Creating a Hamburger Menu in Pure-CSS
Once I had established the basics, it was time to delve into the realm of interactivity. That was the part that would have decided the fate of the new theme. I needed to be able to click on the menu to make the navigation appear. It was a mandatory feature.
Luckily, after just a couple of hours of me smashing my head on the keyboard, I came out with a practical solution.
Writing the HTML
The secret behind pure-CSS menu buttons is the checkbox input element. In fact, this input naturally possesses two states that CSS can target: checked and unchecked. This quality makes it a perfect candidate for any UI system with two states. In the case of a navigation menu, this means menu open and menu closed.
The toggle works by combining three elements:
- The checkbox input element, which provides the interaction behavior.
- The label element, responsible for the visual element (the menu icon) of the menu toggle (we do not want to see the default checkbox).
- The mobile-navbar element, which houses the actual menu and will pop in and out depending on the state of the checkbox.
The Toggle’s CSS
This is a snippet of the relevant CSS.
Here is a summary of the important rules:
- We set
#nav-toggleopacity to 0. As I mentioned, I don’t want the default checkbox to be visible. However, I still want the user to be able to interact with it (using
display: nonedoesn’t work in this case).
#mobile-navbarrule simply defines the base state for the menu. Note the
position: absoluteproperties. In practice, we want the menu to be rendered out of the viewport so that we can slide it in from the left when we click on the hamburger menu icon (the animation is provided by the transition: 0.3s property).
Now, here comes the cool part. The actual behavior is dictated by the last three rules, which are quite intricate. You might wonder, “What do all these symbols like
:: mean?” I understand this feeling because I had the same question.
:checkedis a special selector for targeting a checkbox only when it is checked. That’s the selector that makes all this possible.
::beforeis a pseudo-element. This is not the place to go into the intricacies of pseudo-elements, yet you can just think about this as a “block” rendered before the actual element.
+is the adjacent sibling combinator. It is used to select the element that immediately follows the one specified before. For example,
h1 + pselects only the first
~is the general sibling combinator. It is used to select all the elements that come after the one specified earlier (and not necessarily immediately after). For instance,
h1 ~ pselects every
pelement that follows
h1even if there are other elements in between (e.g.,
With this information, we can understand the three rules:
#nav-toggle + #nav-toggle-label::beforeselects the
#nav-toggle-label. This selection is contingent upon the condition that
#nav-toggle(which is true in this case). Here, the rule defines the default state of an unchecked checkbox, and it specifies the default icon as an actual hamburger emoji 🍔.
#nav-toggle:checked + #nav-toggle-label::beforeselects the same element as the previous rule, but exclusively when the checkbox is checked. We use this rule to define the icon in the “open menu” state. Displaying great creativity, I substituted the hamburger with French fries 🍟. 🤷♂️
#nav-toggle:checked ~ #mobile-navbarultimately selects the navigation menu in the scenario where the checkbox is checked (menu open). As you can see, we set
left: 0, so that the menu can smoothly slide in.
This not-so-simple setup worked like a charm.1
With this out of the way, I found myself confident in my ability to complete this project.
Next stop: theming. Fueled by this surge of confidence, I felt prepared to address another aspiration: implementing a dark/light theme switcher.
As usual, you can find the theme code on GitHub.
See you next time!
I am pretty sure these rule can be simplified further. But I was so happy to have something working that I decided to not mess with them anymore! ↩︎