Nav Bars
Bottom tab bar
Mobile app navigation pinned to the bottom edge
Mobile app navigation pinned to the bottom edge
Create a mobile bottom navigation with Tailwind CSS. Draw a narrow phone-shaped frame with a rounded border on a neutral background, containing a scrollable content area of placeholder blocks and, fixed to the bottom of the frame, a tab bar. The bar has five items with a glyph above a tiny label; the active item is neutral-900 with a small dot under the label while the others are muted, one carries a red notification dot on its glyph, and the middle item is a raised circular dark button with a plus glyph that sits slightly above the bar line. Add a safe-area strip below the tabs. Then add JavaScript that switches the active tab on tap, moves the indicator dot, updates the heading in the content area, and clears the notification dot the first time its tab is opened. <section class="grid min-h-screen place-items-center bg-neutral-100 px-6 py-12">
<div class="relative flex h-[640px] w-[340px] flex-col overflow-hidden rounded-[2.5rem] border-8 border-neutral-900 bg-white">
<div class="flex-1 overflow-y-auto p-6">
<h1 id="tabTitle" class="text-2xl font-semibold tracking-tight text-neutral-900">Today</h1>
<p class="mt-1 text-sm text-neutral-500">Wednesday 18 August</p>
<div class="mt-6 space-y-3">
<div class="h-24 rounded-2xl bg-neutral-100"></div>
<div class="h-24 rounded-2xl bg-neutral-100"></div>
<div class="h-24 rounded-2xl bg-neutral-100"></div>
<div class="h-24 rounded-2xl bg-neutral-100"></div>
</div>
</div>
<nav class="relative border-t border-neutral-200 bg-white/95 px-2 pb-6 pt-2 backdrop-blur">
<div class="flex items-end justify-around">
<button data-tab="Today" class="tab-item flex w-14 flex-col items-center gap-1 py-1 text-neutral-900">
<span class="text-lg"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><path d="m3 11 9-8 9 8"/><path d="M5 10v10h14V10M9 20v-6h6v6"/></svg></span>
<span class="text-[10px] font-medium">Today</span>
<span class="dot h-1 w-1 rounded-full bg-neutral-900"></span>
</button>
<button data-tab="Search" class="tab-item flex w-14 flex-col items-center gap-1 py-1 text-neutral-400">
<span class="text-lg"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><circle cx="11" cy="11" r="7"/><path d="m16 16 4 4"/></svg></span>
<span class="text-[10px] font-medium">Search</span>
<span class="dot h-1 w-1 rounded-full bg-transparent"></span>
</button>
<button class="-mt-6 grid h-14 w-14 place-items-center rounded-full bg-neutral-900 text-xl text-white shadow-lg" aria-label="New entry"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><path d="M12 5v14M5 12h14"/></svg></button>
<button data-tab="Inbox" class="tab-item flex w-14 flex-col items-center gap-1 py-1 text-neutral-400">
<span class="relative text-lg"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m4 7 8 6 8-6"/></svg><span id="tabBadge" class="absolute -right-1 top-0 h-2 w-2 rounded-full bg-red-500"></span></span>
<span class="text-[10px] font-medium">Inbox</span>
<span class="dot h-1 w-1 rounded-full bg-transparent"></span>
</button>
<button data-tab="Profile" class="tab-item flex w-14 flex-col items-center gap-1 py-1 text-neutral-400">
<span class="text-lg"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></svg></span>
<span class="text-[10px] font-medium">Profile</span>
<span class="dot h-1 w-1 rounded-full bg-transparent"></span>
</button>
</div>
<div class="mx-auto mt-3 h-1 w-28 rounded-full bg-neutral-200"></div>
</nav>
</div>
</section> var tabs = Array.prototype.slice.call(document.querySelectorAll(".tab-item"));
var title = document.getElementById("tabTitle");
var badge = document.getElementById("tabBadge");
tabs.forEach(function (tab) {
tab.addEventListener("click", function () {
tabs.forEach(function (t) {
var on = t === tab;
t.classList.toggle("text-neutral-900", on);
t.classList.toggle("text-neutral-400", !on);
var dot = t.querySelector(".dot");
dot.classList.toggle("bg-neutral-900", on);
dot.classList.toggle("bg-transparent", !on);
});
title.textContent = tab.dataset.tab;
// Opening the inbox is what marks it read, not a timer.
if (tab.dataset.tab === "Inbox" && badge) badge.remove();
});
});