UI Components Easy
Mobile Bottom Navigation
A fixed bottom navigation bar for mobile apps with icon + label tabs, active state, notification badges, and a center create button. Zero dependencies.
Open in Lab
MCP
vanilla-js css
Targets: JS HTML
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: #f0f2f5;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
.phone-frame {
width: 375px;
height: 667px;
background: #fff;
border-radius: 40px;
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.18);
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
/* Content area */
.page-content {
flex: 1;
overflow: hidden;
position: relative;
}
.content-screen {
position: absolute;
inset: 0;
padding: 40px 24px 24px;
display: none;
flex-direction: column;
gap: 16px;
opacity: 0;
transition: opacity 0.25s ease;
}
.content-screen.active {
display: flex;
opacity: 1;
}
.content-screen h2 {
font-size: 26px;
font-weight: 700;
color: #111;
}
.content-screen > p {
font-size: 14px;
color: #666;
line-height: 1.6;
}
/* Feed placeholders */
.feed-placeholder {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 8px;
}
.feed-card {
height: 80px;
background: #f3f4f6;
border-radius: 12px;
}
.feed-card.short {
height: 56px;
}
/* Search screen */
.search-box {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 16px;
background: #f3f4f6;
border-radius: 12px;
color: #999;
font-size: 14px;
}
.search-box svg {
width: 16px;
height: 16px;
flex-shrink: 0;
}
/* Create screen */
.create-options {
display: flex;
gap: 10px;
}
.create-option {
flex: 1;
padding: 24px 8px;
background: #f3f4f6;
border-radius: 12px;
text-align: center;
font-size: 13px;
font-weight: 600;
color: #555;
}
/* Notifications */
.notif-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.notif-item {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 14px;
background: #eef2ff;
border-radius: 10px;
font-size: 13px;
color: #333;
}
.notif-item.read {
background: #f9fafb;
color: #666;
}
.notif-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #6366f1;
flex-shrink: 0;
}
.notif-item.read .notif-dot {
background: #d1d5db;
}
/* Profile */
.profile-avatar {
width: 64px;
height: 64px;
border-radius: 50%;
background: linear-gradient(135deg, #6366f1, #8b5cf6);
margin-top: 8px;
}
.profile-name {
font-size: 18px;
font-weight: 600;
color: #111;
}
/* Bottom Nav */
.bottom-nav {
display: flex;
align-items: center;
height: 72px;
background: #fff;
border-top: 1px solid #e8e8e8;
padding: 0 8px;
position: relative;
flex-shrink: 0;
}
.nav-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
border: none;
background: none;
cursor: pointer;
padding: 8px 4px;
border-radius: 12px;
color: #b0b0b8;
transition: color 0.2s ease;
position: relative;
}
.nav-item:active {
transform: scale(0.92);
}
.nav-item.active {
color: #6366f1;
}
.nav-icon {
width: 22px;
height: 22px;
transition: transform 0.2s ease;
}
.nav-item.active .nav-icon {
transform: scale(1.1);
}
.nav-label {
font-size: 10px;
font-weight: 500;
letter-spacing: 0.02em;
}
/* Create button */
.nav-item--create {
color: #6366f1;
}
.create-btn {
width: 44px;
height: 44px;
background: #6366f1;
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
box-shadow: 0 4px 14px rgba(99, 102, 241, 0.4);
margin-top: -12px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.nav-item--create.active .create-btn {
transform: scale(0.9) rotate(45deg);
box-shadow: 0 2px 6px rgba(99, 102, 241, 0.3);
}
.nav-item--create .nav-icon {
width: 18px;
height: 18px;
}
/* Badge */
.icon-wrap {
position: relative;
display: inline-flex;
}
.badge {
position: absolute;
top: -6px;
right: -8px;
background: #ef4444;
color: #fff;
font-size: 9px;
font-weight: 700;
min-width: 16px;
height: 16px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
padding: 0 3px;
border: 2px solid #fff;
}const navItems = document.querySelectorAll(".nav-item");
const screens = document.querySelectorAll(".content-screen");
navItems.forEach((item) => {
item.addEventListener("click", () => {
const target = item.dataset.target;
navItems.forEach((n) => n.classList.remove("active"));
item.classList.add("active");
screens.forEach((s) => {
if (s.dataset.screen === target) {
s.classList.add("active");
} else {
s.classList.remove("active");
}
});
});
});<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<title>Mobile Bottom Navigation</title>
</head>
<body>
<div class="phone-frame">
<div class="page-content">
<div class="content-screen active" data-screen="home">
<h2>Home</h2>
<p>Welcome to the home screen. Tap the navigation tabs below to switch pages.</p>
<div class="feed-placeholder">
<div class="feed-card"></div>
<div class="feed-card"></div>
<div class="feed-card short"></div>
</div>
</div>
<div class="content-screen" data-screen="search">
<h2>Search</h2>
<p>Find anything you're looking for.</p>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<span>Search…</span>
</div>
</div>
<div class="content-screen" data-screen="create">
<h2>Create</h2>
<p>Start something new.</p>
<div class="create-options">
<div class="create-option">Post</div>
<div class="create-option">Story</div>
<div class="create-option">Reel</div>
</div>
</div>
<div class="content-screen" data-screen="notifications">
<h2>Notifications</h2>
<p>You have 3 unread alerts.</p>
<div class="notif-list">
<div class="notif-item"><div class="notif-dot"></div><span>Alex Morgan liked your post</span></div>
<div class="notif-item"><div class="notif-dot"></div><span>Sam Rivera started following you</span></div>
<div class="notif-item"><div class="notif-dot"></div><span>Jordan Lee commented on your story</span></div>
<div class="notif-item read"><div class="notif-dot"></div><span>Taylor Kim replied to your message</span></div>
</div>
</div>
<div class="content-screen" data-screen="profile">
<h2>Profile</h2>
<p>Manage your account and settings.</p>
<div class="profile-avatar"></div>
<div class="profile-name">Your Name</div>
</div>
</div>
<nav class="bottom-nav" role="navigation" aria-label="Main navigation">
<button class="nav-item active" data-target="home" aria-label="Home">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
<polyline points="9 22 9 12 15 12 15 22"/>
</svg>
<span class="nav-label">Home</span>
</button>
<button class="nav-item" data-target="search" aria-label="Search">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/>
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
<span class="nav-label">Search</span>
</button>
<button class="nav-item nav-item--create" data-target="create" aria-label="Create">
<span class="create-btn">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<line x1="12" y1="5" x2="12" y2="19"/>
<line x1="5" y1="12" x2="19" y2="12"/>
</svg>
</span>
<span class="nav-label">Create</span>
</button>
<button class="nav-item" data-target="notifications" aria-label="Notifications">
<span class="icon-wrap">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
<path d="M13.73 21a2 2 0 0 1-3.46 0"/>
</svg>
<span class="badge">3</span>
</span>
<span class="nav-label">Alerts</span>
</button>
<button class="nav-item" data-target="profile" aria-label="Profile">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
<circle cx="12" cy="7" r="4"/>
</svg>
<span class="nav-label">Profile</span>
</button>
</nav>
</div>
<script src="script.js"></script>
</body>
</html>Mobile Bottom Navigation
A fixed bottom navigation bar styled for mobile-first layouts with 5 tabs, each featuring an SVG icon and label. Includes a raised center action button and a notification badge.
How it works
- Each tab is a
<button>with an icon + label - JavaScript adds
.activeon click and swaps the visible content screen - The center create button uses a raised pill style and rotates to an × on activation
- A
badgespan shows notification counts (easily hideable)
Customization
- Swap inline SVGs for Lucide, Heroicons, or any icon set
- Change
--accentCSS variable to retheme the active color - Add
env(safe-area-inset-bottom)padding for iPhone notch support
When to use it
- Mobile web apps and PWAs replacing a desktop sidebar
- Onboarding flows with a persistent tab structure