What is the best way to create an animated X icon for the menu on a responsive navigation bar?

As a beginner developing my first web app, I am currently focused on creating a top navigation bar that is fully responsive. Specifically, I want the menu icon to appear only when the screen width is less than 600px. To enhance user experience, I would like the menu icon to transform into an X icon once clicked. However, I am unsure about how to achieve this functionality.

Below is the code snippet for my navigation bar:


    <!DOCTYPE html>
    <html>
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
        <style>
            (...)
        </style>
    </head>
    <body>
        (...)
    </body>
    </html>

Thank you for any assistance!

Answer №1

Success! This solution worked like a charm.

Explanation

CSS offers an array of animation capabilities to achieve the desired effect. Here's how it works:

  • The top and bottom lines need to rotate in order to form the X shape
  • The middle line should disappear from view

The X shape will be taller and more narrow compared to the original hamburger lines, hence:

  • The top and middle lines must move vertically outwards and to the right to retain their center position

Implementation

/* Define the appearance and color of the hamburger lines */
.navbar-toggler span {
    display: block;
    background-color: #4f4f4f;
    height: 3px;
    width: 25px;
    margin-top: 5px;
    margin-bottom: 5px;
    position: relative;
    left: 0;
    opacity: 1;
    transition: all 0.35s ease-out;
    transform-origin: center left;
}

/* Add slight padding to the top line */
.navbar-toggler span:nth-child(1) {
    margin-top: 0.3em;
}

/**
 * Animate collapse into X.
 */

/* Rotate the top line by 45 degrees clockwise and move it upwards and inward to create the top part of the X */
.navbar-toggler:not(.collapsed) span:nth-child(1) {
    transform: translate(15%, -33%) rotate(45deg);
}
/* Make the middle line transparent */
.navbar-toggler:not(.collapsed) span:nth-child(2) {
    opacity: 0;
}
/* Rotate the bottom line by 45 degrees counter-clockwise, move it inward and downwards to complete the X shape */
.navbar-toggler:not(.collapsed) span:nth-child(3) {
    transform: translate(15%, 33%) rotate(-45deg) ;
}


/**
 * Animate collapse open into hamburger menu
 */

/* Reset the top line to its initial position and rotation (0 degrees) */
.navbar-toggler span:nth-child(1) {
    transform: translate(0%, 0%) rotate(0deg) ;
}
/* Restore the middle line's color and opacity */
.navbar-toggler span:nth-child(2) {
    opacity: 1;
}
/* Reset the bottom line to its initial position and rotation (0 degrees) */
.navbar-toggler span:nth-child(3) {
    transform: translate(0%, 0%) rotate(0deg) ;
}
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"/>

<!-- Bootstrap Navigation -->
<nav class="navbar bg-light">
        <a class="navbar-toggler collapsed border-0" type="button" data-toggle="collapse" data-target="#collapsingNavbar">
            <span> </span>
            <span> </span>
            <span> </span>
        </a>
        <a class="navbar-brand" href="./">
            Brand
        </a>
        <div class="collapse navbar-collapse" id="collapsingNavbar">
            <ul class="nav navbar-nav">
                <li class="nav-item">
                    <a class="nav-link" href="#">About</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Contact</a>
                </li>
            </ul>
        </div>
</nav>
<main class="container">
    <h1>Content Here</h1>
    <p>Shrink the viewport if to expose the hamburger menu.</p>
</main>

Key Factors

Essentially, as the top and bottom lines rotate by 45 degrees to form the X, their centers stretch across 70% of the width, necessitating a 15% inward movement. The math behind this can be computed using the Pythagorean theorem.

In our case, the hamburger menu measures 26x21 px, with a width that is 24% greater than its height. However, once the lines are adjusted and considering their height (set at 3px), the resulting X becomes a square of 20x20 px.

This particular implementation specifies the rotation point of each line as the center-left, influencing how much they shift vertically. Given that the lines add around 1.05px to the X's height per line, or approximately 33% of the X's total height, they need to move up vertically by that amount to meet at the X's center and form a 20x20px square.

Customization

Since the X always forms a square, determining the required movement simply involves knowing your <span> bars' dimensions and the resulting height of the hamburger icon.

Insert these values into the following formula:

Alternatively, in code:

const line_width = 26; // px
const line_height = 3; // px
const hamburger_height = 21; // px

const x_width = x_height = 0.8 * line_width;
const line_move_y_percent = 100 * (line_width - x_width) / (2 * line_height)
const line_move_right_percent = 100 * (x_height - hamburger_height) / (2 * line_height)

Answer №2

Guide to Creating a Simple Animated Menu Button

HTML Code:

<button class="menu-btn" id="menu-icon">
  <div class="btn-line"></div>
</button>

CSS Styling:

.menu-btn {
  width: 40px;
  height: 40px;
  background: none;
  border: 0;
}

.menu-btn,
.btn-line {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.btn-line,
.btn-line::before,
.btn-line::after {
  width: 35px;
  height: 5px;
  border-radius: 4px;
  background: white;
  transition: all .5s;
}

.btn-line {
  position: relative;
}

.btn-line::before,
.btn-line::after {
  position: absolute;
  content: '';
  top: -11px;
}

.btn-line::after {
  top: 11px;
}

.close > .btn-line {
  transform: rotate(225deg);
  background: red;
}

.close > .btn-line::before,
.close > .btn-line::after {
  top: 0;
  transform: rotate(90deg);
  background: red;
}

JS Functionality:

const btn = document.getElementById('menu-icon');
btn.addEventListener('click', (e) => {
  e.target.classList.toggle('close');
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using AJAX to dynamically populate PHP form inputs from HTML

I'm trying to create a simple HTML form where users can input their information and have it sent to a PHP file using JavaScript with AJAX. However, I'm encountering an issue where the values from the form are not being included in the email that ...

Why is the margin of a div not factored into the calculation when it

Currently, I am focusing on practicing basic CSS. My goal is to achieve a specific layout for the header of a webpage that resembles this image with a wide top margin: wide top margin. Here is the HTML code I am working with: <div class="heade ...

The screen-responsive navigation bar is experiencing functionality issues

I am facing an issue with my navigation bar on both desktop and mobile. When I maximize the window while the mobile navbar is open, it disappears as expected but the desktop navbar does not appear. I am using a bootstrap template and I am unsure if solving ...

flexbox align items jumble

I am trying to achieve a similar effect as seen in my sketch. The man should always be placed in the right bottom corner of the header. The carousel should be positioned in the center or center-left (50% of the header height). I have added a new class (p ...

How can I enable pagination in Datatables when the table HTML is generated using Angular after the drawCallBack function?

Below is the drawCallBack function I have implemented: "fnDrawCallback": function( oSettings ) { console.log("dt redrawn"); var selector = $("#example_table"); console.log(selector); function recompileForAn ...

Swift: NSAttributeString generates unexpected HTML output

I attempted to use NSAttributeString to parse HTML, but encountered a problem when trying to display a table within a list item. <ol> <li>first <table> <tbody> <tr> ...

How can I display a drilldown label in HighCharts on a mobile device?

When viewing the chart on mobile devices, I notice that the drilldown data label for my pie chart is being truncated with ellipses. My goal is to adjust the styles so that the entire word "streaming" is displayed without any dots cutting off the bottom par ...

Style the labels on the axis of Frappe Charts with colors (potentially utilizing the appropriate CSS selector)

Is it possible to style the x and y axis labels in a Frappe chart with different colors? https://i.stack.imgur.com/A3vUq.png While trying to identify the CSS selectors using Chrome DevTools, I found that a single text element (representing an x axis labe ...

Ensuring the accuracy of checkboxes in a loop through verification

How can I ensure that at least one checkbox is selected before allowing the user to move on to the next page using JavaScript with a form structure like this? echo "<form method=\"POST\" action=\"confirm.php\">"; $query = mysqli_ ...

Whenever I press the view button, all the cards open instead of just the one I chose from the index. Can anyone tell me where I am going wrong?

I've encountered an issue with the View buttons I added to display more detailed information about the characters in the card bodies. When I click on the view button, it opens all the boxes instead of the one I selected. I've tried using a for lo ...

Elements of Web Scraping

Having trouble extracting data from a specific webpage? If the formatting across different websites varies, you might encounter errors like "Subscript out of range" when running certain scripts. If you need help with web scraping, check out this solution ...

Unexpectedly, the jQuery get function retrieves the entire webpage

I'm encountering difficulties when using .get requests to update elements through page load or button press. Here is the code snippet... $(document).ready(function() { //updateVariable(); $('#dannychoolink').html('<?php dan ...

How can I alter the div once the form has been submitted?

Is there a way to change the background of a form and its results after submitting the form? I need to switch the image background to a plain color. I attempted to use a solution I found, but it doesn't seem to be working as expected. You can view my ...

JavaScript is failing to execute the DELETE SQL transaction

I've been attempting to clear out my html5 local sql database, but for some reason, it's not working as expected. Below is the code I'm using, and no matter what I try, the alert always shows that the length is greater than 0. Any ideas why ...

What is the process for uploading an image using Vue.js?

Looking for guidance on how to upload an image using Vue.js. This is my HTML code to insert a picture: <input type="file" id="wizard-picture"> And here is my Vue.js code: <script> submitBox = new Vue({ el: "#submitBox", data: { usernam ...

What is the method to advance the opposing line in the dynamic chart?

Here is the link to the code for a dynamic graph. I am struggling with keeping the red line updated and moving forward together with the blue line. I cannot figure out why the red line remains static in the dynamic graph. Any suggestions on how to solve t ...

What is the best way to create transitions for Placeholder and Label elements?

I need help understanding how to animate a label/placeholder transition up and out of its placeholder position into a label state, and back again. Can anyone provide an example or tutorial on this? Here is an example I found: ...

Tips for attaching an image to an email

Can anyone assist me with an HTML form that sends emails using PHP with image attachments? I have tried various codes, but none of them seem to work properly. The message gets sent with all the texts and even the image name, but the actual image is missing ...

What is the best way to ensure that the background of my sticky table th stays in place and does not scroll away

I have a dynamic table where the table body rows are loaded dynamically. The wrapper uses Bootstrap5's overflow-scroll feature to keep the table at a fixed height and scroll if there are too many rows. I managed to make the table head sticky, but had ...

Google Web Toolkit - Update PopupPanel layout upon rotation

I am working on designing a context menu for specific elements utilizing a PopupPanel; the context menu is expected to be quite extensive and intricate. My goal is to have a collection of buttons, an image, and some text associated with the clicked element ...