16 / 28
All 28 in 28 Tailwind Hamburger Menus
- 01Tailwind Hamburger Menu with Clip-Path Circle Expansion
- 02Tailwind Keyboard Navigable Hamburger Menu with ARIA Attributes
- 03Tailwind Animated 3-Line to X Hamburger Icon Toggle
- 04Tailwind Slide-In Off-Canvas Drawer with Dimmed Backdrop Overlay
- 05Pure Tailwind Checkbox Hack Hamburger Navigation (No JavaScript)
- 06Tailwind Responsive Hamburger Menu with Integrated Search Bar & Social Links
- 07Tailwind Glassmorphism Backdrop Blur Hamburger Navigation
- 08Tailwind Push Page Navigation Menu (Off-Canvas Content Shift)
- 09Pure Tailwind Hamburger Menu Without JavaScript
- 10Tailwind Hamburger Menu Morphing to Close Icon Animation
- 11Tailwind Full Screen Mobile Navigation Overlay
- 12Tailwind Slide Out Sidebar Navigation / Off Canvas Menu
- 13Tailwind Responsive Navbar Mobile Hamburger Toggle Breakpoints
- 14Tailwind Minimalist Floating Hamburger Button
- 15Tailwind CSS Responsive Hamburger Menu Component
- 16Tailwind Rebuild of the Bootstrap 5 Navbar Hamburger Toggle
- 17Tailwind WCAG Accessible Hamburger Menu Example
- 18Pure Tailwind Dropdown Slide Down Mobile Hamburger Menu
- 19Simple 3-Line Tailwind Hamburger Icon Hover Effect
- 20Tailwind Full Screen Blur Overlay Hamburger Menu
- 21Tailwind Mobile Bottom Navigation Bar with Floating Hamburger Toggle
- 22Pure Tailwind Multi-Level Accordion Hamburger Dropdown Menu
- 23Flexbox Navbar Right-Aligned Tailwind Hamburger Icon
- 24Radial Ripple Expand Tailwind Hamburger Menu
- 25Sticky Tailwind Navbar Shrinking Hamburger Icon on Scroll
- 26Neumorphic Tailwind Hamburger Toggle Button
- 27Brutalist Design Bordered Tailwind Hamburger Menu
- 28Tailwind 3D Flip Canvas Mobile Hamburger Navigation
Tailwind Rebuild of the Bootstrap 5 Navbar Hamburger Toggle
What the Bootstrap 5 navbar becomes when you migrate it — the same markup shape, the same breakpoint behaviour, the same animated collapse, with no framework CSS and no collapse plugin. Every Bootstrap class in the chain is mapped to its utility equivalent in the docs, so this doubles as a migration table for the most-copied navbar on the web.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="thb-16 w-full min-h-svh bg-thb16-bg font-thb16-sans text-thb16-ink px-5 py-9" aria-label="Bootstrap navbar rebuilt in Tailwind demo">
<div class="grid gap-3.5 p-5 rounded-[18px] border border-thb16-edge bg-thb16-bg">
<div class="group @container overflow-hidden rounded-[14px] border border-thb16-edge bg-white shadow-[0_14px_34px_-26px_oklch(0.3_0.03_262/0.6)]">
<nav class="py-2 bg-thb16-tertiary border-b border-thb16-edge" aria-label="Main">
<div class="flex flex-wrap items-center justify-between gap-2 px-4">
<a href="#home" class="flex items-center gap-2.25 py-1.5 text-[16.5px] font-semibold text-thb16-ink no-underline">
<span class="w-4.75 h-4.75 rounded-[5px] bg-linear-[140deg,var(--color-thb16-primary),oklch(0.65_0.2_300)]" aria-hidden="true"></span>Bootlace
</a>
<button type="button" aria-expanded="false" aria-controls="thb16-nav" aria-label="Toggle navigation" class="group grid place-items-center w-13 h-11 rounded-lg border border-thb16-edge2 bg-transparent text-thb16-ink cursor-pointer transition-[box-shadow] duration-150 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)] @min-lg:hidden">
<span class="relative block w-5 h-3.5" aria-hidden="true">
<i class="absolute left-0 top-0 w-5 h-0.5 rounded-sm bg-current transition-[translate,rotate] duration-280 group-aria-expanded:translate-y-1.5 group-aria-expanded:rotate-45 motion-reduce:transition-none"></i>
<i class="absolute left-0 top-1.5 w-5 h-0.5 rounded-sm bg-current transition-[opacity] duration-200 group-aria-expanded:opacity-0 motion-reduce:transition-none"></i>
<i class="absolute left-0 top-3 w-5 h-0.5 rounded-sm bg-current transition-[translate,rotate] duration-280 group-aria-expanded:-translate-y-1.5 group-aria-expanded:-rotate-45 motion-reduce:transition-none"></i>
</span>
</button>
<div id="thb16-nav" class="grid basis-full [grid-template-rows:0fr] transition-[grid-template-rows] duration-300 ease-out group-data-open:[grid-template-rows:1fr] @min-lg:basis-auto @min-lg:ml-auto @min-lg:[grid-template-rows:1fr] motion-reduce:transition-none">
<ul class="list-none flex flex-col min-h-0 overflow-hidden px-1 pt-1.5 pb-2.5 @min-lg:flex-row @min-lg:gap-0.5 @min-lg:p-0">
<li><a href="#home" aria-current="page" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline transition-[color,background] duration-150 hover:text-thb16-ink hover:bg-thb16-hover focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)] aria-current:text-thb16-ink aria-current:font-semibold">Home</a></li>
<li><a href="#features" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline transition-[color,background] duration-150 hover:text-thb16-ink hover:bg-thb16-hover focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)]">Features</a></li>
<li><a href="#pricing" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline transition-[color,background] duration-150 hover:text-thb16-ink hover:bg-thb16-hover focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)]">Pricing</a></li>
<li><a href="#beta" aria-disabled="true" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline aria-disabled:text-thb16-off aria-disabled:pointer-events-none">Beta</a></li>
</ul>
</div>
</div>
</nav>
<div class="grid gap-2.75 p-4" aria-hidden="true">
<span class="block h-2.5 w-[76%] rounded-full bg-thb16-line"></span>
<span class="block h-2.5 w-[90%] rounded-full bg-thb16-line"></span>
<span class="block h-2.5 w-[58%] rounded-full bg-thb16-line"></span>
</div>
</div>
<p class="text-xs leading-relaxed text-thb16-mut text-pretty" aria-hidden="true">No Bootstrap CSS and no collapse plugin — the height animation is a grid row track, and the whole script is a four-line attribute toggle. The card is narrower than <code class="font-thb16-mono text-[11px] px-1.5 py-0.5 rounded bg-thb16-line">@lg</code>, so the toggler shows.</p>
</div>
</section><section class="thb-16 w-full min-h-svh bg-thb16-bg font-thb16-sans text-thb16-ink px-5 py-9" aria-label="Bootstrap navbar rebuilt in Tailwind demo">
<div class="grid gap-3.5 p-5 rounded-[18px] border border-thb16-edge bg-thb16-bg">
<div class="group @container overflow-hidden rounded-[14px] border border-thb16-edge bg-white shadow-[0_14px_34px_-26px_oklch(0.3_0.03_262/0.6)]">
<nav class="py-2 bg-thb16-tertiary border-b border-thb16-edge" aria-label="Main">
<div class="flex flex-wrap items-center justify-between gap-2 px-4">
<a href="#home" class="flex items-center gap-2.25 py-1.5 text-[16.5px] font-semibold text-thb16-ink no-underline">
<span class="w-4.75 h-4.75 rounded-[5px] bg-linear-[140deg,var(--color-thb16-primary),oklch(0.65_0.2_300)]" aria-hidden="true"></span>Bootlace
</a>
<button type="button" aria-expanded="false" aria-controls="thb16-nav" aria-label="Toggle navigation" class="group grid place-items-center w-13 h-11 rounded-lg border border-thb16-edge2 bg-transparent text-thb16-ink cursor-pointer transition-[box-shadow] duration-150 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)] @min-lg:hidden">
<span class="relative block w-5 h-3.5" aria-hidden="true">
<i class="absolute left-0 top-0 w-5 h-0.5 rounded-sm bg-current transition-[translate,rotate] duration-280 group-aria-expanded:translate-y-1.5 group-aria-expanded:rotate-45 motion-reduce:transition-none"></i>
<i class="absolute left-0 top-1.5 w-5 h-0.5 rounded-sm bg-current transition-[opacity] duration-200 group-aria-expanded:opacity-0 motion-reduce:transition-none"></i>
<i class="absolute left-0 top-3 w-5 h-0.5 rounded-sm bg-current transition-[translate,rotate] duration-280 group-aria-expanded:-translate-y-1.5 group-aria-expanded:-rotate-45 motion-reduce:transition-none"></i>
</span>
</button>
<div id="thb16-nav" class="grid basis-full [grid-template-rows:0fr] transition-[grid-template-rows] duration-300 ease-out group-data-open:[grid-template-rows:1fr] @min-lg:basis-auto @min-lg:ml-auto @min-lg:[grid-template-rows:1fr] motion-reduce:transition-none">
<ul class="list-none flex flex-col min-h-0 overflow-hidden px-1 pt-1.5 pb-2.5 @min-lg:flex-row @min-lg:gap-0.5 @min-lg:p-0">
<li><a href="#home" aria-current="page" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline transition-[color,background] duration-150 hover:text-thb16-ink hover:bg-thb16-hover focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)] aria-current:text-thb16-ink aria-current:font-semibold">Home</a></li>
<li><a href="#features" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline transition-[color,background] duration-150 hover:text-thb16-ink hover:bg-thb16-hover focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)]">Features</a></li>
<li><a href="#pricing" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline transition-[color,background] duration-150 hover:text-thb16-ink hover:bg-thb16-hover focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)]">Pricing</a></li>
<li><a href="#beta" aria-disabled="true" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline aria-disabled:text-thb16-off aria-disabled:pointer-events-none">Beta</a></li>
</ul>
</div>
</div>
</nav>
<div class="grid gap-2.75 p-4" aria-hidden="true">
<span class="block h-2.5 w-[76%] rounded-full bg-thb16-line"></span>
<span class="block h-2.5 w-[90%] rounded-full bg-thb16-line"></span>
<span class="block h-2.5 w-[58%] rounded-full bg-thb16-line"></span>
</div>
</div>
<p class="text-xs leading-relaxed text-thb16-mut text-pretty" aria-hidden="true">No Bootstrap CSS and no collapse plugin — the height animation is a grid row track, and the whole script is a four-line attribute toggle. The card is narrower than <code class="font-thb16-mono text-[11px] px-1.5 py-0.5 rounded bg-thb16-line">@lg</code>, so the toggler shows.</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-thb16-bg: #fafafc;
--color-thb16-bg: oklch(0.985 0.003 260);
--color-thb16-tertiary: #f6f7f9;
--color-thb16-tertiary: oklch(0.97 0.004 260);
--color-thb16-edge: #e3e4ea;
--color-thb16-edge: oklch(0.9 0.007 260);
--color-thb16-edge2: #c8cad4;
--color-thb16-edge2: oklch(0.82 0.012 262);
--color-thb16-line: #ebecf1;
--color-thb16-line: oklch(0.93 0.005 260);
--color-thb16-hover: #eceef3;
--color-thb16-hover: oklch(0.94 0.006 260);
--color-thb16-ink: #3b3f52;
--color-thb16-ink: oklch(0.28 0.02 262);
--color-thb16-mut: #6a6f85;
--color-thb16-mut: oklch(0.5 0.016 262);
--color-thb16-off: #b0b2bd;
--color-thb16-off: oklch(0.72 0.008 262);
--color-thb16-primary: #4b5fd6;
--color-thb16-primary: oklch(0.55 0.19 262);
--font-thb16-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb16-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-16 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-thb16-bg: #fafafc;
--color-thb16-bg: oklch(0.985 0.003 260);
--color-thb16-tertiary: #f6f7f9;
--color-thb16-tertiary: oklch(0.97 0.004 260);
--color-thb16-edge: #e3e4ea;
--color-thb16-edge: oklch(0.9 0.007 260);
--color-thb16-edge2: #c8cad4;
--color-thb16-edge2: oklch(0.82 0.012 262);
--color-thb16-line: #ebecf1;
--color-thb16-line: oklch(0.93 0.005 260);
--color-thb16-hover: #eceef3;
--color-thb16-hover: oklch(0.94 0.006 260);
--color-thb16-ink: #3b3f52;
--color-thb16-ink: oklch(0.28 0.02 262);
--color-thb16-mut: #6a6f85;
--color-thb16-mut: oklch(0.5 0.016 262);
--color-thb16-off: #b0b2bd;
--color-thb16-off: oklch(0.72 0.008 262);
--color-thb16-primary: #4b5fd6;
--color-thb16-primary: oklch(0.55 0.19 262);
--font-thb16-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb16-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-16 {
width: 100%;
min-height: 100svh;
}(function () {
document.querySelectorAll('.thb-16 div.group').forEach(function (frame) {
if (frame.dataset.bound) return; frame.dataset.bound = '1';
var btn = frame.querySelector('button[aria-controls="thb16-nav"]');
btn.addEventListener('click', function () {
var open = btn.getAttribute('aria-expanded') !== 'true';
frame.toggleAttribute('data-open', open);
btn.setAttribute('aria-expanded', String(open));
});
});
})();(function () {
document.querySelectorAll('.thb-16 div.group').forEach(function (frame) {
if (frame.dataset.bound) return; frame.dataset.bound = '1';
var btn = frame.querySelector('button[aria-controls="thb16-nav"]');
btn.addEventListener('click', function () {
var open = btn.getAttribute('aria-expanded') !== 'true';
frame.toggleAttribute('data-open', open);
btn.setAttribute('aria-expanded', String(open));
});
});
})();Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-thb16-bg: #fafafc;
--color-thb16-bg: oklch(0.985 0.003 260);
--color-thb16-tertiary: #f6f7f9;
--color-thb16-tertiary: oklch(0.97 0.004 260);
--color-thb16-edge: #e3e4ea;
--color-thb16-edge: oklch(0.9 0.007 260);
--color-thb16-edge2: #c8cad4;
--color-thb16-edge2: oklch(0.82 0.012 262);
--color-thb16-line: #ebecf1;
--color-thb16-line: oklch(0.93 0.005 260);
--color-thb16-hover: #eceef3;
--color-thb16-hover: oklch(0.94 0.006 260);
--color-thb16-ink: #3b3f52;
--color-thb16-ink: oklch(0.28 0.02 262);
--color-thb16-mut: #6a6f85;
--color-thb16-mut: oklch(0.5 0.016 262);
--color-thb16-off: #b0b2bd;
--color-thb16-off: oklch(0.72 0.008 262);
--color-thb16-primary: #4b5fd6;
--color-thb16-primary: oklch(0.55 0.19 262);
--font-thb16-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb16-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-16 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-thb16-bg: #fafafc;
--color-thb16-bg: oklch(0.985 0.003 260);
--color-thb16-tertiary: #f6f7f9;
--color-thb16-tertiary: oklch(0.97 0.004 260);
--color-thb16-edge: #e3e4ea;
--color-thb16-edge: oklch(0.9 0.007 260);
--color-thb16-edge2: #c8cad4;
--color-thb16-edge2: oklch(0.82 0.012 262);
--color-thb16-line: #ebecf1;
--color-thb16-line: oklch(0.93 0.005 260);
--color-thb16-hover: #eceef3;
--color-thb16-hover: oklch(0.94 0.006 260);
--color-thb16-ink: #3b3f52;
--color-thb16-ink: oklch(0.28 0.02 262);
--color-thb16-mut: #6a6f85;
--color-thb16-mut: oklch(0.5 0.016 262);
--color-thb16-off: #b0b2bd;
--color-thb16-off: oklch(0.72 0.008 262);
--color-thb16-primary: #4b5fd6;
--color-thb16-primary: oklch(0.55 0.19 262);
--font-thb16-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb16-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-16 {
width: 100%;
min-height: 100svh;
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Bootstrap's collapse plugin disappears. Its
scrollHeightmeasuring, inline height writing and class dance (collapse→collapsing→collapse show) become one animated grid row track and a four-line toggle. - The CSS version of this demo had to ship a shim for Bootstrap's styles AND a 15-line stand-in for the plugin. Both are gone; nothing here is a stand-in for anything.
navbar-expand-lgbecomes@max-lg:/@min-lg:container variants, which fixes a real Bootstrap limitation: the breakpoint now tracks the component, so an embedded navbar collapses when IT is narrow, not when the window is.- Bootstrap's
box-shadowfocus ring is kept on purpose, as an arbitraryfocus-visible:shadow-[…]reading the primary token — migrations feel wrong when focus styling changes underfoot. .nav-link.activebecomesaria-current:, and.nav-link.disabledbecomesaria-disabled:pluspointer-events-none— both moving from presentational classes to attributes screen readers already understand.- The wide state must force the panel open (
@min-lg:[grid-template-rows:1fr]), which is the utility equivalent of Bootstrap's.navbar-collapse.collapse:not(.show){display:flex}desktop override.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Rebuild of the Bootstrap 5 Navbar Hamburger Toggle
Source: https://codefronts.com/navigation/tailwind-hamburger-menus/tailwind-rebuild-bootstrap-5-navbar-hamburger-toggle/
What the Bootstrap 5 navbar becomes when you migrate it — the same markup shape, the same breakpoint behaviour, the same animated collapse, with no framework CSS and no collapse plugin. Every Bootstrap class in the chain is mapped to its utility equivalent in the docs, so this doubles as a migration table for the most-copied navbar on the web.
## HTML
```html
<section class="thb-16 w-full min-h-svh bg-thb16-bg font-thb16-sans text-thb16-ink px-5 py-9" aria-label="Bootstrap navbar rebuilt in Tailwind demo">
<div class="grid gap-3.5 p-5 rounded-[18px] border border-thb16-edge bg-thb16-bg">
<div class="group @container overflow-hidden rounded-[14px] border border-thb16-edge bg-white shadow-[0_14px_34px_-26px_oklch(0.3_0.03_262/0.6)]">
<nav class="py-2 bg-thb16-tertiary border-b border-thb16-edge" aria-label="Main">
<div class="flex flex-wrap items-center justify-between gap-2 px-4">
<a href="#home" class="flex items-center gap-2.25 py-1.5 text-[16.5px] font-semibold text-thb16-ink no-underline">
<span class="w-4.75 h-4.75 rounded-[5px] bg-linear-[140deg,var(--color-thb16-primary),oklch(0.65_0.2_300)]" aria-hidden="true"></span>Bootlace
</a>
<button type="button" aria-expanded="false" aria-controls="thb16-nav" aria-label="Toggle navigation" class="group grid place-items-center w-13 h-11 rounded-lg border border-thb16-edge2 bg-transparent text-thb16-ink cursor-pointer transition-[box-shadow] duration-150 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)] @min-lg:hidden">
<span class="relative block w-5 h-3.5" aria-hidden="true">
<i class="absolute left-0 top-0 w-5 h-0.5 rounded-sm bg-current transition-[translate,rotate] duration-280 group-aria-expanded:translate-y-1.5 group-aria-expanded:rotate-45 motion-reduce:transition-none"></i>
<i class="absolute left-0 top-1.5 w-5 h-0.5 rounded-sm bg-current transition-[opacity] duration-200 group-aria-expanded:opacity-0 motion-reduce:transition-none"></i>
<i class="absolute left-0 top-3 w-5 h-0.5 rounded-sm bg-current transition-[translate,rotate] duration-280 group-aria-expanded:-translate-y-1.5 group-aria-expanded:-rotate-45 motion-reduce:transition-none"></i>
</span>
</button>
<div id="thb16-nav" class="grid basis-full [grid-template-rows:0fr] transition-[grid-template-rows] duration-300 ease-out group-data-open:[grid-template-rows:1fr] @min-lg:basis-auto @min-lg:ml-auto @min-lg:[grid-template-rows:1fr] motion-reduce:transition-none">
<ul class="list-none flex flex-col min-h-0 overflow-hidden px-1 pt-1.5 pb-2.5 @min-lg:flex-row @min-lg:gap-0.5 @min-lg:p-0">
<li><a href="#home" aria-current="page" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline transition-[color,background] duration-150 hover:text-thb16-ink hover:bg-thb16-hover focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)] aria-current:text-thb16-ink aria-current:font-semibold">Home</a></li>
<li><a href="#features" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline transition-[color,background] duration-150 hover:text-thb16-ink hover:bg-thb16-hover focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)]">Features</a></li>
<li><a href="#pricing" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline transition-[color,background] duration-150 hover:text-thb16-ink hover:bg-thb16-hover focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)]">Pricing</a></li>
<li><a href="#beta" aria-disabled="true" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline aria-disabled:text-thb16-off aria-disabled:pointer-events-none">Beta</a></li>
</ul>
</div>
</div>
</nav>
<div class="grid gap-2.75 p-4" aria-hidden="true">
<span class="block h-2.5 w-[76%] rounded-full bg-thb16-line"></span>
<span class="block h-2.5 w-[90%] rounded-full bg-thb16-line"></span>
<span class="block h-2.5 w-[58%] rounded-full bg-thb16-line"></span>
</div>
</div>
<p class="text-xs leading-relaxed text-thb16-mut text-pretty" aria-hidden="true">No Bootstrap CSS and no collapse plugin — the height animation is a grid row track, and the whole script is a four-line attribute toggle. The card is narrower than <code class="font-thb16-mono text-[11px] px-1.5 py-0.5 rounded bg-thb16-line">@lg</code>, so the toggler shows.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-thb16-bg: #fafafc;
--color-thb16-bg: oklch(0.985 0.003 260);
--color-thb16-tertiary: #f6f7f9;
--color-thb16-tertiary: oklch(0.97 0.004 260);
--color-thb16-edge: #e3e4ea;
--color-thb16-edge: oklch(0.9 0.007 260);
--color-thb16-edge2: #c8cad4;
--color-thb16-edge2: oklch(0.82 0.012 262);
--color-thb16-line: #ebecf1;
--color-thb16-line: oklch(0.93 0.005 260);
--color-thb16-hover: #eceef3;
--color-thb16-hover: oklch(0.94 0.006 260);
--color-thb16-ink: #3b3f52;
--color-thb16-ink: oklch(0.28 0.02 262);
--color-thb16-mut: #6a6f85;
--color-thb16-mut: oklch(0.5 0.016 262);
--color-thb16-off: #b0b2bd;
--color-thb16-off: oklch(0.72 0.008 262);
--color-thb16-primary: #4b5fd6;
--color-thb16-primary: oklch(0.55 0.19 262);
--font-thb16-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb16-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-16 {
width: 100%;
min-height: 100svh;
}
```
## JavaScript
```js
(function () {
document.querySelectorAll('.thb-16 div.group').forEach(function (frame) {
if (frame.dataset.bound) return; frame.dataset.bound = '1';
var btn = frame.querySelector('button[aria-controls="thb16-nav"]');
btn.addEventListener('click', function () {
var open = btn.getAttribute('aria-expanded') !== 'true';
frame.toggleAttribute('data-open', open);
btn.setAttribute('aria-expanded', String(open));
});
});
})();
```Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Rebuild of the Bootstrap 5 Navbar Hamburger Toggle
Source: https://codefronts.com/navigation/tailwind-hamburger-menus/tailwind-rebuild-bootstrap-5-navbar-hamburger-toggle/
What the Bootstrap 5 navbar becomes when you migrate it — the same markup shape, the same breakpoint behaviour, the same animated collapse, with no framework CSS and no collapse plugin. Every Bootstrap class in the chain is mapped to its utility equivalent in the docs, so this doubles as a migration table for the most-copied navbar on the web.
## HTML
```html
<section class="thb-16 w-full min-h-svh bg-thb16-bg font-thb16-sans text-thb16-ink px-5 py-9" aria-label="Bootstrap navbar rebuilt in Tailwind demo">
<div class="grid gap-3.5 p-5 rounded-[18px] border border-thb16-edge bg-thb16-bg">
<div class="group @container overflow-hidden rounded-[14px] border border-thb16-edge bg-white shadow-[0_14px_34px_-26px_oklch(0.3_0.03_262/0.6)]">
<nav class="py-2 bg-thb16-tertiary border-b border-thb16-edge" aria-label="Main">
<div class="flex flex-wrap items-center justify-between gap-2 px-4">
<a href="#home" class="flex items-center gap-2.25 py-1.5 text-[16.5px] font-semibold text-thb16-ink no-underline">
<span class="w-4.75 h-4.75 rounded-[5px] bg-linear-[140deg,var(--color-thb16-primary),oklch(0.65_0.2_300)]" aria-hidden="true"></span>Bootlace
</a>
<button type="button" aria-expanded="false" aria-controls="thb16-nav" aria-label="Toggle navigation" class="group grid place-items-center w-13 h-11 rounded-lg border border-thb16-edge2 bg-transparent text-thb16-ink cursor-pointer transition-[box-shadow] duration-150 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)] @min-lg:hidden">
<span class="relative block w-5 h-3.5" aria-hidden="true">
<i class="absolute left-0 top-0 w-5 h-0.5 rounded-sm bg-current transition-[translate,rotate] duration-280 group-aria-expanded:translate-y-1.5 group-aria-expanded:rotate-45 motion-reduce:transition-none"></i>
<i class="absolute left-0 top-1.5 w-5 h-0.5 rounded-sm bg-current transition-[opacity] duration-200 group-aria-expanded:opacity-0 motion-reduce:transition-none"></i>
<i class="absolute left-0 top-3 w-5 h-0.5 rounded-sm bg-current transition-[translate,rotate] duration-280 group-aria-expanded:-translate-y-1.5 group-aria-expanded:-rotate-45 motion-reduce:transition-none"></i>
</span>
</button>
<div id="thb16-nav" class="grid basis-full [grid-template-rows:0fr] transition-[grid-template-rows] duration-300 ease-out group-data-open:[grid-template-rows:1fr] @min-lg:basis-auto @min-lg:ml-auto @min-lg:[grid-template-rows:1fr] motion-reduce:transition-none">
<ul class="list-none flex flex-col min-h-0 overflow-hidden px-1 pt-1.5 pb-2.5 @min-lg:flex-row @min-lg:gap-0.5 @min-lg:p-0">
<li><a href="#home" aria-current="page" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline transition-[color,background] duration-150 hover:text-thb16-ink hover:bg-thb16-hover focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)] aria-current:text-thb16-ink aria-current:font-semibold">Home</a></li>
<li><a href="#features" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline transition-[color,background] duration-150 hover:text-thb16-ink hover:bg-thb16-hover focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)]">Features</a></li>
<li><a href="#pricing" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline transition-[color,background] duration-150 hover:text-thb16-ink hover:bg-thb16-hover focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_color-mix(in_oklch,var(--color-thb16-primary)_35%,transparent)]">Pricing</a></li>
<li><a href="#beta" aria-disabled="true" class="block px-3 py-2.5 rounded-lg text-[14.5px] font-medium text-thb16-mut no-underline aria-disabled:text-thb16-off aria-disabled:pointer-events-none">Beta</a></li>
</ul>
</div>
</div>
</nav>
<div class="grid gap-2.75 p-4" aria-hidden="true">
<span class="block h-2.5 w-[76%] rounded-full bg-thb16-line"></span>
<span class="block h-2.5 w-[90%] rounded-full bg-thb16-line"></span>
<span class="block h-2.5 w-[58%] rounded-full bg-thb16-line"></span>
</div>
</div>
<p class="text-xs leading-relaxed text-thb16-mut text-pretty" aria-hidden="true">No Bootstrap CSS and no collapse plugin — the height animation is a grid row track, and the whole script is a four-line attribute toggle. The card is narrower than <code class="font-thb16-mono text-[11px] px-1.5 py-0.5 rounded bg-thb16-line">@lg</code>, so the toggler shows.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-thb16-bg: #fafafc;
--color-thb16-bg: oklch(0.985 0.003 260);
--color-thb16-tertiary: #f6f7f9;
--color-thb16-tertiary: oklch(0.97 0.004 260);
--color-thb16-edge: #e3e4ea;
--color-thb16-edge: oklch(0.9 0.007 260);
--color-thb16-edge2: #c8cad4;
--color-thb16-edge2: oklch(0.82 0.012 262);
--color-thb16-line: #ebecf1;
--color-thb16-line: oklch(0.93 0.005 260);
--color-thb16-hover: #eceef3;
--color-thb16-hover: oklch(0.94 0.006 260);
--color-thb16-ink: #3b3f52;
--color-thb16-ink: oklch(0.28 0.02 262);
--color-thb16-mut: #6a6f85;
--color-thb16-mut: oklch(0.5 0.016 262);
--color-thb16-off: #b0b2bd;
--color-thb16-off: oklch(0.72 0.008 262);
--color-thb16-primary: #4b5fd6;
--color-thb16-primary: oklch(0.55 0.19 262);
--font-thb16-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb16-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-16 {
width: 100%;
min-height: 100svh;
}
```
## JavaScript
```js
(function () {
document.querySelectorAll('.thb-16 div.group').forEach(function (frame) {
if (frame.dataset.bound) return; frame.dataset.bound = '1';
var btn = frame.querySelector('button[aria-controls="thb16-nav"]');
btn.addEventListener('click', function () {
var open = btn.getAttribute('aria-expanded') !== 'true';
frame.toggleAttribute('data-open', open);
btn.setAttribute('aria-expanded', String(open));
});
});
})();
```How this works
The mapping, one line at a time.
navbar navbar-expand-lg bg-body-tertiary
→ flex flex-wrap items-center justify-between bg-thb16-tertiary
navbar-brand → flex items-center gap-2 font-semibold no-underline
navbar-toggler → grid place-items-center w-13 h-11 border rounded-lg
@min-lg:hidden
collapse navbar-collapse
→ grid basis-full [grid-template-rows:0fr] @min-lg:[grid-template-rows:1fr]
navbar-nav ms-auto → list-none flex flex-col @min-lg:flex-row @min-lg:ml-auto
nav-link → block px-3 py-2.5 rounded-lg font-medium
nav-link.active → aria-current:text-thb16-ink aria-current:font-semiboldWhat you lose, and it is not much. Bootstrap's collapse plugin measures scrollHeight, writes an inline height, transitions it and cleans up — about 60 lines of framework JS. The utility version transitions a grid row track instead, which needs no measuring at all, and the whole script becomes a four-line attribute toggle.
What you keep on purpose. The markup shape (brand, toggler, collapse target) and the data-bs-*-style pairing of a button with the panel it controls — except here they are honest ARIA attributes: aria-controls is an id, aria-expanded is the state, and there is no selector-vs-id confusion to get wrong. Bootstrap's own docs list that mix-up as the top cause of “hamburger not working”.
The breakpoint is a container query so the navbar behaves correctly inside a card, a split pane or a full page — something navbar-expand-lg cannot do, since it is hard-wired to the viewport.
Make it yours
- Breakpoint: swap every
@max-lg:/@min-lg:pair for the size you need — the equivalent of choosingnavbar-expand-{sm|md|lg|xl}. Omit them entirely for an always-burger nav. - Alignment:
@min-lg:ml-autopushes the links right,@min-lg:mx-autocentres them. Same margin trick Bootstrap uses, no navbar-specific classes. - Collapse speed: the
duration-300on the panel. Bootstrap's default is 350ms; matching it makes a partial migration feel seamless. - Dark navbar: Bootstrap 5.3 uses
data-bs-theme="dark"; here prefix the surface utilities withdark:and the toggler follows automatically. - Retheme through
--color-thb16-primary, which feeds the brand mark, the active link and the focus ring.
Gotchas — read before shipping
- Do not keep Bootstrap's CSS loaded “just for the grid” while adding utilities. Two competing resets on the same navbar is where mystery specificity bugs come from — migrate the component fully or not at all.
- The panel's list still needs
min-h-0 overflow-hidden, or the0frtrack refuses to compress and the menu is stuck open. - Keep
type="button"on the toggler. Inside a form, a typeless button submits — Bootstrap's classic “menu opens then instantly closes”. - Two navbars on one page need two ids for
aria-controlsand twogrouproots. Bootstrap's selector-based targeting has the same trap. aria-disabledpluspointer-events-noneis not the same asdisabled: the link stays in the tab order by design, which is the accessible way to expose a temporarily unavailable item.- The wide-state panel must be forced open (
@min-lg:[grid-template-rows:1fr]), or resizing while closed leaves the desktop links collapsed to zero height.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
The 0fr → 1fr row animation is Baseline 2023 and container queries need Chrome 105, Safari 16 and Firefox 110; the floor quoted here is Tailwind v4's own generated sheet. Bootstrap 5.3 itself targets roughly the same modern browsers.