9. Menu

In the next lesson, or example code, we'll build a complete Responsive Design menu that adapts to both desktop and smartphone. When it's on a reduced resolution you'll see a hamburger button that opens a full-screen menu in the browser. On desktop it widens to take up all the available space, placing the logo on the left and the navigation on the right, running horizontally.

We'll start with a solid HTML structure, following a good order and using content tags like <header> or <nav> and an unordered list to structure the menu, building the foundation we'll work on with CSS later.

Additionally, and always optionally, we've added a minimalist JavaScript framework called AlpineJS to handle the events. This is because we have 2 clicks on smartphone that must add or remove the nav--show class. A modifier in charge of showing or hiding the menu. You can use plain JavaScript if you have some experience.

We'll need to add some important metas in the <head>.

<!-- Disable viewport zoom -->
<meta
    name="viewport"
    content="width=device-width, initial-scale=1.0, shrink-to-fit=no"
>
<!-- CSS normalizer -->
<link
    rel="stylesheet"
    type="text/css"
    href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css"
>
<!-- AlpineJS -->
<script src="https://unpkg.com/alpinejs" defer></script>

Our HTML would look like this:

<header
    class="header"
    x-data="{
           show: false,
           isUpScroll: true,
           lastPosScroll: 0
    }"
    @scroll.window="window.pageYOffset > lastPosScroll ? (isUpScroll = false, lastPosScroll =  window.pageYOffset) : (isUpScroll = true, lastPosScroll =  window.pageYOffset)"
            :class="isUpScroll ? '' : 'header--hide'"
>
    <div class="container">
        <a href="/" class="link" role="heading" aria-level="1">
            Your logo
        </a>
        <button class="is-in-mobile button header__open" @click="show = true">Open</button>
        <nav class="nav" :class="show ? 'nav--show' : ''">
            <button class="is-in-mobile button header__close" @click="show = false">Close</button>
            <ul class="ul-reset nav__ul">
                <li class="nav__item"><a class="link nav__link" href="#">Home</a></li>
                <li class="nav__item"><a class="link nav__link" href="#">About me</a></li>
                <li class="nav__item"><a class="link nav__link" href="#">Portfolio</a></li>
                <li class="nav__item"><a class="link nav__link" href="#">Blog</a></li>
                <li class="nav__item"><a class="link nav__link" href="#">Contact</a></li>
            </ul>
        </nav>
    </div>
</header>

The x-data, @scroll.window and :class attributes belong to AlpineJS. They're used to manage when the nav--show classes (show/hide the navigation on smartphone) and header--hide (show/hide the <header> as you scroll) should appear and disappear.

Notice how the logo's hyperlink has been turned into an <h1> to keep it consistent with the site's headings.

Now let's implement the CSS styles:

:root {
    /* Colors */
    --color-black: #000;
    --color-white: #fff;
    --color-primary: orange;
    --color-secondary: purple;

    /* Gaps */
    --gap-s: 0.9rem;
    --gap-m: 1rem;
    --gap-l: 1.5rem;
    --gap-xl: 2rem;
    --gap-xxl: 3rem;
}

/* Components */
.ul-reset {
  display: flex;
  flex-wrap: wrap;
  list-style: none;
  padding: 0;
}

.link {
  display: inline-block;
  color: inherit;
  text-decoration: none;
}

.container {
    margin-inline: auto;
    max-width: 1000px;
    padding: 1rem;
}

.is-in-mobile {
    display: none;
    @media (width < 600px) {
        display: inherit;
    }
}

/* Header */

.header {
    position: fixed;
    inset: 0 0 auto;
    background-color: var(--color-primary);
    color: var(--color-white);
    transition: .5s;

    &.header--hide {
        transform: translateY(-12rem);
    }

    & > .container {
        display: flex;
        justify-content: space-between;
        align-items: center;
    }

    .nav__link {
        padding-inline: var(--gap-m);
    }

    @media (width < 600px) {
        .nav {
            display: grid;
            grid-template-columns: 1fr;
            grid-template-rows: var(--gap-xl) 1fr;
            position: fixed;
            inset: 0;
            transform: translateX(-100vw);
            transition: .5s;
            background-color: var(--color-secondary);
        }

        .nav--show {
            transform: translateX(0);
        }

        .nav__ul {
            flex-direction: column;
            justify-content: center;
            align-items: center;
        }

        .nav__link {
            padding: var(--gap-l);
        }
    }
}

.main {
    margin-top: 6rem;
}

And with that, we now have a navigation menu that adapts to any device.

If you want more information about other types of navigation menus, you can check out the following tutorials:

This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.

Desafíos de programación atemporales y multiparadigmáticos

Desafíos de programación atemporales y multiparadigmáticos

Te encuentras ante un librillo de actividades, divididas en 2 niveles de dificultad. Te enfrentarás a los casos más comunes que te puedes encontrar en pruebas técnicas o aprender conceptos elementales de programación.

Buy the book

Will you buy me a coffee?

This is how I keep writing without ads or paywalls.

Comments

There are no comments yet.