More Navigation Snippets
Collapsible Sidebar Nav — Free HTML CSS Dashboard Snippet
Collapsible Sidebar Nav · Navigation · Plain HTML, CSS & JS · Live preview
What's included
Features
width + min-width transition at cubic-bezier(.4,0,.2,1) collapses the sidebar from 220px to 60px without layout reflow or icon jumping.::after pseudo-elements with content: attr(data-tip) render label tooltips without any JavaScript — one attribute per nav item is all that is needed.transform: rotate(180deg) on .collapsed .collapse-btn svg — a pure CSS direction indicator..nav-label divides items into labelled sections ("Main", "Management"). Labels fade out in collapsed state to avoid cluttering the icon-only view..active class applies an indigo accent colour matching the logo and avatar gradient — visual coherence across the navigation chrome.classList.toggle call — three lines of JS total. All animation, tooltip, and label logic lives in CSS.About this UI Snippet
Collapsible Sidebar Nav — CSS Width Transition, Icon Tooltip System & Active State Design

The collapsible sidebar is the defining pattern of dashboard UI design — a persistent navigation panel that maximises screen space when collapsed to icon-only view while remaining fully navigable. This snippet builds a production-quality sidebar in pure HTML and CSS, with a single JavaScript toggle: smooth width animation, labelled nav items with icon fallback, badge counters, notification dots, a user profile footer, and CSS-only tooltips in the collapsed state.
Dashboards are where developers most often need a sidebar, and getting it right requires solving several simultaneous problems: the panel must animate smoothly without layout reflow, labels must hide without leaving gaps, icons must remain accessible in collapsed state (with tooltips), and badges must not clutter the icon view.
The width collapse animation
The sidebar collapses by toggling a .collapsed class that changes width and min-width from 220px to 60px. Both properties are animated with transition: width .28s cubic-bezier(.4,0,.2,1) — the Material Design standard easing for panel motions. Using min-width alongside width prevents the flex container from shrinking below the target during animation. The content inside (logo-text, nav-text, badges, labels) fades out with opacity: 0 and width: 0, which collapses their space without affecting the icon alignment.
CSS-only tooltips in collapsed state
Each .nav-item carries a data-tip attribute with its label text. The collapsed state uses an ::after pseudo-element with content: attr(data-tip) to render this as a floating tooltip. The tooltip is positioned left: calc(100% + 10px) — to the right of the icon — with a dark background and border to match the sidebar theme. pointer-events: none prevents it interfering with clicks. Visibility is controlled with opacity: 0 + opacity: 1 on hover — no JavaScript needed for the tooltip system.
Badge and notification dot system
Three badge styles are demonstrated: a purple numeric badge (3), a green "New" label badge, and a yellow notification dot. In collapsed state, all badges have opacity: 0 — they would crowd the icon-only view. The icons alone communicate location; users expand the sidebar to check counts. This pattern matches VS Code, Linear, and Notion's sidebar behaviour.
Active state with accent colour
The active nav item uses background: #334155 and color: #818cf8 (indigo accent). This accent colour matches the logo icon and the user avatar gradient, creating visual coherence across the sidebar. The active state is set in HTML via the .active class — in a real app, your router would apply this dynamically.
User profile footer
The sidebar footer contains an avatar (initials-based <div> with gradient background), username, and role label. In collapsed state, the details fade out and the avatar tooltip shows the full name. This footer is a standard dashboard pattern — it anchors the user's identity in the navigation chrome and provides a consistent entry point for profile/account settings. Pair with a command palette for keyboard navigation between sections.
Step by step
How to Use
- 1Paste the three code blocksDrop the HTML, CSS, and JS into your page. A full-height layout appears with a 220px sidebar on the left and a main content area on the right.
- 2Click the collapse arrowThe sidebar smoothly animates to 60px — labels, badges, and the logo text fade out. Only icons remain, bottom-aligned.
- 3Hover over icons in collapsed stateA CSS tooltip appears to the right of each icon showing its label. The user avatar tooltip shows the full name. No JavaScript required.
- 4Click again to expandThe collapse arrow rotates 180° and the sidebar expands back to 220px. Labels, badges, and the user details fade back in.
- 5Set an active itemAdd the
.activeclass to any.nav-itemto highlight it with the accent colour. In a real app, apply this based on the current route. - 6Add your nav itemsCopy any
.nav-itemblock, swap the SVG icon, update thedata-tipattribute, label text, and optionally add a badge or dot. Sections are grouped by.nav-section.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Save the state to localStorage on toggle: localStorage.setItem('sidebar_collapsed', isCollapsed). On page load, read it: if (localStorage.getItem('sidebar_collapsed') === 'true') sidebar.classList.add('collapsed').
On mobile breakpoints (max-width: 768px), position the sidebar with position: fixed; left: -220px by default, and left: 0 when open. Add an overlay backdrop behind it. The toggle button would move to the topbar. See the drawer snippet for the full pattern.
Add a <div class="sub-menu"> inside a nav item. Toggle a .open class on click. Use max-height: 0 → max-height: 200px with a CSS transition for a smooth expand. The parent item needs a chevron icon that rotates on .open.
Replace the toggle JS with const [collapsed, setCollapsed] = useState(false). Apply className={sidebar${collapsed ? ' collapsed' : ''}} to the aside. The collapse button's onClick calls setCollapsed(c => !c). Active state comes from comparing the current route to each item's href using Next.js usePathname() or React Router useLocation().
Collapsible Sidebar Nav — Export as HTML, React, Vue, Angular & Tailwind
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Collapsible Sidebar Nav</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,sans-serif;background:#0f172a;color:#e2e8f0;height:100vh;overflow:hidden}
.layout{display:flex;height:100vh}
/* Sidebar */
.sidebar{
width:220px;min-width:220px;
background:#1e293b;
border-right:1px solid #334155;
display:flex;flex-direction:column;
transition:width .28s cubic-bezier(.4,0,.2,1),min-width .28s cubic-bezier(.4,0,.2,1);
overflow:hidden;
}
.sidebar.collapsed{width:60px;min-width:60px;}
.sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:16px 14px;border-bottom:1px solid #334155;flex-shrink:0}
.logo{display:flex;align-items:center;gap:10px;color:#818cf8}
.logo-text{font-size:15px;font-weight:800;color:#f1f5f9;white-space:nowrap;overflow:hidden;transition:opacity .2s}
.collapsed .logo-text{opacity:0;width:0}
.collapse-btn{width:30px;height:30px;border-radius:8px;background:rgba(129,140,248,.1);border:1px solid rgba(129,140,248,.28);color:#818cf8;display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0;transition:background .15s,border-color .15s,color .15s}
.collapse-btn:hover{background:rgba(129,140,248,.22);border-color:rgba(129,140,248,.55);color:#a5b4fc}
.collapse-btn svg{transition:transform .28s cubic-bezier(.4,0,.2,1)}
.collapsed .collapse-btn svg{transform:rotate(180deg)}
.collapsed .sidebar-header{justify-content:center}
.collapsed .logo{display:none}
.nav-section{padding:12px 8px 0;flex-shrink:0}
.nav-label{font-size:9.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#475569;padding:0 8px 6px;white-space:nowrap;overflow:hidden;transition:opacity .15s}
.collapsed .nav-label{opacity:0}
.collapsed .nav-section{padding-left:0;padding-right:0}
.collapsed .nav-item{justify-content:center;padding:9px 0;border-radius:0;gap:0}
.collapsed .nav-text,.collapsed .nav-badge,.collapsed .nav-dot{display:none}
.collapsed .sidebar-footer{padding-left:0;padding-right:0}
.collapsed .user-info{justify-content:center;gap:0}
.collapsed .user-details{display:none}
.nav-item{display:flex;align-items:center;gap:10px;padding:9px 8px;border-radius:8px;text-decoration:none;color:#94a3b8;font-size:13px;font-weight:500;transition:background .15s,color .15s;position:relative;white-space:nowrap;cursor:pointer;margin-bottom:2px}
.nav-item:hover,.nav-item.active{background:#334155;color:#f1f5f9}
.nav-item.active{color:#818cf8}
.nav-icon{width:18px;height:18px;flex-shrink:0}
.nav-text{overflow:hidden;transition:opacity .15s,width .28s}
.collapsed .nav-text{opacity:0;width:0;overflow:hidden}
.nav-badge{font-size:9px;font-weight:700;padding:1px 6px;border-radius:20px;background:#818cf8;color:#fff;margin-left:auto;flex-shrink:0;transition:opacity .15s}
.nav-badge.new{background:#10b981}
.collapsed .nav-badge{opacity:0}
.nav-dot{width:6px;height:6px;border-radius:50%;background:#f59e0b;margin-left:auto;flex-shrink:0;transition:opacity .15s}
.collapsed .nav-dot{opacity:0}
/* Tooltip for collapsed state */
.collapsed .nav-item::after,.collapsed .user-info::after{content:attr(data-tip);position:absolute;left:calc(100% + 10px);top:50%;transform:translateY(-50%);background:#1e293b;border:1px solid #334155;color:#f1f5f9;font-size:11px;font-weight:500;padding:4px 10px;border-radius:6px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s;z-index:100;box-shadow:0 4px 12px rgba(0,0,0,0.3)}
.collapsed .nav-item:hover::after,.collapsed .user-info:hover::after{opacity:1}
/* Footer */
.sidebar-footer{margin-top:auto;padding:12px 8px;border-top:1px solid #334155}
.user-info{display:flex;align-items:center;gap:10px;padding:8px;border-radius:8px;cursor:pointer;transition:background .15s;position:relative}
.user-info:hover{background:#334155}
.avatar{width:30px;height:30px;border-radius:8px;background:linear-gradient(135deg,#6366f1,#8b5cf6);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;color:#fff;flex-shrink:0}
.user-details{overflow:hidden;transition:opacity .15s}
.collapsed .user-details{opacity:0;width:0}
.user-name{display:block;font-size:12px;font-weight:600;color:#f1f5f9;white-space:nowrap}
.user-role{display:block;font-size:10px;color:#64748b}
/* Main content */
.main-content{flex:1;overflow:auto;background:#0f172a;display:flex;flex-direction:column;min-width:0}
.topbar{display:flex;align-items:center;justify-content:space-between;padding:18px 24px;border-bottom:1px solid #1e293b}
.page-title{font-size:18px;font-weight:700;color:#f1f5f9}
.topbar-right{display:flex;align-items:center;gap:8px}
.status-dot{width:7px;height:7px;border-radius:50%;background:#10b981}
.status-text{font-size:12px;color:#64748b}
.stats-row{display:flex;gap:16px;padding:20px 24px}
.stat-card{flex:1;background:#1e293b;border:1px solid #334155;border-radius:12px;padding:16px;display:flex;flex-direction:column;gap:4px}
.stat-num{font-size:22px;font-weight:800;color:#f1f5f9}
.stat-label{font-size:11px;color:#64748b}
.placeholder-text{padding:0 24px;font-size:13px;color:#475569;line-height:1.7}
</style>
</head>
<body>
<div class="layout">
<aside class="sidebar" id="sidebar" aria-label="Main navigation">
<div class="sidebar-header">
<div class="logo">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 22 22 7 12 2"/></svg>
<span class="logo-text">Nexus</span>
</div>
<button class="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" stroke-linecap="round"><polyline points="15 18 9 12 15 6"/></svg>
</button>
</div>
<nav class="nav-section">
<p class="nav-label">Main</p>
<a class="nav-item active" href="#" data-tip="Dashboard">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
<span class="nav-text">Dashboard</span>
<span class="nav-badge">3</span>
</a>
<a class="nav-item" href="#" data-tip="Analytics">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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"/></svg>
<span class="nav-text">Analytics</span>
</a>
<a class="nav-item" href="#" data-tip="Projects">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M3 7l9-4 9 4v13l-9 4-9-4z"/><polyline points="3 7 12 11 21 7"/><line x1="12" y1="11" x2="12" y2="22"/></svg>
<span class="nav-text">Projects</span>
<span class="nav-badge new">New</span>
</a>
<a class="nav-item" href="#" data-tip="Messages">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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 class="nav-text">Messages</span>
<span class="nav-dot"></span>
</a>
</nav>
<nav class="nav-section">
<p class="nav-label">Management</p>
<a class="nav-item" href="#" data-tip="Team">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
<span class="nav-text">Team</span>
</a>
<a class="nav-item" href="#" data-tip="Files">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>
<span class="nav-text">Files</span>
</a>
<a class="nav-item" href="#" data-tip="Settings">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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 class="nav-text">Settings</span>
</a>
</nav>
<div class="sidebar-footer">
<div class="user-info" data-tip="Puneet Sharma">
<div class="avatar">PS</div>
<div class="user-details">
<span class="user-name">Puneet Sharma</span>
<span class="user-role">Admin</span>
</div>
</div>
</div>
</aside>
<main class="main-content">
<div class="topbar">
<h1 class="page-title">Dashboard</h1>
<div class="topbar-right">
<span class="status-dot"></span>
<span class="status-text">All systems normal</span>
</div>
</div>
<div class="stats-row">
<div class="stat-card"><span class="stat-num">2,847</span><span class="stat-label">Active Users</span></div>
<div class="stat-card"><span class="stat-num">$18.2k</span><span class="stat-label">Revenue</span></div>
<div class="stat-card"><span class="stat-num">94.3%</span><span class="stat-label">Uptime</span></div>
</div>
<p class="placeholder-text">Click the arrow to collapse the sidebar — icons stay visible with tooltips. Click again to expand.</p>
</main>
</div>
<script>
const sidebar = document.getElementById('sidebar');
const collapseBtn = document.getElementById('collapseBtn');
collapseBtn.addEventListener('click', () => {
const isCollapsed = sidebar.classList.toggle('collapsed');
collapseBtn.setAttribute('aria-expanded', String(!isCollapsed));
});
</script>
</body>
</html><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Collapsible Sidebar Nav</title>
<!-- Tailwind CSS v3+ — arbitrary value classes (e.g. bg-[#0f172a]) are valid -->
<script src="https://cdn.tailwindcss.com"></script>
<style>
* {
box-sizing:border-box;margin:0;padding:0
}
body {
font-family:system-ui,sans-serif;background:#0f172a;color:#e2e8f0;height:100vh;overflow:hidden
}
.sidebar {
width:220px;min-width:220px;
background:#1e293b;
border-right:1px solid #334155;
display:flex;flex-direction:column;
transition:width .28s cubic-bezier(.4,0,.2,1),min-width .28s cubic-bezier(.4,0,.2,1);
overflow:hidden;
}
.sidebar.collapsed {
width:60px;min-width:60px;
}
.collapsed .logo-text {
opacity:0;width:0
}
.collapse-btn svg {
transition:transform .28s cubic-bezier(.4,0,.2,1)
}
.collapsed .collapse-btn svg {
transform:rotate(180deg)
}
.collapsed .sidebar-header {
justify-content:center
}
.collapsed .logo {
display:none
}
.collapsed .nav-label {
opacity:0
}
.collapsed .nav-section {
padding-left:0;padding-right:0
}
.collapsed .nav-item {
justify-content:center;padding:9px 0;border-radius:0;gap:0
}
.collapsed .nav-text,.collapsed .nav-badge,.collapsed .nav-dot {
display:none
}
.collapsed .sidebar-footer {
padding-left:0;padding-right:0
}
.collapsed .user-info {
justify-content:center;gap:0
}
.collapsed .user-details {
display:none
}
.nav-item:hover,.nav-item.active {
background:#334155;color:#f1f5f9
}
.nav-item.active {
color:#818cf8
}
.collapsed .nav-text {
opacity:0;width:0;overflow:hidden
}
.nav-badge.new {
background:#10b981
}
.collapsed .nav-badge {
opacity:0
}
.collapsed .nav-dot {
opacity:0
}
.collapsed .nav-item::after,.collapsed .user-info::after {
content:attr(data-tip);position:absolute;left:calc(100% + 10px);top:50%;transform:translateY(-50%);background:#1e293b;border:1px solid #334155;color:#f1f5f9;font-size:11px;font-weight:500;padding:4px 10px;border-radius:6px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s;z-index:100;box-shadow:0 4px 12px rgba(0,0,0,0.3)
}
.collapsed .nav-item:hover::after,.collapsed .user-info:hover::after {
opacity:1
}
.collapsed .user-details {
opacity:0;width:0
}
</style>
</head>
<body>
<div class="flex h-screen">
<aside class="sidebar" id="sidebar" aria-label="Main navigation">
<div class="sidebar-header flex items-center justify-between py-4 px-3.5 border-b border-b-[#334155] shrink-0">
<div class="logo flex items-center gap-2.5 text-[#818cf8]">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 22 22 7 12 2"/></svg>
<span class="logo-text text-[15px] font-extrabold text-[#f1f5f9] whitespace-nowrap overflow-hidden [transition:opacity_.2s]">Nexus</span>
</div>
<button class="collapse-btn w-[30px] h-[30px] rounded-lg bg-[rgba(129,140,248,.1)] border border-[rgba(129,140,248,.28)] text-[#818cf8] flex items-center justify-center cursor-pointer shrink-0 [transition:background_.15s,border-color_.15s,color_.15s] hover:bg-[rgba(129,140,248,.22)] hover:border-[rgba(129,140,248,.55)] hover:text-[#a5b4fc]" 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" stroke-linecap="round"><polyline points="15 18 9 12 15 6"/></svg>
</button>
</div>
<nav class="nav-section pt-3 px-2 pb-0 shrink-0">
<p class="nav-label text-[9.5px] font-bold tracking-[.08em] uppercase text-[#475569] pt-0 px-2 pb-1.5 whitespace-nowrap overflow-hidden [transition:opacity_.15s]">Main</p>
<a class="nav-item flex items-center gap-2.5 py-[9px] px-2 rounded-lg no-underline text-[#94a3b8] text-[13px] font-medium [transition:background_.15s,color_.15s] relative whitespace-nowrap cursor-pointer mb-0.5 active" href="#" data-tip="Dashboard">
<svg class="w-[18px] h-[18px] shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
<span class="nav-text overflow-hidden [transition:opacity_.15s,width_.28s]">Dashboard</span>
<span class="nav-badge text-[9px] font-bold py-px px-1.5 rounded-[20px] bg-[#818cf8] text-[#fff] ml-auto shrink-0 [transition:opacity_.15s]">3</span>
</a>
<a class="nav-item flex items-center gap-2.5 py-[9px] px-2 rounded-lg no-underline text-[#94a3b8] text-[13px] font-medium [transition:background_.15s,color_.15s] relative whitespace-nowrap cursor-pointer mb-0.5" href="#" data-tip="Analytics">
<svg class="w-[18px] h-[18px] shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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"/></svg>
<span class="nav-text overflow-hidden [transition:opacity_.15s,width_.28s]">Analytics</span>
</a>
<a class="nav-item flex items-center gap-2.5 py-[9px] px-2 rounded-lg no-underline text-[#94a3b8] text-[13px] font-medium [transition:background_.15s,color_.15s] relative whitespace-nowrap cursor-pointer mb-0.5" href="#" data-tip="Projects">
<svg class="w-[18px] h-[18px] shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M3 7l9-4 9 4v13l-9 4-9-4z"/><polyline points="3 7 12 11 21 7"/><line x1="12" y1="11" x2="12" y2="22"/></svg>
<span class="nav-text overflow-hidden [transition:opacity_.15s,width_.28s]">Projects</span>
<span class="nav-badge text-[9px] font-bold py-px px-1.5 rounded-[20px] bg-[#818cf8] text-[#fff] ml-auto shrink-0 [transition:opacity_.15s] new">New</span>
</a>
<a class="nav-item flex items-center gap-2.5 py-[9px] px-2 rounded-lg no-underline text-[#94a3b8] text-[13px] font-medium [transition:background_.15s,color_.15s] relative whitespace-nowrap cursor-pointer mb-0.5" href="#" data-tip="Messages">
<svg class="w-[18px] h-[18px] shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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 class="nav-text overflow-hidden [transition:opacity_.15s,width_.28s]">Messages</span>
<span class="nav-dot w-1.5 h-1.5 rounded-full bg-[#f59e0b] ml-auto shrink-0 [transition:opacity_.15s]"></span>
</a>
</nav>
<nav class="nav-section pt-3 px-2 pb-0 shrink-0">
<p class="nav-label text-[9.5px] font-bold tracking-[.08em] uppercase text-[#475569] pt-0 px-2 pb-1.5 whitespace-nowrap overflow-hidden [transition:opacity_.15s]">Management</p>
<a class="nav-item flex items-center gap-2.5 py-[9px] px-2 rounded-lg no-underline text-[#94a3b8] text-[13px] font-medium [transition:background_.15s,color_.15s] relative whitespace-nowrap cursor-pointer mb-0.5" href="#" data-tip="Team">
<svg class="w-[18px] h-[18px] shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
<span class="nav-text overflow-hidden [transition:opacity_.15s,width_.28s]">Team</span>
</a>
<a class="nav-item flex items-center gap-2.5 py-[9px] px-2 rounded-lg no-underline text-[#94a3b8] text-[13px] font-medium [transition:background_.15s,color_.15s] relative whitespace-nowrap cursor-pointer mb-0.5" href="#" data-tip="Files">
<svg class="w-[18px] h-[18px] shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>
<span class="nav-text overflow-hidden [transition:opacity_.15s,width_.28s]">Files</span>
</a>
<a class="nav-item flex items-center gap-2.5 py-[9px] px-2 rounded-lg no-underline text-[#94a3b8] text-[13px] font-medium [transition:background_.15s,color_.15s] relative whitespace-nowrap cursor-pointer mb-0.5" href="#" data-tip="Settings">
<svg class="w-[18px] h-[18px] shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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 class="nav-text overflow-hidden [transition:opacity_.15s,width_.28s]">Settings</span>
</a>
</nav>
<div class="sidebar-footer mt-auto py-3 px-2 border-t border-t-[#334155]">
<div class="user-info flex items-center gap-2.5 p-2 rounded-lg cursor-pointer [transition:background_.15s] relative hover:bg-[#334155]" data-tip="Puneet Sharma">
<div class="w-[30px] h-[30px] rounded-lg [background:linear-gradient(135deg,#6366f1,#8b5cf6)] flex items-center justify-center text-xs font-bold text-[#fff] shrink-0">PS</div>
<div class="user-details overflow-hidden [transition:opacity_.15s]">
<span class="block text-xs font-semibold text-[#f1f5f9] whitespace-nowrap">Puneet Sharma</span>
<span class="block text-xs text-[#64748b]">Admin</span>
</div>
</div>
</div>
</aside>
<main class="flex-1 overflow-auto bg-[#0f172a] flex flex-col min-w-0">
<div class="flex items-center justify-between py-[18px] px-6 border-b border-b-[#1e293b]">
<h1 class="text-lg font-bold text-[#f1f5f9]">Dashboard</h1>
<div class="flex items-center gap-2">
<span class="w-[7px] h-[7px] rounded-full bg-[#10b981]"></span>
<span class="text-xs text-[#64748b]">All systems normal</span>
</div>
</div>
<div class="flex gap-4 py-5 px-6">
<div class="flex-1 bg-[#1e293b] border border-[#334155] rounded-xl p-4 flex flex-col gap-1"><span class="text-[22px] font-extrabold text-[#f1f5f9]">2,847</span><span class="text-[11px] text-[#64748b]">Active Users</span></div>
<div class="flex-1 bg-[#1e293b] border border-[#334155] rounded-xl p-4 flex flex-col gap-1"><span class="text-[22px] font-extrabold text-[#f1f5f9]">$18.2k</span><span class="text-[11px] text-[#64748b]">Revenue</span></div>
<div class="flex-1 bg-[#1e293b] border border-[#334155] rounded-xl p-4 flex flex-col gap-1"><span class="text-[22px] font-extrabold text-[#f1f5f9]">94.3%</span><span class="text-[11px] text-[#64748b]">Uptime</span></div>
</div>
<p class="py-0 px-6 text-[13px] text-[#475569] leading-[1.7]">Click the arrow to collapse the sidebar — icons stay visible with tooltips. Click again to expand.</p>
</main>
</div>
<script>
const sidebar = document.getElementById('sidebar');
const collapseBtn = document.getElementById('collapseBtn');
collapseBtn.addEventListener('click', () => {
const isCollapsed = sidebar.classList.toggle('collapsed');
collapseBtn.setAttribute('aria-expanded', String(!isCollapsed));
});
</script>
</body>
</html>import React, { useEffect } from 'react';
// CSS — optionally move to CollapsibleSidebarNav.module.css
const css = `
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,sans-serif;background:#0f172a;color:#e2e8f0;height:100vh;overflow:hidden}
.layout{display:flex;height:100vh}
/* Sidebar */
.sidebar{
width:220px;min-width:220px;
background:#1e293b;
border-right:1px solid #334155;
display:flex;flex-direction:column;
transition:width .28s cubic-bezier(.4,0,.2,1),min-width .28s cubic-bezier(.4,0,.2,1);
overflow:hidden;
}
.sidebar.collapsed{width:60px;min-width:60px;}
.sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:16px 14px;border-bottom:1px solid #334155;flex-shrink:0}
.logo{display:flex;align-items:center;gap:10px;color:#818cf8}
.logo-text{font-size:15px;font-weight:800;color:#f1f5f9;white-space:nowrap;overflow:hidden;transition:opacity .2s}
.collapsed .logo-text{opacity:0;width:0}
.collapse-btn{width:30px;height:30px;border-radius:8px;background:rgba(129,140,248,.1);border:1px solid rgba(129,140,248,.28);color:#818cf8;display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0;transition:background .15s,border-color .15s,color .15s}
.collapse-btn:hover{background:rgba(129,140,248,.22);border-color:rgba(129,140,248,.55);color:#a5b4fc}
.collapse-btn svg{transition:transform .28s cubic-bezier(.4,0,.2,1)}
.collapsed .collapse-btn svg{transform:rotate(180deg)}
.collapsed .sidebar-header{justify-content:center}
.collapsed .logo{display:none}
.nav-section{padding:12px 8px 0;flex-shrink:0}
.nav-label{font-size:9.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#475569;padding:0 8px 6px;white-space:nowrap;overflow:hidden;transition:opacity .15s}
.collapsed .nav-label{opacity:0}
.collapsed .nav-section{padding-left:0;padding-right:0}
.collapsed .nav-item{justify-content:center;padding:9px 0;border-radius:0;gap:0}
.collapsed .nav-text,.collapsed .nav-badge,.collapsed .nav-dot{display:none}
.collapsed .sidebar-footer{padding-left:0;padding-right:0}
.collapsed .user-info{justify-content:center;gap:0}
.collapsed .user-details{display:none}
.nav-item{display:flex;align-items:center;gap:10px;padding:9px 8px;border-radius:8px;text-decoration:none;color:#94a3b8;font-size:13px;font-weight:500;transition:background .15s,color .15s;position:relative;white-space:nowrap;cursor:pointer;margin-bottom:2px}
.nav-item:hover,.nav-item.active{background:#334155;color:#f1f5f9}
.nav-item.active{color:#818cf8}
.nav-icon{width:18px;height:18px;flex-shrink:0}
.nav-text{overflow:hidden;transition:opacity .15s,width .28s}
.collapsed .nav-text{opacity:0;width:0;overflow:hidden}
.nav-badge{font-size:9px;font-weight:700;padding:1px 6px;border-radius:20px;background:#818cf8;color:#fff;margin-left:auto;flex-shrink:0;transition:opacity .15s}
.nav-badge.new{background:#10b981}
.collapsed .nav-badge{opacity:0}
.nav-dot{width:6px;height:6px;border-radius:50%;background:#f59e0b;margin-left:auto;flex-shrink:0;transition:opacity .15s}
.collapsed .nav-dot{opacity:0}
/* Tooltip for collapsed state */
.collapsed .nav-item::after,.collapsed .user-info::after{content:attr(data-tip);position:absolute;left:calc(100% + 10px);top:50%;transform:translateY(-50%);background:#1e293b;border:1px solid #334155;color:#f1f5f9;font-size:11px;font-weight:500;padding:4px 10px;border-radius:6px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s;z-index:100;box-shadow:0 4px 12px rgba(0,0,0,0.3)}
.collapsed .nav-item:hover::after,.collapsed .user-info:hover::after{opacity:1}
/* Footer */
.sidebar-footer{margin-top:auto;padding:12px 8px;border-top:1px solid #334155}
.user-info{display:flex;align-items:center;gap:10px;padding:8px;border-radius:8px;cursor:pointer;transition:background .15s;position:relative}
.user-info:hover{background:#334155}
.avatar{width:30px;height:30px;border-radius:8px;background:linear-gradient(135deg,#6366f1,#8b5cf6);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;color:#fff;flex-shrink:0}
.user-details{overflow:hidden;transition:opacity .15s}
.collapsed .user-details{opacity:0;width:0}
.user-name{display:block;font-size:12px;font-weight:600;color:#f1f5f9;white-space:nowrap}
.user-role{display:block;font-size:10px;color:#64748b}
/* Main content */
.main-content{flex:1;overflow:auto;background:#0f172a;display:flex;flex-direction:column;min-width:0}
.topbar{display:flex;align-items:center;justify-content:space-between;padding:18px 24px;border-bottom:1px solid #1e293b}
.page-title{font-size:18px;font-weight:700;color:#f1f5f9}
.topbar-right{display:flex;align-items:center;gap:8px}
.status-dot{width:7px;height:7px;border-radius:50%;background:#10b981}
.status-text{font-size:12px;color:#64748b}
.stats-row{display:flex;gap:16px;padding:20px 24px}
.stat-card{flex:1;background:#1e293b;border:1px solid #334155;border-radius:12px;padding:16px;display:flex;flex-direction:column;gap:4px}
.stat-num{font-size:22px;font-weight:800;color:#f1f5f9}
.stat-label{font-size:11px;color:#64748b}
.placeholder-text{padding:0 24px;font-size:13px;color:#475569;line-height:1.7}
`;
export default function CollapsibleSidebarNav() {
// Auto-generated escape hatch: the original snippet's vanilla JS runs once
// after mount and queries the rendered DOM. For idiomatic React, lift this
// into state + handlers.
useEffect(() => {
const _listeners = [];
const _originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
_listeners.push({ target: this, type, listener, options });
return _originalAddEventListener.call(this, type, listener, options);
};
const sidebar = document.getElementById('sidebar');
const collapseBtn = document.getElementById('collapseBtn');
collapseBtn.addEventListener('click', () => {
const isCollapsed = sidebar.classList.toggle('collapsed');
collapseBtn.setAttribute('aria-expanded', String(!isCollapsed));
});
// Cleanup: restore addEventListener and remove all listeners
return () => {
EventTarget.prototype.addEventListener = _originalAddEventListener;
for (const { target, type, listener, options } of _listeners) {
target.removeEventListener(type, listener, options);
}
};
}, []);
return (
<>
<style>{css}</style>
<div className="layout">
<aside className="sidebar" id="sidebar" aria-label="Main navigation">
<div className="sidebar-header">
<div className="logo">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 22 22 7 12 2"/></svg>
<span className="logo-text">Nexus</span>
</div>
<button className="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" strokeWidth="2.5" strokeLinecap="round"><polyline points="15 18 9 12 15 6"/></svg>
</button>
</div>
<nav className="nav-section">
<p className="nav-label">Main</p>
<a className="nav-item active" href="#" data-tip="Dashboard">
<svg className="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
<span className="nav-text">Dashboard</span>
<span className="nav-badge">3</span>
</a>
<a className="nav-item" href="#" data-tip="Analytics">
<svg className="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><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"/></svg>
<span className="nav-text">Analytics</span>
</a>
<a className="nav-item" href="#" data-tip="Projects">
<svg className="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M3 7l9-4 9 4v13l-9 4-9-4z"/><polyline points="3 7 12 11 21 7"/><line x1="12" y1="11" x2="12" y2="22"/></svg>
<span className="nav-text">Projects</span>
<span className="nav-badge new">New</span>
</a>
<a className="nav-item" href="#" data-tip="Messages">
<svg className="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><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 className="nav-text">Messages</span>
<span className="nav-dot"></span>
</a>
</nav>
<nav className="nav-section">
<p className="nav-label">Management</p>
<a className="nav-item" href="#" data-tip="Team">
<svg className="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><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.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
<span className="nav-text">Team</span>
</a>
<a className="nav-item" href="#" data-tip="Files">
<svg className="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>
<span className="nav-text">Files</span>
</a>
<a className="nav-item" href="#" data-tip="Settings">
<svg className="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><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 className="nav-text">Settings</span>
</a>
</nav>
<div className="sidebar-footer">
<div className="user-info" data-tip="Puneet Sharma">
<div className="avatar">PS</div>
<div className="user-details">
<span className="user-name">Puneet Sharma</span>
<span className="user-role">Admin</span>
</div>
</div>
</div>
</aside>
<main className="main-content">
<div className="topbar">
<h1 className="page-title">Dashboard</h1>
<div className="topbar-right">
<span className="status-dot"></span>
<span className="status-text">All systems normal</span>
</div>
</div>
<div className="stats-row">
<div className="stat-card"><span className="stat-num">2,847</span><span className="stat-label">Active Users</span></div>
<div className="stat-card"><span className="stat-num">$18.2k</span><span className="stat-label">Revenue</span></div>
<div className="stat-card"><span className="stat-num">94.3%</span><span className="stat-label">Uptime</span></div>
</div>
<p className="placeholder-text">Click the arrow to collapse the sidebar — icons stay visible with tooltips. Click again to expand.</p>
</main>
</div>
</>
);
}import React, { useEffect } from 'react';
// Requires Tailwind CSS v3+ — https://tailwindcss.com/docs/installation
// Arbitrary value classes (e.g. bg-[#0f172a]) are valid Tailwind v3+
export default function CollapsibleSidebarNav() {
// Auto-generated escape hatch: the original snippet's vanilla JS runs once
// after mount and queries the rendered DOM. For idiomatic React, lift this
// into state + handlers.
useEffect(() => {
const _listeners = [];
const _originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
_listeners.push({ target: this, type, listener, options });
return _originalAddEventListener.call(this, type, listener, options);
};
const sidebar = document.getElementById('sidebar');
const collapseBtn = document.getElementById('collapseBtn');
collapseBtn.addEventListener('click', () => {
const isCollapsed = sidebar.classList.toggle('collapsed');
collapseBtn.setAttribute('aria-expanded', String(!isCollapsed));
});
// Cleanup: restore addEventListener and remove all listeners
return () => {
EventTarget.prototype.addEventListener = _originalAddEventListener;
for (const { target, type, listener, options } of _listeners) {
target.removeEventListener(type, listener, options);
}
};
}, []);
return (
<>
<style>{`
* {
box-sizing:border-box;margin:0;padding:0
}
body {
font-family:system-ui,sans-serif;background:#0f172a;color:#e2e8f0;height:100vh;overflow:hidden
}
.sidebar {
width:220px;min-width:220px;
background:#1e293b;
border-right:1px solid #334155;
display:flex;flex-direction:column;
transition:width .28s cubic-bezier(.4,0,.2,1),min-width .28s cubic-bezier(.4,0,.2,1);
overflow:hidden;
}
.sidebar.collapsed {
width:60px;min-width:60px;
}
.collapsed .logo-text {
opacity:0;width:0
}
.collapse-btn svg {
transition:transform .28s cubic-bezier(.4,0,.2,1)
}
.collapsed .collapse-btn svg {
transform:rotate(180deg)
}
.collapsed .sidebar-header {
justify-content:center
}
.collapsed .logo {
display:none
}
.collapsed .nav-label {
opacity:0
}
.collapsed .nav-section {
padding-left:0;padding-right:0
}
.collapsed .nav-item {
justify-content:center;padding:9px 0;border-radius:0;gap:0
}
.collapsed .nav-text,.collapsed .nav-badge,.collapsed .nav-dot {
display:none
}
.collapsed .sidebar-footer {
padding-left:0;padding-right:0
}
.collapsed .user-info {
justify-content:center;gap:0
}
.collapsed .user-details {
display:none
}
.nav-item:hover,.nav-item.active {
background:#334155;color:#f1f5f9
}
.nav-item.active {
color:#818cf8
}
.collapsed .nav-text {
opacity:0;width:0;overflow:hidden
}
.nav-badge.new {
background:#10b981
}
.collapsed .nav-badge {
opacity:0
}
.collapsed .nav-dot {
opacity:0
}
.collapsed .nav-item::after,.collapsed .user-info::after {
content:attr(data-tip);position:absolute;left:calc(100% + 10px);top:50%;transform:translateY(-50%);background:#1e293b;border:1px solid #334155;color:#f1f5f9;font-size:11px;font-weight:500;padding:4px 10px;border-radius:6px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s;z-index:100;box-shadow:0 4px 12px rgba(0,0,0,0.3)
}
.collapsed .nav-item:hover::after,.collapsed .user-info:hover::after {
opacity:1
}
.collapsed .user-details {
opacity:0;width:0
}
`}</style>
<div className="flex h-screen">
<aside className="sidebar" id="sidebar" aria-label="Main navigation">
<div className="sidebar-header flex items-center justify-between py-4 px-3.5 border-b border-b-[#334155] shrink-0">
<div className="logo flex items-center gap-2.5 text-[#818cf8]">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 22 22 7 12 2"/></svg>
<span className="logo-text text-[15px] font-extrabold text-[#f1f5f9] whitespace-nowrap overflow-hidden [transition:opacity_.2s]">Nexus</span>
</div>
<button className="collapse-btn w-[30px] h-[30px] rounded-lg bg-[rgba(129,140,248,.1)] border border-[rgba(129,140,248,.28)] text-[#818cf8] flex items-center justify-center cursor-pointer shrink-0 [transition:background_.15s,border-color_.15s,color_.15s] hover:bg-[rgba(129,140,248,.22)] hover:border-[rgba(129,140,248,.55)] hover:text-[#a5b4fc]" id="collapseBtn" aria-label="Toggle sidebar" aria-expanded="true">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><polyline points="15 18 9 12 15 6"/></svg>
</button>
</div>
<nav className="nav-section pt-3 px-2 pb-0 shrink-0">
<p className="nav-label text-[9.5px] font-bold tracking-[.08em] uppercase text-[#475569] pt-0 px-2 pb-1.5 whitespace-nowrap overflow-hidden [transition:opacity_.15s]">Main</p>
<a className="nav-item flex items-center gap-2.5 py-[9px] px-2 rounded-lg no-underline text-[#94a3b8] text-[13px] font-medium [transition:background_.15s,color_.15s] relative whitespace-nowrap cursor-pointer mb-0.5 active" href="#" data-tip="Dashboard">
<svg className="w-[18px] h-[18px] shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
<span className="nav-text overflow-hidden [transition:opacity_.15s,width_.28s]">Dashboard</span>
<span className="nav-badge text-[9px] font-bold py-px px-1.5 rounded-[20px] bg-[#818cf8] text-[#fff] ml-auto shrink-0 [transition:opacity_.15s]">3</span>
</a>
<a className="nav-item flex items-center gap-2.5 py-[9px] px-2 rounded-lg no-underline text-[#94a3b8] text-[13px] font-medium [transition:background_.15s,color_.15s] relative whitespace-nowrap cursor-pointer mb-0.5" href="#" data-tip="Analytics">
<svg className="w-[18px] h-[18px] shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><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"/></svg>
<span className="nav-text overflow-hidden [transition:opacity_.15s,width_.28s]">Analytics</span>
</a>
<a className="nav-item flex items-center gap-2.5 py-[9px] px-2 rounded-lg no-underline text-[#94a3b8] text-[13px] font-medium [transition:background_.15s,color_.15s] relative whitespace-nowrap cursor-pointer mb-0.5" href="#" data-tip="Projects">
<svg className="w-[18px] h-[18px] shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M3 7l9-4 9 4v13l-9 4-9-4z"/><polyline points="3 7 12 11 21 7"/><line x1="12" y1="11" x2="12" y2="22"/></svg>
<span className="nav-text overflow-hidden [transition:opacity_.15s,width_.28s]">Projects</span>
<span className="nav-badge text-[9px] font-bold py-px px-1.5 rounded-[20px] bg-[#818cf8] text-[#fff] ml-auto shrink-0 [transition:opacity_.15s] new">New</span>
</a>
<a className="nav-item flex items-center gap-2.5 py-[9px] px-2 rounded-lg no-underline text-[#94a3b8] text-[13px] font-medium [transition:background_.15s,color_.15s] relative whitespace-nowrap cursor-pointer mb-0.5" href="#" data-tip="Messages">
<svg className="w-[18px] h-[18px] shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><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 className="nav-text overflow-hidden [transition:opacity_.15s,width_.28s]">Messages</span>
<span className="nav-dot w-1.5 h-1.5 rounded-full bg-[#f59e0b] ml-auto shrink-0 [transition:opacity_.15s]"></span>
</a>
</nav>
<nav className="nav-section pt-3 px-2 pb-0 shrink-0">
<p className="nav-label text-[9.5px] font-bold tracking-[.08em] uppercase text-[#475569] pt-0 px-2 pb-1.5 whitespace-nowrap overflow-hidden [transition:opacity_.15s]">Management</p>
<a className="nav-item flex items-center gap-2.5 py-[9px] px-2 rounded-lg no-underline text-[#94a3b8] text-[13px] font-medium [transition:background_.15s,color_.15s] relative whitespace-nowrap cursor-pointer mb-0.5" href="#" data-tip="Team">
<svg className="w-[18px] h-[18px] shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><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.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
<span className="nav-text overflow-hidden [transition:opacity_.15s,width_.28s]">Team</span>
</a>
<a className="nav-item flex items-center gap-2.5 py-[9px] px-2 rounded-lg no-underline text-[#94a3b8] text-[13px] font-medium [transition:background_.15s,color_.15s] relative whitespace-nowrap cursor-pointer mb-0.5" href="#" data-tip="Files">
<svg className="w-[18px] h-[18px] shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>
<span className="nav-text overflow-hidden [transition:opacity_.15s,width_.28s]">Files</span>
</a>
<a className="nav-item flex items-center gap-2.5 py-[9px] px-2 rounded-lg no-underline text-[#94a3b8] text-[13px] font-medium [transition:background_.15s,color_.15s] relative whitespace-nowrap cursor-pointer mb-0.5" href="#" data-tip="Settings">
<svg className="w-[18px] h-[18px] shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><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 className="nav-text overflow-hidden [transition:opacity_.15s,width_.28s]">Settings</span>
</a>
</nav>
<div className="sidebar-footer mt-auto py-3 px-2 border-t border-t-[#334155]">
<div className="user-info flex items-center gap-2.5 p-2 rounded-lg cursor-pointer [transition:background_.15s] relative hover:bg-[#334155]" data-tip="Puneet Sharma">
<div className="w-[30px] h-[30px] rounded-lg [background:linear-gradient(135deg,#6366f1,#8b5cf6)] flex items-center justify-center text-xs font-bold text-[#fff] shrink-0">PS</div>
<div className="user-details overflow-hidden [transition:opacity_.15s]">
<span className="block text-xs font-semibold text-[#f1f5f9] whitespace-nowrap">Puneet Sharma</span>
<span className="block text-xs text-[#64748b]">Admin</span>
</div>
</div>
</div>
</aside>
<main className="flex-1 overflow-auto bg-[#0f172a] flex flex-col min-w-0">
<div className="flex items-center justify-between py-[18px] px-6 border-b border-b-[#1e293b]">
<h1 className="text-lg font-bold text-[#f1f5f9]">Dashboard</h1>
<div className="flex items-center gap-2">
<span className="w-[7px] h-[7px] rounded-full bg-[#10b981]"></span>
<span className="text-xs text-[#64748b]">All systems normal</span>
</div>
</div>
<div className="flex gap-4 py-5 px-6">
<div className="flex-1 bg-[#1e293b] border border-[#334155] rounded-xl p-4 flex flex-col gap-1"><span className="text-[22px] font-extrabold text-[#f1f5f9]">2,847</span><span className="text-[11px] text-[#64748b]">Active Users</span></div>
<div className="flex-1 bg-[#1e293b] border border-[#334155] rounded-xl p-4 flex flex-col gap-1"><span className="text-[22px] font-extrabold text-[#f1f5f9]">$18.2k</span><span className="text-[11px] text-[#64748b]">Revenue</span></div>
<div className="flex-1 bg-[#1e293b] border border-[#334155] rounded-xl p-4 flex flex-col gap-1"><span className="text-[22px] font-extrabold text-[#f1f5f9]">94.3%</span><span className="text-[11px] text-[#64748b]">Uptime</span></div>
</div>
<p className="py-0 px-6 text-[13px] text-[#475569] leading-[1.7]">Click the arrow to collapse the sidebar — icons stay visible with tooltips. Click again to expand.</p>
</main>
</div>
</>
);
}<template>
<div class="layout">
<aside class="sidebar" id="sidebar" aria-label="Main navigation">
<div class="sidebar-header">
<div class="logo">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 22 22 7 12 2"/></svg>
<span class="logo-text">Nexus</span>
</div>
<button class="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" stroke-linecap="round"><polyline points="15 18 9 12 15 6"/></svg>
</button>
</div>
<nav class="nav-section">
<p class="nav-label">Main</p>
<a class="nav-item active" href="#" data-tip="Dashboard">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
<span class="nav-text">Dashboard</span>
<span class="nav-badge">3</span>
</a>
<a class="nav-item" href="#" data-tip="Analytics">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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"/></svg>
<span class="nav-text">Analytics</span>
</a>
<a class="nav-item" href="#" data-tip="Projects">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M3 7l9-4 9 4v13l-9 4-9-4z"/><polyline points="3 7 12 11 21 7"/><line x1="12" y1="11" x2="12" y2="22"/></svg>
<span class="nav-text">Projects</span>
<span class="nav-badge new">New</span>
</a>
<a class="nav-item" href="#" data-tip="Messages">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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 class="nav-text">Messages</span>
<span class="nav-dot"></span>
</a>
</nav>
<nav class="nav-section">
<p class="nav-label">Management</p>
<a class="nav-item" href="#" data-tip="Team">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
<span class="nav-text">Team</span>
</a>
<a class="nav-item" href="#" data-tip="Files">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>
<span class="nav-text">Files</span>
</a>
<a class="nav-item" href="#" data-tip="Settings">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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 class="nav-text">Settings</span>
</a>
</nav>
<div class="sidebar-footer">
<div class="user-info" data-tip="Puneet Sharma">
<div class="avatar">PS</div>
<div class="user-details">
<span class="user-name">Puneet Sharma</span>
<span class="user-role">Admin</span>
</div>
</div>
</div>
</aside>
<main class="main-content">
<div class="topbar">
<h1 class="page-title">Dashboard</h1>
<div class="topbar-right">
<span class="status-dot"></span>
<span class="status-text">All systems normal</span>
</div>
</div>
<div class="stats-row">
<div class="stat-card"><span class="stat-num">2,847</span><span class="stat-label">Active Users</span></div>
<div class="stat-card"><span class="stat-num">$18.2k</span><span class="stat-label">Revenue</span></div>
<div class="stat-card"><span class="stat-num">94.3%</span><span class="stat-label">Uptime</span></div>
</div>
<p class="placeholder-text">Click the arrow to collapse the sidebar — icons stay visible with tooltips. Click again to expand.</p>
</main>
</div>
</template>
<script setup>
import { onMounted } from 'vue';
let sidebar;
let collapseBtn;
onMounted(() => {
sidebar = document.getElementById('sidebar');
collapseBtn = document.getElementById('collapseBtn');
collapseBtn.addEventListener('click', () => {
const isCollapsed = sidebar.classList.toggle('collapsed');
collapseBtn.setAttribute('aria-expanded', String(!isCollapsed));
});
});
</script>
<style scoped>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,sans-serif;background:#0f172a;color:#e2e8f0;height:100vh;overflow:hidden}
.layout{display:flex;height:100vh}
/* Sidebar */
.sidebar{
width:220px;min-width:220px;
background:#1e293b;
border-right:1px solid #334155;
display:flex;flex-direction:column;
transition:width .28s cubic-bezier(.4,0,.2,1),min-width .28s cubic-bezier(.4,0,.2,1);
overflow:hidden;
}
.sidebar.collapsed{width:60px;min-width:60px;}
.sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:16px 14px;border-bottom:1px solid #334155;flex-shrink:0}
.logo{display:flex;align-items:center;gap:10px;color:#818cf8}
.logo-text{font-size:15px;font-weight:800;color:#f1f5f9;white-space:nowrap;overflow:hidden;transition:opacity .2s}
.collapsed .logo-text{opacity:0;width:0}
.collapse-btn{width:30px;height:30px;border-radius:8px;background:rgba(129,140,248,.1);border:1px solid rgba(129,140,248,.28);color:#818cf8;display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0;transition:background .15s,border-color .15s,color .15s}
.collapse-btn:hover{background:rgba(129,140,248,.22);border-color:rgba(129,140,248,.55);color:#a5b4fc}
.collapse-btn svg{transition:transform .28s cubic-bezier(.4,0,.2,1)}
.collapsed .collapse-btn svg{transform:rotate(180deg)}
.collapsed .sidebar-header{justify-content:center}
.collapsed .logo{display:none}
.nav-section{padding:12px 8px 0;flex-shrink:0}
.nav-label{font-size:9.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#475569;padding:0 8px 6px;white-space:nowrap;overflow:hidden;transition:opacity .15s}
.collapsed .nav-label{opacity:0}
.collapsed .nav-section{padding-left:0;padding-right:0}
.collapsed .nav-item{justify-content:center;padding:9px 0;border-radius:0;gap:0}
.collapsed .nav-text,.collapsed .nav-badge,.collapsed .nav-dot{display:none}
.collapsed .sidebar-footer{padding-left:0;padding-right:0}
.collapsed .user-info{justify-content:center;gap:0}
.collapsed .user-details{display:none}
.nav-item{display:flex;align-items:center;gap:10px;padding:9px 8px;border-radius:8px;text-decoration:none;color:#94a3b8;font-size:13px;font-weight:500;transition:background .15s,color .15s;position:relative;white-space:nowrap;cursor:pointer;margin-bottom:2px}
.nav-item:hover,.nav-item.active{background:#334155;color:#f1f5f9}
.nav-item.active{color:#818cf8}
.nav-icon{width:18px;height:18px;flex-shrink:0}
.nav-text{overflow:hidden;transition:opacity .15s,width .28s}
.collapsed .nav-text{opacity:0;width:0;overflow:hidden}
.nav-badge{font-size:9px;font-weight:700;padding:1px 6px;border-radius:20px;background:#818cf8;color:#fff;margin-left:auto;flex-shrink:0;transition:opacity .15s}
.nav-badge.new{background:#10b981}
.collapsed .nav-badge{opacity:0}
.nav-dot{width:6px;height:6px;border-radius:50%;background:#f59e0b;margin-left:auto;flex-shrink:0;transition:opacity .15s}
.collapsed .nav-dot{opacity:0}
/* Tooltip for collapsed state */
.collapsed .nav-item::after,.collapsed .user-info::after{content:attr(data-tip);position:absolute;left:calc(100% + 10px);top:50%;transform:translateY(-50%);background:#1e293b;border:1px solid #334155;color:#f1f5f9;font-size:11px;font-weight:500;padding:4px 10px;border-radius:6px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s;z-index:100;box-shadow:0 4px 12px rgba(0,0,0,0.3)}
.collapsed .nav-item:hover::after,.collapsed .user-info:hover::after{opacity:1}
/* Footer */
.sidebar-footer{margin-top:auto;padding:12px 8px;border-top:1px solid #334155}
.user-info{display:flex;align-items:center;gap:10px;padding:8px;border-radius:8px;cursor:pointer;transition:background .15s;position:relative}
.user-info:hover{background:#334155}
.avatar{width:30px;height:30px;border-radius:8px;background:linear-gradient(135deg,#6366f1,#8b5cf6);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;color:#fff;flex-shrink:0}
.user-details{overflow:hidden;transition:opacity .15s}
.collapsed .user-details{opacity:0;width:0}
.user-name{display:block;font-size:12px;font-weight:600;color:#f1f5f9;white-space:nowrap}
.user-role{display:block;font-size:10px;color:#64748b}
/* Main content */
.main-content{flex:1;overflow:auto;background:#0f172a;display:flex;flex-direction:column;min-width:0}
.topbar{display:flex;align-items:center;justify-content:space-between;padding:18px 24px;border-bottom:1px solid #1e293b}
.page-title{font-size:18px;font-weight:700;color:#f1f5f9}
.topbar-right{display:flex;align-items:center;gap:8px}
.status-dot{width:7px;height:7px;border-radius:50%;background:#10b981}
.status-text{font-size:12px;color:#64748b}
.stats-row{display:flex;gap:16px;padding:20px 24px}
.stat-card{flex:1;background:#1e293b;border:1px solid #334155;border-radius:12px;padding:16px;display:flex;flex-direction:column;gap:4px}
.stat-num{font-size:22px;font-weight:800;color:#f1f5f9}
.stat-label{font-size:11px;color:#64748b}
.placeholder-text{padding:0 24px;font-size:13px;color:#475569;line-height:1.7}
</style>// @ts-nocheck
// Note: vanilla JS DOM manipulation is preserved as-is inside ngAfterViewInit().
// For idiomatic Angular, replace document.getElementById() with @ViewChild() refs
// and move state into component properties with two-way binding.
import { Component, AfterViewInit, ViewEncapsulation } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-collapsible-sidebar-nav',
standalone: true,
imports: [CommonModule],
encapsulation: ViewEncapsulation.None,
template: `
<div class="layout">
<aside class="sidebar" id="sidebar" aria-label="Main navigation">
<div class="sidebar-header">
<div class="logo">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 22 22 7 12 2"/></svg>
<span class="logo-text">Nexus</span>
</div>
<button class="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" stroke-linecap="round"><polyline points="15 18 9 12 15 6"/></svg>
</button>
</div>
<nav class="nav-section">
<p class="nav-label">Main</p>
<a class="nav-item active" href="#" data-tip="Dashboard">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
<span class="nav-text">Dashboard</span>
<span class="nav-badge">3</span>
</a>
<a class="nav-item" href="#" data-tip="Analytics">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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"/></svg>
<span class="nav-text">Analytics</span>
</a>
<a class="nav-item" href="#" data-tip="Projects">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M3 7l9-4 9 4v13l-9 4-9-4z"/><polyline points="3 7 12 11 21 7"/><line x1="12" y1="11" x2="12" y2="22"/></svg>
<span class="nav-text">Projects</span>
<span class="nav-badge new">New</span>
</a>
<a class="nav-item" href="#" data-tip="Messages">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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 class="nav-text">Messages</span>
<span class="nav-dot"></span>
</a>
</nav>
<nav class="nav-section">
<p class="nav-label">Management</p>
<a class="nav-item" href="#" data-tip="Team">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
<span class="nav-text">Team</span>
</a>
<a class="nav-item" href="#" data-tip="Files">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>
<span class="nav-text">Files</span>
</a>
<a class="nav-item" href="#" data-tip="Settings">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><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 class="nav-text">Settings</span>
</a>
</nav>
<div class="sidebar-footer">
<div class="user-info" data-tip="Puneet Sharma">
<div class="avatar">PS</div>
<div class="user-details">
<span class="user-name">Puneet Sharma</span>
<span class="user-role">Admin</span>
</div>
</div>
</div>
</aside>
<main class="main-content">
<div class="topbar">
<h1 class="page-title">Dashboard</h1>
<div class="topbar-right">
<span class="status-dot"></span>
<span class="status-text">All systems normal</span>
</div>
</div>
<div class="stats-row">
<div class="stat-card"><span class="stat-num">2,847</span><span class="stat-label">Active Users</span></div>
<div class="stat-card"><span class="stat-num">$18.2k</span><span class="stat-label">Revenue</span></div>
<div class="stat-card"><span class="stat-num">94.3%</span><span class="stat-label">Uptime</span></div>
</div>
<p class="placeholder-text">Click the arrow to collapse the sidebar — icons stay visible with tooltips. Click again to expand.</p>
</main>
</div>
`,
styles: [`
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,sans-serif;background:#0f172a;color:#e2e8f0;height:100vh;overflow:hidden}
.layout{display:flex;height:100vh}
/* Sidebar */
.sidebar{
width:220px;min-width:220px;
background:#1e293b;
border-right:1px solid #334155;
display:flex;flex-direction:column;
transition:width .28s cubic-bezier(.4,0,.2,1),min-width .28s cubic-bezier(.4,0,.2,1);
overflow:hidden;
}
.sidebar.collapsed{width:60px;min-width:60px;}
.sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:16px 14px;border-bottom:1px solid #334155;flex-shrink:0}
.logo{display:flex;align-items:center;gap:10px;color:#818cf8}
.logo-text{font-size:15px;font-weight:800;color:#f1f5f9;white-space:nowrap;overflow:hidden;transition:opacity .2s}
.collapsed .logo-text{opacity:0;width:0}
.collapse-btn{width:30px;height:30px;border-radius:8px;background:rgba(129,140,248,.1);border:1px solid rgba(129,140,248,.28);color:#818cf8;display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0;transition:background .15s,border-color .15s,color .15s}
.collapse-btn:hover{background:rgba(129,140,248,.22);border-color:rgba(129,140,248,.55);color:#a5b4fc}
.collapse-btn svg{transition:transform .28s cubic-bezier(.4,0,.2,1)}
.collapsed .collapse-btn svg{transform:rotate(180deg)}
.collapsed .sidebar-header{justify-content:center}
.collapsed .logo{display:none}
.nav-section{padding:12px 8px 0;flex-shrink:0}
.nav-label{font-size:9.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#475569;padding:0 8px 6px;white-space:nowrap;overflow:hidden;transition:opacity .15s}
.collapsed .nav-label{opacity:0}
.collapsed .nav-section{padding-left:0;padding-right:0}
.collapsed .nav-item{justify-content:center;padding:9px 0;border-radius:0;gap:0}
.collapsed .nav-text,.collapsed .nav-badge,.collapsed .nav-dot{display:none}
.collapsed .sidebar-footer{padding-left:0;padding-right:0}
.collapsed .user-info{justify-content:center;gap:0}
.collapsed .user-details{display:none}
.nav-item{display:flex;align-items:center;gap:10px;padding:9px 8px;border-radius:8px;text-decoration:none;color:#94a3b8;font-size:13px;font-weight:500;transition:background .15s,color .15s;position:relative;white-space:nowrap;cursor:pointer;margin-bottom:2px}
.nav-item:hover,.nav-item.active{background:#334155;color:#f1f5f9}
.nav-item.active{color:#818cf8}
.nav-icon{width:18px;height:18px;flex-shrink:0}
.nav-text{overflow:hidden;transition:opacity .15s,width .28s}
.collapsed .nav-text{opacity:0;width:0;overflow:hidden}
.nav-badge{font-size:9px;font-weight:700;padding:1px 6px;border-radius:20px;background:#818cf8;color:#fff;margin-left:auto;flex-shrink:0;transition:opacity .15s}
.nav-badge.new{background:#10b981}
.collapsed .nav-badge{opacity:0}
.nav-dot{width:6px;height:6px;border-radius:50%;background:#f59e0b;margin-left:auto;flex-shrink:0;transition:opacity .15s}
.collapsed .nav-dot{opacity:0}
/* Tooltip for collapsed state */
.collapsed .nav-item::after,.collapsed .user-info::after{content:attr(data-tip);position:absolute;left:calc(100% + 10px);top:50%;transform:translateY(-50%);background:#1e293b;border:1px solid #334155;color:#f1f5f9;font-size:11px;font-weight:500;padding:4px 10px;border-radius:6px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s;z-index:100;box-shadow:0 4px 12px rgba(0,0,0,0.3)}
.collapsed .nav-item:hover::after,.collapsed .user-info:hover::after{opacity:1}
/* Footer */
.sidebar-footer{margin-top:auto;padding:12px 8px;border-top:1px solid #334155}
.user-info{display:flex;align-items:center;gap:10px;padding:8px;border-radius:8px;cursor:pointer;transition:background .15s;position:relative}
.user-info:hover{background:#334155}
.avatar{width:30px;height:30px;border-radius:8px;background:linear-gradient(135deg,#6366f1,#8b5cf6);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;color:#fff;flex-shrink:0}
.user-details{overflow:hidden;transition:opacity .15s}
.collapsed .user-details{opacity:0;width:0}
.user-name{display:block;font-size:12px;font-weight:600;color:#f1f5f9;white-space:nowrap}
.user-role{display:block;font-size:10px;color:#64748b}
/* Main content */
.main-content{flex:1;overflow:auto;background:#0f172a;display:flex;flex-direction:column;min-width:0}
.topbar{display:flex;align-items:center;justify-content:space-between;padding:18px 24px;border-bottom:1px solid #1e293b}
.page-title{font-size:18px;font-weight:700;color:#f1f5f9}
.topbar-right{display:flex;align-items:center;gap:8px}
.status-dot{width:7px;height:7px;border-radius:50%;background:#10b981}
.status-text{font-size:12px;color:#64748b}
.stats-row{display:flex;gap:16px;padding:20px 24px}
.stat-card{flex:1;background:#1e293b;border:1px solid #334155;border-radius:12px;padding:16px;display:flex;flex-direction:column;gap:4px}
.stat-num{font-size:22px;font-weight:800;color:#f1f5f9}
.stat-label{font-size:11px;color:#64748b}
.placeholder-text{padding:0 24px;font-size:13px;color:#475569;line-height:1.7}
`]
})
export class CollapsibleSidebarNavComponent implements AfterViewInit {
ngAfterViewInit(): void {
const sidebar = document.getElementById('sidebar');
const collapseBtn = document.getElementById('collapseBtn');
collapseBtn.addEventListener('click', () => {
const isCollapsed = sidebar.classList.toggle('collapsed');
collapseBtn.setAttribute('aria-expanded', String(!isCollapsed));
});
}
}