Chapter 7: Vertical Tabs
How to create vertical tabs (also called side tabs, left sidebar tabs, vertical tab navigation, etc.).
Vertical tabs are very common in:
- Admin dashboards
- Settings pages
- Long forms / wizards
- Documentation sites
- Email clients / project management tools
- Desktop-style web applications
Let’s build them properly — from simple to professional — with clear explanations and real-world considerations.
What makes good vertical tabs?
- The active tab is clearly visible (color, border, background, icon…)
- Tabs stay fixed or scroll with content (depending on design)
- Content area takes most of the space on the right
- Looks good on desktop → collapses gracefully on mobile (usually becomes horizontal tabs or accordion)
- Keyboard accessible
- Works with mouse, touch, and screen readers
We’ll create three versions:
- Simple & clean vertical tabs (pure HTML + CSS + minimal JS)
- Responsive version (desktop vertical → mobile horizontal)
- Modern, accessible version with ARIA + smooth transitions
Version 1 – Clean & Classic Vertical Tabs
HTML Structure
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<div class="vertical-tabs-container"> <!-- Left sidebar with tabs --> <div class="tab-list" role="tablist"> <button class="tab-btn active" data-tab="overview" aria-selected="true" aria-controls="overview-panel"> Overview </button> <button class="tab-btn" data-tab="profile" aria-selected="false" aria-controls="profile-panel"> Profile </button> <button class="tab-btn" data-tab="account" aria-selected="false" aria-controls="account-panel"> Account </button> <button class="tab-btn" data-tab="notifications" aria-selected="false" aria-controls="notifications-panel"> Notifications </button> <button class="tab-btn" data-tab="security" aria-selected="false" aria-controls="security-panel"> Security </button> </div> <!-- Right content area --> <div class="tab-content-area"> <div class="tab-panel active" id="overview-panel" role="tabpanel" aria-labelledby="overview-tab"> <h2>Overview</h2> <p>This is the dashboard overview. You can see quick stats, recent activity, and important alerts here.</p> </div> <div class="tab-panel" id="profile-panel" role="tabpanel" aria-labelledby="profile-tab" hidden> <h2>Profile Information</h2> <p>Update your name, photo, bio, job title, location, etc.</p> </div> <div class="tab-panel" id="account-panel" role="tabpanel" aria-labelledby="account-tab" hidden> <h2>Account Settings</h2> <p>Language, timezone, currency, dark mode preference...</p> </div> <div class="tab-panel" id="notifications-panel" role="tabpanel" aria-labelledby="notifications-tab" hidden> <h2>Notification Preferences</h2> <p>Choose what kind of notifications you want to receive and how.</p> </div> <div class="tab-panel" id="security-panel" role="tabpanel" aria-labelledby="security-tab" hidden> <h2>Security & Login</h2> <p>Change password, enable 2FA, review login history, active sessions.</p> </div> </div> </div> |
CSS (modern & clean look)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
.vertical-tabs-container { display: flex; min-height: 500px; max-width: 1100px; margin: 2rem auto; border: 1px solid #e5e5e5; border-radius: 8px; overflow: hidden; font-family: system-ui, -apple-system, sans-serif; } .tab-list { width: 240px; background: #f8f9fa; border-right: 1px solid #e5e5e5; padding: 1.5rem 0; flex-shrink: 0; } .tab-btn { display: block; width: 100%; padding: 0.95rem 1.8rem; text-align: left; background: none; border: none; font-size: 1.03rem; color: #444; cursor: pointer; transition: all 0.18s ease; } .tab-btn:hover { background: #eef2ff; color: #1e40af; } .tab-btn.active { background: white; color: #1d4ed8; font-weight: 600; border-right: 3px solid #3b82f6; box-shadow: inset 3px 0 0 #3b82f6; position: relative; } .tab-content-area { flex: 1; background: white; padding: 2.2rem; } .tab-panel { display: none; } .tab-panel.active { display: block; } /* Very subtle fade-in */ .tab-panel { opacity: 0; transition: opacity 0.25s ease; } .tab-panel.active { opacity: 1; } |
JavaScript (minimal & clean)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
document.addEventListener("DOMContentLoaded", () => { const buttons = document.querySelectorAll(".tab-btn"); buttons.forEach(btn => { btn.addEventListener("click", () => { // Remove active from all buttons.forEach(b => { b.classList.remove("active"); b.setAttribute("aria-selected", "false"); }); document.querySelectorAll(".tab-panel").forEach(p => { p.classList.remove("active"); p.setAttribute("hidden", ""); }); // Activate clicked tab btn.classList.add("active"); btn.setAttribute("aria-selected", "true"); const panelId = btn.getAttribute("aria-controls"); const panel = document.getElementById(panelId); panel.classList.add("active"); panel.removeAttribute("hidden"); }); }); }); |
Version 2 – Responsive (Desktop vertical → Mobile horizontal)
Add a media query to switch layout on small screens.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
@media (max-width: 768px) { .vertical-tabs-container { flex-direction: column; } .tab-list { width: 100%; border-right: none; border-bottom: 1px solid #e5e5e5; display: flex; overflow-x: auto; padding: 0; background: #fff; } .tab-btn { flex: 0 0 auto; padding: 1rem 1.4rem; text-align: center; white-space: nowrap; } .tab-btn.active { border-right: none; border-bottom: 3px solid #3b82f6; box-shadow: none; } .tab-content-area { padding: 1.5rem; } } |
Now on mobile the tabs become a horizontal scrollable bar — very common pattern.
Version 3 – With Icons + Better Visual Feedback
Add icons (using Font Awesome or similar):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<button class="tab-btn active" data-tab="overview" aria-selected="true" aria-controls="overview-panel"> <i class="fas fa-home"></i> Overview </button> <button class="tab-btn" data-tab="profile" aria-selected="false" aria-controls="profile-panel"> <i class="fas fa-user"></i> Profile </button> <button class="tab-btn" data-tab="account" aria-selected="false" aria-controls="account-panel"> <i class="fas fa-cog"></i> Account </button> ... |
CSS adjustment:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
.tab-btn { display: flex; align-items: center; gap: 12px; padding: 1rem 1.6rem; } .tab-btn i { width: 20px; font-size: 1.15rem; opacity: 0.7; } .tab-btn.active i { opacity: 1; } |
Important Things Good Developers Always Consider
- Active indicator Most common options: left border, background color, text color + bold, subtle shadow
- Minimum touch/click target Buttons should be at least 44×44 px (mobile) — we usually make them taller (52–60 px)
- Keyboard navigation
- Tab key should move between tab buttons
- Arrow keys can move focus between tabs (optional but nice)
- Mobile behavior
- Horizontal scrollable tabs
- OR collapse to accordion (using <details>)
- Lazy loading (advanced) Only load content of the active tab — useful for heavy dashboards
- Sticky tabs (sometimes) If the tab list is long, you can make it position: sticky; top: 0;
Quick Checklist Before You Finish
- Clear visual difference for active tab
- Enough padding & spacing
- Works on mobile (test at 320–768 px)
- Keyboard focus is visible
- aria-selected and aria-controls are correct
- role=”tablist”, role=”tab”, role=”tabpanel” used
Would you like to go deeper into any of these next topics?
Examples:
- How to make vertical tabs sticky when scrolling
- How to turn vertical tabs into accordion on mobile
- How to add smooth slide/fade animation
- How to make icon-only tabs with tooltips
- How to handle very long tab lists (scrollable sidebar)
- How to do vertical tabs in Tailwind CSS
- How to make nested / sub-tabs inside a tab
Just tell me what you want next — I’ll explain it slowly with complete code examples. 😊
