Responsive Navbar using Only Tailwind CSS and CSS

This will be a short guide with snippets to create a fully responsive navbar using tailwind css (and css). First let's see it in using Next.js but the styling remains the same for tailwind and can be easily obtained as css if wanted.

JSX

function LogoText() {
    return (<>
        <div className="logo-font font-architects">
            PG's BLOG
        </div>
    </>
    );
}

export default function Navbar() {
    return (<>
        <nav className="navbar fixed border-b-2 border-neutral-200">
            <div className="flex w-full sm:w-auto sm:block flex-row items-center justify-between">

                <Link href={"/"}><LogoText /></Link>


                <div className="sm:hidden justify-end menu-icon">
                    <input type="checkbox" id="menu-toggle" />
                    <label htmlFor="menu-toggle">
                        <MenuIcon />
                    </label>
                </div>
            </div>




            <div className="xlg:gap-10 lg:gap-8 md:gap-5 sm:gap-2 nav-links">
                <Link href={"/"}>Blogs</Link>
                <Link href={"/portfolio"}>Portfolio</Link>
                <Link href={"/projects"}>Projects</Link>
                <Link href={"/contact"}>Contact</Link>
            </div>
            <div className="user hidden sm:flex justify-end">Sign Up</div>




            <div className="nav-expanded">
                <div className="nav-links-vertical">
                <Link href={"/"}>Blogs</Link>
                <Link href={"/portfolio"}>Portfolio</Link>
                <Link href={"/projects"}>Projects</Link>
                <Link href={"/contact"}>Contact</Link>
                </div>

                <div className="sm:flex justify-end">Sign Up</div>
            </div>
        </nav>
        <br/>
        <br/>
        </>
);
}

The navbar is fixed to the top and contains the company name/logo along with links to various routes and also a signup button. The Link tag is a Next.js feature and can be sustituted by <a> tag or equivalent for the used framework. The main principle is that there is a nav-expanded div and nav-links to show the links. The nav-expanded is not visible for larger screens and only visible on smaller screens like mobile phones. The section

CSS

.logo-font {
	font-size: min(32px, max(28px, calc(100vw / 10)));
	paint-order: stroke fill;
	text-stroke: 0.5px #171717;
	-webkit-text-stroke: 0.5px #171717;
}

.nav-links {
	flex: 1 1 0%;
	flex-direction: row;
	justify-content: center;
	a {
		color: rgb(81, 81, 81);
		position: relative;
	}

	a::before {
		content: "";
		position: absolute;
		display: block;
		width: 100%;
		height: 2.5px;
		bottom: 0;
		left: 0;
		background-color: rgb(0, 0, 255);
		transform: scaleX(0);
		transform-origin: top left;
		transition: transform 0.3s ease;
	}
	a:hover::before {
		transform: scaleX(1);
	}
}

@media (min-width: 640px) {
	.nav-links {
		display: flex;
	}
}

@media (max-width: 640px) {
	.nav-links {
		display: none;
	}

	.navbar:has(#menu-toggle:checked) {
		.nav-expanded {
			width: 100%;
			display: flex;
			position: absolute;
			top: 100%;
			left: 0%;
		}
		.menu-icon {
			transform:scale(1) rotate(-180deg);
		}
	}
}

.nav-expanded {
	display: none;
	padding: 0px max(4vw,15px) 5px max(4vw,15px);
	gap: 5px;
	flex-direction: column;
	align-items: start;
	justify-content: start;
	background-color: #fbfbfb;
	border-bottom: #171717  1px solid;
	border-top: #171717  1px solid;

	user-select: none;

	.nav-links-vertical {
		display: flex;
		flex-direction: column;
		width: 100%;
		border-bottom: 1px solid #bababa;
		gap: 10px;

        a {
            color: rgb(81, 81, 81);
            position: relative;
        }
    
        a::before {
            content: "";
            position: absolute;
            display: block;
            width: 100%;
            height: 2.5px;
            bottom: 0;
            left: 0;
            background-color: rgb(0, 0, 255);
            transform: scaleX(0);
            transform-origin: top left;
            transition: transform 0.1s ease;
        }
        a:hover::before {
            transform: scaleX(1);
        }
	}
}

#menu-toggle {
	visibility: hidden;
	position: absolute;
	pointer-events: none;
}

.menu-icon{
	transition: transform 0.3s ease;
	transform:scale(1) rotate(0deg);
}
.menu-icon:hover{
    transform: scale(1.1);
}
.menu-icon:active{
    transform:scale(0.5);
}

.navbar {
	@apply flex flex-row justify-between items-center w-full bg-white/95 px-[max(4vw,15px)] sm:px-[3vw] fixed z-10;
}

Let's look at the how to handle the hamburger icon for smaller screens for the navbar. The related style is:

@media (max-width: 640px) {    
    ...

	.navbar:has(#menu-toggle:checked) {
		.nav-expanded {
			width: 100%;
			display: flex;
			position: absolute;
			top: 100%;
			left: 0%;
		}
		.menu-icon {
			transform:scale(1) rotate(-180deg);
		}
	}
}

#menu-toggle {
	visibility: hidden;
	position: absolute;
	pointer-events: none;
}

Here we are using a checkbox to handle the navbar expanded and navbar collapsed states. The checkbox is hidden from view with a absolute position and pointer events are turned off. The checkbox has a label which is the hamburger icon, so when the icon is clicked the checkbox is toggled. This eliminated need of js to handle the state.

Pure HTML/CSS

For my blog, I modified this navbar and removed the signup/profile section and changed from tailwind to css. You can see the live demo in this blog itself. Here is the code:

<nav class="navbar">
    <div class="nav-container">
        <a href="#" class="logo-font">
            <span class="text-red">PG</span>'s Blog
        </a>

        <div class="menu-icon-container">
            <input type="checkbox" id="menu-toggle">
            <label for="menu-toggle" class="menu-icon">
                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
                    <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
                </svg>                      
            </label>
        </div>
    </div>

    <div class="nav-links">
        <a href="#">Blog</a>
        <a href="#">Portfolio</a>
        <a href="#">Projects</a>
        <a href="#">Resume</a>
    </div>

    <div class="nav-expanded">
        <div class="nav-links-vertical">
            <a href="#">Blog</a>
            <a href="#">Portfolio</a>
            <a href="#">Projects</a>
            <a href="#">Resume</a>
        </div>
    </div>
</nav>
<style>
    .logo-font {
        font-size: min(25px, max(28px, calc(100vw / 10)));
        text-stroke: 0.5px #171717;
        -webkit-text-stroke: 0.5px #171717;
        text-decoration: none;
    }

    .text-red {
        color: rgb(255, 0, 0);
    }

    .navbar {
        display: flex;
        flex-direction: row;
        justify-content: space-between;
        align-items: center;
        width: 100%;
        background-color: white;
        padding: 5px 15px ; 
        position: fixed;
        top: 0;
        z-index: 10;
        border-bottom: 2px solid #E5E5E5;
    }

    .nav-container {
        display: flex; 
        width: 100%;
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
    }
    
    @media (min-width: 640px) {
        .nav-container {
            width: auto;
            display: block;
        }
    }

    .nav-expanded {
        display: none;
        padding: 0px max(4vw, 15px) 5px max(4vw, 15px);
        gap: 5px;
        flex-direction: column;
        align-items: start;
        justify-content: start;
        background-color: #fbfbfb;
        border-bottom: #171717 1px solid;
        border-top: #171717 1px solid;

        user-select: none;

        .nav-links-vertical {
            display: flex;
            flex-direction: column;
            width: 100%;
            border-bottom: 1px solid #bababa;
            gap: 10px;

            a {
                color: rgb(81, 81, 81);
                position: relative;
            }

            a::before {
                content: "";
                position: absolute;
                display: block;
                width: 100%;
                height: 2.5px;
                bottom: 0;
                left: 0;
                background-color: rgb(0, 0, 255);
                transform: scaleX(0);
                transform-origin: top left;
                transition: transform 0.1s ease;
            }

            a:hover::before {
                transform: scaleX(1);
            }
        }
    }

    .menu-icon-container {
        display: none;
    }

    .nav-links {
        display: flex;
        gap: 20px;
    }

    .nav-links a {
        color: rgb(81, 81, 81);
        position: relative;
        text-decoration: none;
    }

    .nav-links a::before {
        content: "";
        position: absolute;
        display: block;
        width: 100%;
        height: 2.5px;
        bottom: 0;
        left: 0;
        background-color: rgb(0, 0, 255);
        transform: scaleX(0);
        transform-origin: top left;
        transition: transform 0.3s ease;
    }

    .nav-links a:hover::before {
        transform: scaleX(1);
    }


    @media (max-width: 640px) {
        .nav-links {
            display: none;
        }

        .menu-icon-container {
            display: block;
        }

        #menu-toggle {
            display: none;
        }

        .menu-icon {
            width: 30px;
            height: 30px;
            background: url('menu-icon.svg') no-repeat center;
            cursor: pointer;
        }

        .navbar:has(#menu-toggle:checked) .nav-expanded {
            display: flex;
            position: absolute;
            top: 100%;
            left: 0;
            width: 100%;
            flex-direction: column;
            background-color: #fbfbfb;
            border-top: 1px solid #171717;
            border-bottom: 1px solid #171717;
        }

        .nav-expanded {
            display: none;
            padding: 5px 15px;
        }

        .nav-links-vertical {
            display: flex;
            flex-direction: column;
            gap: 10px;
            border-bottom: 1px solid #bababa;
        }

        .nav-links-vertical a {
            color: rgb(81, 81, 81);
            position: relative;
            text-decoration: none;
        }

        .nav-links-vertical a::before {
            content: "";
            position: absolute;
            display: block;
            width: 100%;
            height: 2.5px;
            bottom: 0;
            left: 0;
            background-color: rgb(0, 0, 255);
            transform: scaleX(0);
            transform-origin: top left;
            transition: transform 0.1s ease;
        }

        .nav-links-vertical a:hover::before {
            transform: scaleX(1);
        }
    }
</style>