Thoughts .toString()

HTML: Hamburger menu without JS


Not too long ago, if we wanted to make navigation menus, it took Javascript to animate the icons, and open and close menus on click. Today, CSS can achieve all of this as long as some attention is paid to its structure to make them accessible to the selectors. For this example, we’ll create a “plus” icon that rotates into an “X” when clicked. On top of that, it opens a menu. We might also want the menu to be fully visible when the screen width exceeds a threshold e.g. for desktop monitors.

We’ll start with a typical HTML layout of a menu icon on top of main content section with some navigation links on the right.

html
1<details>
2    <summary>
3        <div class="vertical"></div>
4        <div class="horizontal"></div>
5    </summary>
6</details>
7<main>
8    <article>
9        <p>
10            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vitae ultricies leo integer malesuada nunc vel risus commodo viverra. Adipiscing enim eu turpis egestas pretium. Euismod elementum nisi quis eleifend quam adipiscing. 
11        </p>
12    </article>
13    <nav>
14        <ul>
15            <li>Menu Item 1</li>
16            <li>Menu Item 2</li>
17        </ul>
18    </nav>
19</main>

The <details> will be leveraged for the menu icon, since it has the built-in capability to add an open attribute when clicked without Javascript. Of course, it takes some styling to get it to how we might want it.

css
1details {
Place the icon in the top right corner.
2    position: absolute;
3    top: 10px;
4	right: 10px;
Parametrize some sizes, so they can be adapted for Astro.
5    --icon-size: 30px;
6    --icon-width: 8px;
7    --icon-start: calc(0.5 * (var(--icon-size) - var(--icon-width)));
8    --icon-color: black;
9    width: var(--icon-size);
10	height: var(--icon-size);
Summary is shown whether open or closed. It defaults with an arrow list style, which we need to remove.
11    & summary {
12        list-style: none;
13
Change cursor and icon appearance when mouse is hovered to make it feel more like a button.
14        &:hover {
15            cursor: pointer;
16            opacity: 0.6;
17        }
18
19        & div {
These divs represent the two bars of the "Plus" icon.
20            transition-duration: 0.5s;
21            position: absolute;
22            background-color: var(--icon-color);
23            &.vertical {
24                width: var(--icon-width);
25                height: var(--icon-size);
26                left: var(--icon-start);
27            }
28            &.horizontal {
29                width: var(--icon-size);
30                height: var(--icon-width);
31                top: var(--icon-start);
32            }
33        }
34    }
The "open" attribute on <details> is added when clicked and selected with css.
35    &[open] summary {
Rotate the "Plus" icon to create an "X" icon when clicked.
36        transform: rotateZ(45deg) scale(1.2);
37    }
Let's give some default styling to the main content and nav links.
38    & + main {
Make sure the text starts below the menu icon.
39        margin-top: 50px;
On larger screens, nav links should appear on the right quarter of the screen. Put a little spacing around each component.
40        @media (min-width: 600px) {
41            display: flex;
42            gap: 1rem;
43            margin-left: 1rem;
44            margin-right: 1rem;
45        }
46        & article {
47            @media (min-width: 600px) {
48                flex-basis: 75%;
49            }
50        }
51        & nav {
52            @media (min-width: 600px) {
53                flex-basis: 25%;
54                text-align: center;
55            }
56            & ul {
Lists come with 40px left padding by default. We want the menu to be centered instead.
57                padding: 0;
58                list-style: none;
59            }
60        }
61    }
62}

The remaining styling is what’s important. We already have a flex container that splits content and navigation to a 75% to 25% basis for larger screens, when the screen width is greater than 600px. We’ve set the appearance of the icon when it’s open and closed. What’s left is to make the icon invisible on large screens, and open the menu on top when the icon is clicked for smaller screens.

css
Hide icon on larger screens since navigation is already on the right side.
1details {
2    @media (min-width: 600px) {
3        display: none;
4    }
Hide navigation on smaller screens when the menu is closed.
5    & + main nav {
6        @media (max-width: 599px) {
7            display: none;
8        }
9    }
Show a floating navigation menu when it is open on smaller screens.
10    &[open] + main nav {
11       @media (max-width: 599px) {
Stretch menu across screen, but underneath the icon.
12            position: absolute;
13            top: 0;
14            width: 95%;
15            margin-top: 50px;
16            padding-top: 4rem;
17            padding-bottom: 4rem;
Center menu items.
18            display: flex;
19            flex-direction: column;
20            justify-content: flex-start;
21            align-items: center;
Give it some color to hide main text behind it.
22            background-color: lightslategray;
23            z-index: 100;
24        } 
25    }
26}

Results

This is what it looks like on larger screens without the menu icon.

This is what it looks like on smaller screens.