Caio Cerano | Designer & Developer
Designed & Built by
Loosely conceptualized using Figma & animated with Framer Motion.
Built on Next.js & styled with Tailwind CSS.
Animated Hamburger Icon
This is the custom icon I created for my portfolio.
Tags
Tailwind CSS
React
This is a React and Tailwind CSS-based navigation icon that dynamically transitioning between a hamburger menu and an 'X'.
Introduction
In my portfolio website, the site you're currently using, there's a navigation bar at the top that's optimized for PC and larger screens. However, it doesn't work as smoothly on mobile and smaller screens. So, I began exploring different menu implementations and settled on a sidebar menu that's accessible through a button at the screen's top. I aimed for a clean and simple animation for this button. After some digging, I found the "hamburger-react" library. It seemed like a solid fix for this menu issue for many users, but I felt it was an overkill for my needs since I've already got a button component that could do the job with just a bit of animation. Or maybe, I did try the library but ran into minor snags and decided to take matters into my own hands. I'll leave that to your imagination.
I'm working with React and Tailwind CSS on this project. My goal was to create this animation as simply and elegantly as possible.
Overview of the Code Snippet
Here, I'm showcasing a React functional component called MenuIcon. It's a key player in displaying a mobile-friendly menu icon. It takes a single prop, isOpen, which toggles the icon's appearance based on whether the menu is open or not. At its core, this component has a div element, styled with Tailwind CSS, that serves as the icon's container. Inside this div are two span elements that make up the menu icon's lines. Their orientation shifts from horizontal to a crossed pattern, controlled by the isOpen state. Thanks to Tailwind's utility classes like absolute, transform, and transition, the icon gets its precise positioning and smooth animation. The dynamic rendering here changes its appearance in a snap.
import React from 'react'
interface MenuIconProps {
isOpen: boolean
}
const MenuIcon: React.FC<MenuIconProps> = ({ isOpen }) => {
return (
<div className="block w-10 absolute left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2">
<span
aria-hidden="true"
className={`block absolute bg-white h-0.5 w-10 transform transition duration-500 ease-in-out ${isOpen ? 'rotate-45' : '-translate-y-1.5'}`}
/>
<span
aria-hidden="true"
className={`block absolute bg-white h-0.5 w-10 transform transition duration-500 ease-in-out ${isOpen ? '-rotate-45' : 'translate-y-1.5'}`}
/>
</div>
)
}
export default MenuIcon
Problems I Found
During the development of this component, I hit a snag. The span elements weren't landing where I wanted them to when transitioning into the 'X' shape. They were slightly off, not forming a perfect X. Initially, I tried creating this component without using position absolute in the styling. This caused the tiny, yet noticeable, difference in the X formation. However, I managed to fix it by rewriting the styling to include position absolute for both span elements.
Detailing the JSX Structure
The component's structure starts with an outer div, ensuring the icon is properly sized and centered. Within this div, each span element forms a line of the menu icon. The applied classes on these spans dictate their appearance and position. I used backticks and ${} syntax in the className for conditionally applying Tailwind classes based on the isOpen state. For example, when the menu is open (isOpen is true), the top line rotates 45 degrees to form the upper part of a cross. In contrast, when the menu is closed, it reverts to its horizontal position. The rotate-45, -rotate-45, and translate-y classes are key to the animation, ensuring the icon morphs smoothly and visibly between states.
Conclusion
The MenuIcon component is my simple solution to a small problem with the menu icon. It's been a great exercise in animation and finding ways to overcome minor issues without leaning on a library. Feel free to use this example as you like. Credit me if you want, but it's not necessary. I think it's a solid starting point for developers to adapt and tweak to fit their project's unique needs.