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.
Media Queries
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.
This is where I encountered the most challenges. However, I must admit that most of the difficulties were self-inflicted. In fact, I chose to achieve this without using JavaScript. Only CSS.
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-toggle
opacity 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 (usingdisplay: none
doesn’t work in this case). - The
#mobile-navbar
rule simply defines the base state for the menu. Note theleft: -100%
andposition: absolute
properties. 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 +
and ~
and ::
mean?” I understand this feeling because I had the same question.
:checked
is a special selector for targeting a checkbox only when it is checked. That’s the selector that makes all this possible.::before
is 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 + p
selects only the firstp
following anh1
element.~
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 ~ p
selects everyp
element that followsh1
even if there are other elements in between (e.g.,<h1><h2><p>...
).
With this information, we can understand the three rules:
#nav-toggle + #nav-toggle-label::before
selects the::before
pseudo-element of#nav-toggle-label
. This selection is contingent upon the condition that#nav-toggle-label
directly follows#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::before
selects 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-navbar
ultimately selects the navigation menu in the scenario where the checkbox is checked (menu open). As you can see, we setleft: 0
, so that the menu can smoothly slide in.
This not-so-simple setup worked like a charm.1
Conclusions
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! ↩︎