UI Components Easy
Rudder Navigation
Bottom navigation with a raised central FAB button. Combines standard tab items with a prominent primary action.
Open in Lab
MCP
css vanilla-js
Targets: JS HTML
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--bg: #050910;
--nav-bg: #0d1117;
--accent: #3b82f6;
--text: #e4e4e7;
--text-muted: #71717a;
--muted: #475569;
}
body {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: var(--bg);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
color: var(--text);
padding: 2rem;
}
/* ── Demo wrapper ── */
.demo {
width: 100%;
max-width: 400px;
display: flex;
flex-direction: column;
align-items: center;
gap: 2rem;
}
.demo-title {
font-size: 1.5rem;
font-weight: 800;
text-align: center;
}
.demo-sub {
color: var(--muted);
font-size: 0.875rem;
text-align: center;
}
/* ── Phone frame ── */
.phone-frame {
width: 320px;
height: 520px;
background: #fff;
border-radius: 32px;
border: 1.5px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.04);
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
/* ── Content area ── */
.page-content {
flex: 1;
overflow: hidden;
position: relative;
background: #fff;
}
.content-screen {
position: absolute;
inset: 0;
padding: 32px 20px 20px;
display: none;
flex-direction: column;
gap: 12px;
opacity: 0;
transition: opacity 0.25s ease;
}
.content-screen.active {
display: flex;
opacity: 1;
}
.content-screen h2 {
font-size: 1.25rem;
font-weight: 700;
color: #111;
}
.content-screen > p {
font-size: 0.8rem;
color: #666;
line-height: 1.5;
}
/* ── Feed placeholders ── */
.feed-placeholder {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 4px;
}
.feed-card {
height: 64px;
background: #f3f4f6;
border-radius: 10px;
}
.feed-card.short {
height: 48px;
}
/* ── Search ── */
.search-box {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
background: #f3f4f6;
border-radius: 10px;
color: #999;
font-size: 13px;
}
.search-box svg {
width: 14px;
height: 14px;
flex-shrink: 0;
}
/* ── Create ── */
.create-options {
display: flex;
gap: 8px;
}
.create-option {
flex: 1;
padding: 20px 8px;
background: #f3f4f6;
border-radius: 10px;
text-align: center;
font-size: 12px;
font-weight: 600;
color: #555;
}
/* ── Likes ── */
.likes-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.likes-item {
padding: 10px 12px;
background: #eff6ff;
border-radius: 8px;
font-size: 12px;
color: #333;
}
/* ── Profile ── */
.profile-avatar {
width: 56px;
height: 56px;
border-radius: 50%;
background: linear-gradient(135deg, #3b82f6, #60a5fa);
margin-top: 4px;
}
.profile-name {
font-size: 1rem;
font-weight: 600;
color: #111;
}
/* ── Bottom Nav ── */
.bottom-nav {
display: flex;
align-items: flex-end;
height: 64px;
background: var(--nav-bg);
padding: 0 6px;
flex-shrink: 0;
}
.nav-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 3px;
border: none;
background: none;
cursor: pointer;
padding: 8px 4px 10px;
color: var(--text-muted);
transition: color 0.2s;
-webkit-tap-highlight-color: transparent;
}
.nav-item:active {
transform: scale(0.92);
}
.nav-item.active {
color: var(--accent);
}
.nav-icon {
width: 20px;
height: 20px;
transition: transform 0.2s;
}
.nav-item.active .nav-icon {
transform: scale(1.1);
}
.nav-label {
font-size: 9px;
font-weight: 500;
letter-spacing: 0.02em;
}
/* ── FAB — raised center button ── */
.nav-item--fab {
align-self: flex-end;
padding: 0 4px 10px;
}
.fab-btn {
width: 50px;
height: 50px;
background: var(--accent);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
box-shadow: 0 6px 20px rgba(59, 130, 246, 0.5);
margin-bottom: 6px;
margin-top: -28px;
transition: transform 0.2s, box-shadow 0.2s;
}
.nav-item--fab:active .fab-btn {
transform: scale(0.9);
}
.nav-item--fab.active .fab-btn {
transform: rotate(45deg);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
}
.fab-icon {
width: 22px;
height: 22px;
}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" />
<title>Rudder Navigation</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="demo">
<h1 class="demo-title">Rudder Navigation</h1>
<p class="demo-sub">Bottom nav with raised central FAB button.</p>
<div class="phone-frame">
<div class="page-content">
<div class="content-screen active" data-screen="home">
<h2>Home</h2>
<p>Welcome back. Here's what's new today.</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 need.</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="likes">
<h2>Likes</h2>
<p>People are loving your content.</p>
<div class="likes-list"><div class="likes-item">12 likes on your latest post</div><div class="likes-item">8 likes on your story</div></div>
</div>
<div class="content-screen" data-screen="profile">
<h2>Profile</h2>
<p>Manage your account.</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--fab" data-target="create" aria-label="Create">
<span class="fab-btn"><svg class="fab-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>
</button>
<button class="nav-item" data-target="likes" aria-label="Likes">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>
<span class="nav-label">Likes</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>
</div>
<script src="script.js"></script>
</body>
</html>Rudder Navigation
A bottom navigation bar featuring a raised central FAB (floating action button) that sits above the bar. Regular nav items flank the FAB on both sides.
Features
- Bottom nav bar with 4 standard items
- Raised central FAB with notch cutout effect
- Active state with color change
- Smooth transitions
- Keyboard accessible
How it works
- Bottom bar uses flexbox with 5 items (2 left, FAB center, 2 right)
- Center FAB is elevated with negative margin and circular shape
- Active tab tracked via class toggle
- FAB has special scale animation on tap