UI Components Medium
Sidebar Admin
Collapsible admin sidebar with icon-only mode, grouped navigation sections, active state, and mobile overlay. No dependencies.
Open in Lab
MCP
css vanilla-js
Targets: JS HTML
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--sidebar-w: 260px;
--sidebar-w-collapsed: 64px;
--bg: #0f1117;
--surface: #16181f;
--surface2: #1e2130;
--border: #2a2d3a;
--text: #e2e8f0;
--text-muted: #64748b;
--accent: #818cf8;
--accent-bg: rgba(129, 140, 248, 0.12);
--green: #34d399;
--red: #f87171;
--transition: 260ms cubic-bezier(0.4, 0, 0.2, 1);
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: var(--bg);
color: var(--text);
display: flex;
height: 100vh;
overflow: hidden;
}
/* ── Overlay (mobile) ── */
.overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.55);
z-index: 40;
}
.overlay.visible {
display: block;
}
/* ── Sidebar ── */
.sidebar {
width: var(--sidebar-w);
min-height: 100vh;
background: var(--surface);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
transition: width var(--transition);
overflow: hidden;
position: relative;
flex-shrink: 0;
z-index: 50;
}
.sidebar.collapsed {
width: var(--sidebar-w-collapsed);
}
/* Logo */
.s-logo {
display: flex;
align-items: center;
gap: 10px;
padding: 20px 16px 16px;
border-bottom: 1px solid var(--border);
overflow: hidden;
white-space: nowrap;
}
.s-logo-icon {
width: 36px;
height: 36px;
background: var(--accent-bg);
border-radius: 10px;
display: grid;
place-items: center;
flex-shrink: 0;
}
.s-logo-text {
font-size: 0.95rem;
font-weight: 700;
color: var(--text);
opacity: 1;
transition: opacity var(--transition);
}
.sidebar.collapsed .s-logo-text {
opacity: 0;
pointer-events: none;
}
/* Collapse button */
.s-collapse-btn {
position: absolute;
top: 20px;
right: 10px;
width: 24px;
height: 24px;
border: 1px solid var(--border);
border-radius: 50%;
background: var(--surface2);
color: var(--text-muted);
cursor: pointer;
display: grid;
place-items: center;
transition: background .2s, color .2s;
z-index: 2;
}
.s-collapse-btn:hover {
background: var(--accent-bg);
color: var(--accent);
}
.sidebar.collapsed .s-collapse-btn svg {
transform: rotate(180deg);
}
.s-collapse-btn svg {
transition: transform var(--transition);
}
/* Nav */
.s-nav {
flex: 1;
padding: 12px 10px;
display: flex;
flex-direction: column;
gap: 2px;
overflow: hidden;
}
.s-section-label {
font-size: 0.65rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .08em;
color: var(--text-muted);
padding: 8px 6px 4px;
white-space: nowrap;
overflow: hidden;
transition: opacity var(--transition), height var(--transition);
}
.sidebar.collapsed .s-section-label {
opacity: 0;
height: 0;
padding: 0;
}
.s-divider {
height: 1px;
background: var(--border);
margin: 8px 0;
}
.s-item {
display: flex;
align-items: center;
gap: 10px;
padding: 9px 10px;
border-radius: 8px;
color: var(--text-muted);
text-decoration: none;
font-size: 0.875rem;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
transition: background .15s, color .15s;
position: relative;
}
.s-item:hover {
background: var(--surface2);
color: var(--text);
}
.s-item.active {
background: var(--accent-bg);
color: var(--accent);
}
.s-item.active::before {
content: "";
position: absolute;
left: 0;
top: 4px;
bottom: 4px;
width: 3px;
background: var(--accent);
border-radius: 0 3px 3px 0;
}
.s-icon {
width: 18px;
height: 18px;
flex-shrink: 0;
display: grid;
place-items: center;
}
.s-label {
flex: 1;
opacity: 1;
transition: opacity var(--transition);
}
.sidebar.collapsed .s-label {
opacity: 0;
}
.s-badge {
font-size: 0.65rem;
font-weight: 700;
background: var(--accent);
color: #fff;
padding: 1px 6px;
border-radius: 999px;
line-height: 1.5;
transition: opacity var(--transition);
}
.s-badge--dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--green);
padding: 0;
}
.sidebar.collapsed .s-badge {
opacity: 0;
}
/* Tooltip in collapsed mode */
.sidebar.collapsed .s-item::after {
content: attr(data-tooltip);
position: absolute;
left: calc(100% + 12px);
top: 50%;
transform: translateY(-50%);
background: var(--surface2);
border: 1px solid var(--border);
color: var(--text);
font-size: 0.8rem;
padding: 4px 10px;
border-radius: 6px;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity .15s;
z-index: 100;
}
.sidebar.collapsed .s-item:hover::after {
opacity: 1;
}
/* User */
.s-user {
display: flex;
align-items: center;
gap: 10px;
padding: 14px 12px;
border-top: 1px solid var(--border);
overflow: hidden;
cursor: pointer;
white-space: nowrap;
transition: background .15s;
}
.s-user:hover {
background: var(--surface2);
}
.s-user-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: linear-gradient(135deg, var(--accent), #a5b4fc);
display: grid;
place-items: center;
font-size: 0.7rem;
font-weight: 700;
color: #fff;
flex-shrink: 0;
}
.s-user-info {
display: flex;
flex-direction: column;
opacity: 1;
transition: opacity var(--transition);
}
.sidebar.collapsed .s-user-info {
opacity: 0;
}
.s-user-name {
font-size: 0.82rem;
font-weight: 600;
color: var(--text);
}
.s-user-role {
font-size: 0.7rem;
color: var(--text-muted);
margin-top: 1px;
}
/* Tooltip for user */
.sidebar.collapsed .s-user::after {
content: attr(data-tooltip);
position: absolute;
left: calc(100% + 12px);
bottom: 12px;
background: var(--surface2);
border: 1px solid var(--border);
color: var(--text);
font-size: 0.8rem;
padding: 4px 10px;
border-radius: 6px;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity .15s;
z-index: 100;
}
.sidebar.collapsed .s-user:hover::after {
opacity: 1;
}
/* ── Main area ── */
.main {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Topbar */
.topbar {
height: 60px;
background: var(--surface);
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
padding: 0 20px;
gap: 12px;
}
.hamburger {
display: none;
flex-direction: column;
gap: 4px;
background: none;
border: none;
cursor: pointer;
padding: 4px;
}
.hamburger span {
display: block;
width: 18px;
height: 2px;
background: var(--text-muted);
border-radius: 2px;
transition: background .15s;
}
.hamburger:hover span {
background: var(--text);
}
.topbar-title {
font-size: 1rem;
font-weight: 600;
flex: 1;
}
.topbar-actions {
display: flex;
align-items: center;
gap: 8px;
}
.topbar-icon-btn {
width: 36px;
height: 36px;
border-radius: 8px;
border: 1px solid var(--border);
background: var(--surface2);
color: var(--text-muted);
cursor: pointer;
display: grid;
place-items: center;
position: relative;
transition: background .15s, color .15s;
}
.topbar-icon-btn:hover {
color: var(--text);
background: var(--border);
}
.topbar-badge {
position: absolute;
top: -4px;
right: -4px;
width: 16px;
height: 16px;
background: var(--red);
border-radius: 50%;
font-size: 0.6rem;
font-weight: 700;
color: #fff;
display: grid;
place-items: center;
}
.topbar-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: linear-gradient(135deg, var(--accent), #a5b4fc);
display: grid;
place-items: center;
font-size: 0.7rem;
font-weight: 700;
color: #fff;
cursor: pointer;
}
/* Content */
.content {
flex: 1;
overflow-y: auto;
padding: 28px 24px;
}
.content-title {
font-size: 1.4rem;
font-weight: 700;
margin-bottom: 4px;
}
.content-subtitle {
color: var(--text-muted);
margin-bottom: 28px;
font-size: 0.875rem;
}
.stat-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 16px;
}
.stat-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 20px;
transition: border-color .2s;
}
.stat-card:hover {
border-color: var(--accent);
}
.stat-label {
font-size: 0.78rem;
color: var(--text-muted);
margin-bottom: 8px;
font-weight: 500;
}
.stat-value {
font-size: 1.6rem;
font-weight: 700;
color: var(--text);
margin-bottom: 4px;
}
.stat-delta {
font-size: 0.78rem;
font-weight: 600;
}
.stat-delta.up {
color: var(--green);
}
.stat-delta.down {
color: var(--red);
}
/* ── Mobile ── */
@media (max-width: 768px) {
.sidebar {
position: fixed;
top: 0;
left: 0;
height: 100vh;
transform: translateX(-100%);
transition: transform var(--transition), width var(--transition);
width: var(--sidebar-w) !important;
}
.sidebar.mobile-open {
transform: translateX(0);
}
.s-collapse-btn {
display: none;
}
.hamburger {
display: flex;
}
}const sidebar = document.getElementById("sidebar");
const collapseBtn = document.getElementById("collapseBtn");
const hamburger = document.getElementById("hamburger");
const overlay = document.getElementById("overlay");
// Desktop collapse toggle
collapseBtn?.addEventListener("click", () => {
sidebar.classList.toggle("collapsed");
const isCollapsed = sidebar.classList.contains("collapsed");
collapseBtn.setAttribute("aria-expanded", String(!isCollapsed));
});
// Mobile open
hamburger?.addEventListener("click", () => {
sidebar.classList.add("mobile-open");
overlay.classList.add("visible");
});
// Mobile close
function closeMobile() {
sidebar.classList.remove("mobile-open");
overlay.classList.remove("visible");
}
overlay?.addEventListener("click", closeMobile);
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") closeMobile();
});
// Active nav item
document.querySelectorAll(".s-item").forEach((item) => {
item.addEventListener("click", (e) => {
e.preventDefault();
document.querySelectorAll(".s-item").forEach((i) => {
i.classList.remove("active");
i.removeAttribute("aria-current");
});
item.classList.add("active");
item.setAttribute("aria-current", "page");
// Update topbar title
const label = item.querySelector(".s-label")?.textContent?.trim();
const title = document.querySelector(".topbar-title");
if (label && title) title.textContent = label;
// Close mobile
closeMobile();
});
});
// Persist collapse state
const savedCollapsed = localStorage.getItem("sidebar-collapsed") === "true";
if (savedCollapsed) {
sidebar.classList.add("collapsed");
collapseBtn?.setAttribute("aria-expanded", "false");
}
collapseBtn?.addEventListener("click", () => {
localStorage.setItem("sidebar-collapsed", sidebar.classList.contains("collapsed").toString());
});<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sidebar Admin</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- Mobile overlay -->
<div class="overlay" id="overlay"></div>
<!-- ── Sidebar ── -->
<aside class="sidebar" id="sidebar">
<!-- Logo -->
<div class="s-logo">
<div class="s-logo-icon">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#818cf8" stroke-width="2.5">
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
</svg>
</div>
<span class="s-logo-text">StealthAdmin</span>
</div>
<!-- Collapse toggle -->
<button class="s-collapse-btn" id="collapseBtn" aria-label="Toggle sidebar" aria-expanded="true">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<path d="m15 18-6-6 6-6"/>
</svg>
</button>
<!-- Navigation -->
<nav class="s-nav" aria-label="Admin navigation">
<div class="s-section-label">Main</div>
<a href="#" class="s-item active" aria-current="page" data-tooltip="Dashboard">
<span class="s-icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/>
<rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>
</svg>
</span>
<span class="s-label">Dashboard</span>
</a>
<a href="#" class="s-item" data-tooltip="Analytics">
<span class="s-icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/>
<line x1="6" y1="20" x2="6" y2="14"/><line x1="2" y1="20" x2="22" y2="20"/>
</svg>
</span>
<span class="s-label">Analytics</span>
<span class="s-badge">12</span>
</a>
<a href="#" class="s-item" data-tooltip="Users">
<span class="s-icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
<circle cx="9" cy="7" r="4"/>
<path d="M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/>
</svg>
</span>
<span class="s-label">Users</span>
</a>
<a href="#" class="s-item" data-tooltip="Orders">
<span class="s-icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/>
<line x1="3" y1="6" x2="21" y2="6"/>
<path d="M16 10a4 4 0 0 1-8 0"/>
</svg>
</span>
<span class="s-label">Orders</span>
</a>
<a href="#" class="s-item" data-tooltip="Products">
<span class="s-icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
</svg>
</span>
<span class="s-label">Products</span>
</a>
<div class="s-divider"></div>
<div class="s-section-label">System</div>
<a href="#" class="s-item" data-tooltip="Messages">
<span class="s-icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
</svg>
</span>
<span class="s-label">Messages</span>
<span class="s-badge s-badge--dot"></span>
</a>
<a href="#" class="s-item" data-tooltip="Settings">
<span class="s-icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"/>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
</svg>
</span>
<span class="s-label">Settings</span>
</a>
</nav>
<!-- User profile -->
<div class="s-user" data-tooltip="Alex Chen">
<div class="s-user-avatar">AC</div>
<div class="s-user-info">
<span class="s-user-name">Alex Chen</span>
<span class="s-user-role">Admin</span>
</div>
</div>
</aside>
<!-- ── Main content ── -->
<div class="main" id="main">
<!-- Top bar -->
<header class="topbar">
<button class="hamburger" id="hamburger" aria-label="Open menu">
<span></span><span></span><span></span>
</button>
<h1 class="topbar-title">Dashboard</h1>
<div class="topbar-actions">
<button class="topbar-icon-btn" title="Search">
<svg width="18" height="18" 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>
</button>
<button class="topbar-icon-btn" title="Notifications">
<svg width="18" height="18" 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="topbar-badge">3</span>
</button>
<div class="topbar-avatar">AC</div>
</div>
</header>
<!-- Content -->
<main class="content">
<h2 class="content-title">Good morning, Alex 👋</h2>
<p class="content-subtitle">Here's what's happening today.</p>
<div class="stat-grid">
<div class="stat-card">
<div class="stat-label">Total Users</div>
<div class="stat-value">24,521</div>
<div class="stat-delta up">+12.5%</div>
</div>
<div class="stat-card">
<div class="stat-label">Revenue</div>
<div class="stat-value">$84,320</div>
<div class="stat-delta up">+8.2%</div>
</div>
<div class="stat-card">
<div class="stat-label">Orders</div>
<div class="stat-value">1,284</div>
<div class="stat-delta down">-3.1%</div>
</div>
<div class="stat-card">
<div class="stat-label">Uptime</div>
<div class="stat-value">99.98%</div>
<div class="stat-delta up">+0.01%</div>
</div>
</div>
</main>
</div>
<script src="script.js"></script>
</body>
</html>Sidebar Admin
A production-ready collapsible admin sidebar with icon-only compact mode, grouped navigation sections, tooltips in icon mode, mobile overlay, and smooth transitions.
Features
- Collapses to icon-only strip (260px → 64px) with CSS transitions
- Tooltip labels appear on hover in collapsed mode
- Grouped sections with dividers and labels
- Active item highlighting with left-accent indicator
- Mobile: hidden off-screen, hamburger button slides it in with overlay backdrop
- Bottom user profile block with avatar and role
How it works
- Toggle button sets
.collapsedclass on<aside>— CSS handles width and label visibility - In collapsed mode,
[data-tooltip]attribute + CSS::afterpseudo-element shows labels on hover - Mobile breakpoint (
≤768px) switches to overlay mode — sidebar usestransform: translateX - Overlay backdrop click closes the sidebar
Accessibility
<nav>landmark witharia-label- Collapse button has
aria-expandedandaria-label - Active item uses
aria-current="page"