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)