Vertical Tabs
A copy-paste accordions & tabs component in pure HTML, CSS & vanilla JS. Zero dependencies, framework-agnostic, MIT-licensed.
Accordions & TabsHTMLCSSJavaScriptany framework
Copy into your project
HTML
<div class="nuda-vtabs">
<div class="nuda-vtabs__sidebar" role="tablist" aria-orientation="vertical">
<button class="nuda-vtabs__tab is-active" role="tab" data-tab="a">Tab A</button>
<button class="nuda-vtabs__tab" role="tab" data-tab="b">Tab B</button>
<button class="nuda-vtabs__tab" role="tab" data-tab="c">Tab C</button>
</div>
<div class="nuda-vtabs__panel is-active" data-panel="a" role="tabpanel">Panel A content</div>
<div class="nuda-vtabs__panel" data-panel="b" role="tabpanel">Panel B content</div>
<div class="nuda-vtabs__panel" data-panel="c" role="tabpanel">Panel C content</div>
</div>CSS
/* Vertical Tabs
Side-by-side tabs with left sidebar.
Customize: --vtab-accent */
.nuda-vtabs {
--vtab-accent: #e4ff54;
display: flex;
gap: 0.5rem;
max-width: 600px;
}
.nuda-vtabs__sidebar {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 120px;
}
.nuda-vtabs__tab {
padding: 0.6rem 0.75rem;
font-size: 0.85rem;
color: #888;
background: none;
border: none;
border-left: 2px solid transparent;
border-radius: 0 8px 8px 0;
cursor: pointer;
text-align: left;
transition: all 0.2s ease;
}
.nuda-vtabs__tab:hover { color: #ccc; background: rgba(255, 255, 255, 0.02); }
.nuda-vtabs__tab.is-active {
color: var(--vtab-accent);
background: rgba(228, 255, 84, 0.06);
border-left-color: var(--vtab-accent);
}
.nuda-vtabs__panel {
display: none;
flex: 1;
padding: 1rem;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 10px;
color: #ccc;
font-size: 0.85rem;
animation: nuda-vtab-fade 0.3s ease-out;
}
.nuda-vtabs__panel.is-active { display: block; }
@keyframes nuda-vtab-fade {
from { opacity: 0; transform: translateX(8px); }
to { opacity: 1; transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) {
.nuda-vtabs__tab { transition: none; }
.nuda-vtabs__panel { animation: none; }
}JavaScript
/* Vertical Tabs — Switch panels */
(function () {
var containers = document.querySelectorAll('.nuda-vtabs');
containers.forEach(function (vtabs) {
var tabs = vtabs.querySelectorAll('.nuda-vtabs__tab');
var panels = vtabs.querySelectorAll('.nuda-vtabs__panel');
tabs.forEach(function (tab) {
tab.addEventListener('click', function () {
tabs.forEach(function (t) { t.classList.remove('is-active'); });
panels.forEach(function (p) { p.classList.remove('is-active'); });
tab.classList.add('is-active');
var panel = vtabs.querySelector('[data-panel="' + tab.dataset.tab + '"]');
if (panel) panel.classList.add('is-active');
});
});
});
})();How to use Vertical Tabs
Paste the HTML where you need it and the CSS into a global stylesheet (or a <style> tag). Every class is prefixed nuda- so it never collides with Tailwind or your own styles. Tweak the CSS custom properties to match your design system.
Works in React, Vue, Svelte, Astro, Next.js, Nuxt, Laravel Blade, Django, Rails — or a single .html file. No npm install, no build step.