21 / 22
All 22 in 22 Tailwind Dropdown Menus
- 01Tailwind Slide-Down Fade Dropdown Menu
- 02Tailwind Clip-Path Curtain Reveal Dropdown
- 03Tailwind 3D Perspective Flip Dropdown
- 04Tailwind Elastic Bounce Dropdown Menu
- 05Tailwind Glassmorphism Float Panel Dropdown
- 06Tailwind Stagger Children Reveal Dropdown
- 07Tailwind Mega Menu Grid Dropdown
- 08Tailwind Underline Nav Fade Panel Dropdown
- 09Tailwind Details Summary Native Dropdown
- 10Tailwind Checkbox Hack Mobile Nav Dropdown
- 11Tailwind Nested Multi-Level Dropdown
- 12Tailwind Icon Sidebar Flyout Dropdown
- 13Tailwind Split-Screen Mega Menu Dropdown
- 14Tailwind Segmented Pill Nav Dropdown
- 15Tailwind Color Swatch Picker Dropdown
- 16Tailwind Right-Click Context Menu Dropdown
- 17Tailwind Keyboard Accessible ARIA Dropdown
- 18Tailwind Stagger Blur Entrance Dropdown
- 19Tailwind Command Palette Search Dropdown
- 20Tailwind Autocomplete Suggestion Dropdown
- 21Tailwind Tab Panel Dropdown
- 22Tailwind Custom Animated Select Dropdown
Tailwind Tab Panel Dropdown
A dropdown containing a real tabbed interface. Both states read off ARIA: the panel opens on the trigger's aria-expanded, and the tabs style themselves from aria-selected — so the tab strip's appearance and its announcement can never disagree.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tdd-21 w-full min-h-svh grid justify-center content-start bg-linear-[135deg,var(--color-tdd21-bg1),var(--color-tdd21-bg2)] font-tdd21-sans text-tdd21-ink px-5 py-8" aria-label="Tab panel dropdown demo">
<nav class="relative z-10 flex w-[min(600px,100%)] items-center gap-1 rounded-2xl border border-tdd21-edge bg-white py-2.5 pr-2.5 pl-5 shadow-[0_4px_24px_oklch(0.7_0.16_163/0.12)]" aria-label="Primary">
<span class="mr-3 text-[17px] font-bold text-tdd21-acc">◈ Mosaic</span>
<div class="group relative">
<button type="button" id="tdd21-btn" aria-expanded="false" aria-controls="tdd21-drop" class="min-h-11 cursor-pointer rounded-lg border-0 bg-transparent px-3.5 py-2 font-tdd21-sans text-sm font-semibold text-tdd21-ink transition-colors duration-150 hover:bg-tdd21-tint focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd21-acc aria-expanded:bg-tdd21-tint aria-expanded:text-tdd21-acc">Explore ▾</button>
<div id="tdd21-drop" class="pointer-events-none invisible absolute top-[calc(100%+10px)] left-0 w-65 -translate-y-2 overflow-hidden rounded-2xl border border-tdd21-edge bg-white opacity-0 shadow-[0_12px_40px_oklch(0.7_0.16_163/0.14)] transition-[opacity,translate,visibility] duration-280 ease-tdd21 group-data-open:pointer-events-auto group-data-open:visible group-data-open:translate-y-0 group-data-open:opacity-100 motion-reduce:transition-none">
<div role="tablist" aria-label="Explore sections" class="flex gap-0.5 border-b border-tdd21-edge px-1.5 pt-1.5">
<button type="button" role="tab" aria-selected="true" aria-controls="tdd21-p1" class="-mb-px flex-1 cursor-pointer rounded-t-lg border-0 border-b-2 border-b-transparent bg-transparent px-1.5 py-2 font-tdd21-sans text-[13px] font-semibold text-tdd21-mut transition-colors duration-150 hover:bg-tdd21-tint2 hover:text-tdd21-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tdd21-acc aria-selected:border-b-tdd21-acc aria-selected:bg-tdd21-tint2 aria-selected:text-tdd21-acc">Popular</button>
<button type="button" role="tab" aria-selected="false" aria-controls="tdd21-p2" class="-mb-px flex-1 cursor-pointer rounded-t-lg border-0 border-b-2 border-b-transparent bg-transparent px-1.5 py-2 font-tdd21-sans text-[13px] font-semibold text-tdd21-mut transition-colors duration-150 hover:bg-tdd21-tint2 hover:text-tdd21-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tdd21-acc aria-selected:border-b-tdd21-acc aria-selected:bg-tdd21-tint2 aria-selected:text-tdd21-acc">New</button>
<button type="button" role="tab" aria-selected="false" aria-controls="tdd21-p3" class="-mb-px flex-1 cursor-pointer rounded-t-lg border-0 border-b-2 border-b-transparent bg-transparent px-1.5 py-2 font-tdd21-sans text-[13px] font-semibold text-tdd21-mut transition-colors duration-150 hover:bg-tdd21-tint2 hover:text-tdd21-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tdd21-acc aria-selected:border-b-tdd21-acc aria-selected:bg-tdd21-tint2 aria-selected:text-tdd21-acc">Topics</button>
</div>
<div class="min-h-35 p-1.5">
<div role="tabpanel" id="tdd21-p1" class="flex flex-col gap-0.5 [&:not([hidden])]:animate-tdd21-fade">
<a href="#most-visited" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🔥 Most Visited</a>
<a href="#editor-picks" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🌟 Editor Picks</a>
<a href="#trending" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">📊 Trending Now</a>
<a href="#all-time" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🏆 All-Time Best</a>
</div>
<div role="tabpanel" id="tdd21-p2" hidden class="flex flex-col gap-0.5 [&:not([hidden])]:animate-tdd21-fade">
<a href="#just-launched" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🚀 Just Launched</a>
<a href="#this-week" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🕐 This Week</a>
<a href="#this-month" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">📅 This Month</a>
<a href="#coming-soon" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🆕 Coming Soon</a>
</div>
<div role="tabpanel" id="tdd21-p3" hidden class="flex flex-col gap-0.5 [&:not([hidden])]:animate-tdd21-fade">
<a href="#design" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🌟 Design</a>
<a href="#engineering" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">💻 Engineering</a>
<a href="#product" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🚀 Product</a>
<a href="#business" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">📚 Business</a>
</div>
</div>
</div>
</div>
<a href="#pricing" class="rounded-lg px-3.5 py-2 text-sm font-medium text-tdd21-mut no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd21-acc">Pricing</a>
<a href="#blog" class="rounded-lg px-3.5 py-2 text-sm font-medium text-tdd21-mut no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd21-acc">Blog</a>
</nav>
</section><section class="tdd-21 w-full min-h-svh grid justify-center content-start bg-linear-[135deg,var(--color-tdd21-bg1),var(--color-tdd21-bg2)] font-tdd21-sans text-tdd21-ink px-5 py-8" aria-label="Tab panel dropdown demo">
<nav class="relative z-10 flex w-[min(600px,100%)] items-center gap-1 rounded-2xl border border-tdd21-edge bg-white py-2.5 pr-2.5 pl-5 shadow-[0_4px_24px_oklch(0.7_0.16_163/0.12)]" aria-label="Primary">
<span class="mr-3 text-[17px] font-bold text-tdd21-acc">◈ Mosaic</span>
<div class="group relative">
<button type="button" id="tdd21-btn" aria-expanded="false" aria-controls="tdd21-drop" class="min-h-11 cursor-pointer rounded-lg border-0 bg-transparent px-3.5 py-2 font-tdd21-sans text-sm font-semibold text-tdd21-ink transition-colors duration-150 hover:bg-tdd21-tint focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd21-acc aria-expanded:bg-tdd21-tint aria-expanded:text-tdd21-acc">Explore ▾</button>
<div id="tdd21-drop" class="pointer-events-none invisible absolute top-[calc(100%+10px)] left-0 w-65 -translate-y-2 overflow-hidden rounded-2xl border border-tdd21-edge bg-white opacity-0 shadow-[0_12px_40px_oklch(0.7_0.16_163/0.14)] transition-[opacity,translate,visibility] duration-280 ease-tdd21 group-data-open:pointer-events-auto group-data-open:visible group-data-open:translate-y-0 group-data-open:opacity-100 motion-reduce:transition-none">
<div role="tablist" aria-label="Explore sections" class="flex gap-0.5 border-b border-tdd21-edge px-1.5 pt-1.5">
<button type="button" role="tab" aria-selected="true" aria-controls="tdd21-p1" class="-mb-px flex-1 cursor-pointer rounded-t-lg border-0 border-b-2 border-b-transparent bg-transparent px-1.5 py-2 font-tdd21-sans text-[13px] font-semibold text-tdd21-mut transition-colors duration-150 hover:bg-tdd21-tint2 hover:text-tdd21-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tdd21-acc aria-selected:border-b-tdd21-acc aria-selected:bg-tdd21-tint2 aria-selected:text-tdd21-acc">Popular</button>
<button type="button" role="tab" aria-selected="false" aria-controls="tdd21-p2" class="-mb-px flex-1 cursor-pointer rounded-t-lg border-0 border-b-2 border-b-transparent bg-transparent px-1.5 py-2 font-tdd21-sans text-[13px] font-semibold text-tdd21-mut transition-colors duration-150 hover:bg-tdd21-tint2 hover:text-tdd21-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tdd21-acc aria-selected:border-b-tdd21-acc aria-selected:bg-tdd21-tint2 aria-selected:text-tdd21-acc">New</button>
<button type="button" role="tab" aria-selected="false" aria-controls="tdd21-p3" class="-mb-px flex-1 cursor-pointer rounded-t-lg border-0 border-b-2 border-b-transparent bg-transparent px-1.5 py-2 font-tdd21-sans text-[13px] font-semibold text-tdd21-mut transition-colors duration-150 hover:bg-tdd21-tint2 hover:text-tdd21-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tdd21-acc aria-selected:border-b-tdd21-acc aria-selected:bg-tdd21-tint2 aria-selected:text-tdd21-acc">Topics</button>
</div>
<div class="min-h-35 p-1.5">
<div role="tabpanel" id="tdd21-p1" class="flex flex-col gap-0.5 [&:not([hidden])]:animate-tdd21-fade">
<a href="#most-visited" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🔥 Most Visited</a>
<a href="#editor-picks" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🌟 Editor Picks</a>
<a href="#trending" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">📊 Trending Now</a>
<a href="#all-time" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🏆 All-Time Best</a>
</div>
<div role="tabpanel" id="tdd21-p2" hidden class="flex flex-col gap-0.5 [&:not([hidden])]:animate-tdd21-fade">
<a href="#just-launched" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🚀 Just Launched</a>
<a href="#this-week" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🕐 This Week</a>
<a href="#this-month" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">📅 This Month</a>
<a href="#coming-soon" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🆕 Coming Soon</a>
</div>
<div role="tabpanel" id="tdd21-p3" hidden class="flex flex-col gap-0.5 [&:not([hidden])]:animate-tdd21-fade">
<a href="#design" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🌟 Design</a>
<a href="#engineering" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">💻 Engineering</a>
<a href="#product" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🚀 Product</a>
<a href="#business" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">📚 Business</a>
</div>
</div>
</div>
</div>
<a href="#pricing" class="rounded-lg px-3.5 py-2 text-sm font-medium text-tdd21-mut no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd21-acc">Pricing</a>
<a href="#blog" class="rounded-lg px-3.5 py-2 text-sm font-medium text-tdd21-mut no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd21-acc">Blog</a>
</nav>
</section>@import "tailwindcss";
@theme {
--color-tdd21-bg1: #ecfdf5;
--color-tdd21-bg1: oklch(0.98 0.02 165);
--color-tdd21-bg2: #d1fae5;
--color-tdd21-bg2: oklch(0.92 0.06 165);
--color-tdd21-ink: #064e3b;
--color-tdd21-ink: oklch(0.36 0.08 165);
--color-tdd21-mut: #6b7280;
--color-tdd21-mut: oklch(0.55 0.02 265);
--color-tdd21-edge: #d1fae5;
--color-tdd21-edge: oklch(0.92 0.06 165);
--color-tdd21-tint: #ecfdf5;
--color-tdd21-tint: oklch(0.98 0.02 165);
--color-tdd21-tint2: #f0fdf4;
--color-tdd21-tint2: oklch(0.98 0.02 150);
--color-tdd21-acc: #10b981;
--color-tdd21-acc: oklch(0.7 0.16 163);
--font-tdd21-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--ease-tdd21: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tdd21-fade: tdd21-fade 0.22s ease;
}
/* Keyframes are the one thing utilities cannot express; the token above turns
this into a real animate-tdd21-fade utility. */
@keyframes tdd21-fade {
from {
opacity: 0;
transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.tdd-21 {
width: 100%;
min-height: 100svh;
}
.tdd-21 ::selection {
background: var(--color-tdd21-acc);
color: #fff;
}@import "tailwindcss";
@theme {
--color-tdd21-bg1: #ecfdf5;
--color-tdd21-bg1: oklch(0.98 0.02 165);
--color-tdd21-bg2: #d1fae5;
--color-tdd21-bg2: oklch(0.92 0.06 165);
--color-tdd21-ink: #064e3b;
--color-tdd21-ink: oklch(0.36 0.08 165);
--color-tdd21-mut: #6b7280;
--color-tdd21-mut: oklch(0.55 0.02 265);
--color-tdd21-edge: #d1fae5;
--color-tdd21-edge: oklch(0.92 0.06 165);
--color-tdd21-tint: #ecfdf5;
--color-tdd21-tint: oklch(0.98 0.02 165);
--color-tdd21-tint2: #f0fdf4;
--color-tdd21-tint2: oklch(0.98 0.02 150);
--color-tdd21-acc: #10b981;
--color-tdd21-acc: oklch(0.7 0.16 163);
--font-tdd21-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--ease-tdd21: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tdd21-fade: tdd21-fade 0.22s ease;
}
/* Keyframes are the one thing utilities cannot express; the token above turns
this into a real animate-tdd21-fade utility. */
@keyframes tdd21-fade {
from {
opacity: 0;
transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.tdd-21 {
width: 100%;
min-height: 100svh;
}
.tdd-21 ::selection {
background: var(--color-tdd21-acc);
color: #fff;
}(function () {
document.querySelectorAll('.tdd-21').forEach(function (root) {
if (root.dataset.bound) return; root.dataset.bound = '1';
var wrap = root.querySelector('.group'),
btn = root.querySelector('#tdd21-btn'),
drop = root.querySelector('#tdd21-drop');
if (!wrap || !btn || !drop) return;
var tabs = Array.from(drop.querySelectorAll('[role="tab"]'));
function select(tab) {
tabs.forEach(function (t) {
var on = t === tab;
t.setAttribute('aria-selected', String(on));
var panel = root.querySelector('#' + t.getAttribute('aria-controls'));
if (panel) panel.hidden = !on;
});
}
function setOpen(open) {
wrap.toggleAttribute('data-open', open);
btn.setAttribute('aria-expanded', String(open));
}
tabs.forEach(function (tab, i) {
tab.addEventListener('click', function () { select(tab); });
tab.addEventListener('keydown', function (e) {
if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') return;
e.preventDefault();
var n = e.key === 'ArrowRight' ? (i + 1) % tabs.length : (i - 1 + tabs.length) % tabs.length;
tabs[n].focus(); select(tabs[n]);
});
});
btn.addEventListener('click', function (e) {
e.stopPropagation();
setOpen(btn.getAttribute('aria-expanded') !== 'true');
});
document.addEventListener('click', function (e) { if (!wrap.contains(e.target)) setOpen(false); });
root.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && wrap.hasAttribute('data-open')) { setOpen(false); btn.focus(); }
});
});
})();(function () {
document.querySelectorAll('.tdd-21').forEach(function (root) {
if (root.dataset.bound) return; root.dataset.bound = '1';
var wrap = root.querySelector('.group'),
btn = root.querySelector('#tdd21-btn'),
drop = root.querySelector('#tdd21-drop');
if (!wrap || !btn || !drop) return;
var tabs = Array.from(drop.querySelectorAll('[role="tab"]'));
function select(tab) {
tabs.forEach(function (t) {
var on = t === tab;
t.setAttribute('aria-selected', String(on));
var panel = root.querySelector('#' + t.getAttribute('aria-controls'));
if (panel) panel.hidden = !on;
});
}
function setOpen(open) {
wrap.toggleAttribute('data-open', open);
btn.setAttribute('aria-expanded', String(open));
}
tabs.forEach(function (tab, i) {
tab.addEventListener('click', function () { select(tab); });
tab.addEventListener('keydown', function (e) {
if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') return;
e.preventDefault();
var n = e.key === 'ArrowRight' ? (i + 1) % tabs.length : (i - 1 + tabs.length) % tabs.length;
tabs[n].focus(); select(tabs[n]);
});
});
btn.addEventListener('click', function (e) {
e.stopPropagation();
setOpen(btn.getAttribute('aria-expanded') !== 'true');
});
document.addEventListener('click', function (e) { if (!wrap.contains(e.target)) setOpen(false); });
root.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && wrap.hasAttribute('data-open')) { setOpen(false); btn.focus(); }
});
});
})();Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tdd21-bg1: #ecfdf5;
--color-tdd21-bg1: oklch(0.98 0.02 165);
--color-tdd21-bg2: #d1fae5;
--color-tdd21-bg2: oklch(0.92 0.06 165);
--color-tdd21-ink: #064e3b;
--color-tdd21-ink: oklch(0.36 0.08 165);
--color-tdd21-mut: #6b7280;
--color-tdd21-mut: oklch(0.55 0.02 265);
--color-tdd21-edge: #d1fae5;
--color-tdd21-edge: oklch(0.92 0.06 165);
--color-tdd21-tint: #ecfdf5;
--color-tdd21-tint: oklch(0.98 0.02 165);
--color-tdd21-tint2: #f0fdf4;
--color-tdd21-tint2: oklch(0.98 0.02 150);
--color-tdd21-acc: #10b981;
--color-tdd21-acc: oklch(0.7 0.16 163);
--font-tdd21-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--ease-tdd21: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tdd21-fade: tdd21-fade 0.22s ease;
}
/* Keyframes are the one thing utilities cannot express; the token above turns
this into a real animate-tdd21-fade utility. */
@keyframes tdd21-fade {
from {
opacity: 0;
transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.tdd-21 {
width: 100%;
min-height: 100svh;
}
.tdd-21 ::selection {
background: var(--color-tdd21-acc);
color: #fff;
}@import "tailwindcss";
@theme {
--color-tdd21-bg1: #ecfdf5;
--color-tdd21-bg1: oklch(0.98 0.02 165);
--color-tdd21-bg2: #d1fae5;
--color-tdd21-bg2: oklch(0.92 0.06 165);
--color-tdd21-ink: #064e3b;
--color-tdd21-ink: oklch(0.36 0.08 165);
--color-tdd21-mut: #6b7280;
--color-tdd21-mut: oklch(0.55 0.02 265);
--color-tdd21-edge: #d1fae5;
--color-tdd21-edge: oklch(0.92 0.06 165);
--color-tdd21-tint: #ecfdf5;
--color-tdd21-tint: oklch(0.98 0.02 165);
--color-tdd21-tint2: #f0fdf4;
--color-tdd21-tint2: oklch(0.98 0.02 150);
--color-tdd21-acc: #10b981;
--color-tdd21-acc: oklch(0.7 0.16 163);
--font-tdd21-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--ease-tdd21: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tdd21-fade: tdd21-fade 0.22s ease;
}
/* Keyframes are the one thing utilities cannot express; the token above turns
this into a real animate-tdd21-fade utility. */
@keyframes tdd21-fade {
from {
opacity: 0;
transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.tdd-21 {
width: 100%;
min-height: 100svh;
}
.tdd-21 ::selection {
background: var(--color-tdd21-acc);
color: #fff;
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
aria-selected:means the tab strip's appearance is literally derived from what a screen reader announces — the two cannot drift.hiddeninstead of an.is-hiddenclass gives correct semantics for free and lets the entry animation key off[&:not([hidden])]:.- The keyframes stay in CSS but are exposed as a utility through
--animate-tdd21-fade, which is the pattern for every animation in this collection. - Arrow-key tab navigation was a documented omission in the CSS version; the port ships it in six lines.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Tab Panel Dropdown
Source: https://codefronts.com/navigation/tailwind-dropdown-menus/tailwind-tab-panel-dropdown/
A dropdown containing a real tabbed interface. Both states read off ARIA: the panel opens on the trigger's aria-expanded, and the tabs style themselves from aria-selected — so the tab strip's appearance and its announcement can never disagree.
## HTML
```html
<section class="tdd-21 w-full min-h-svh grid justify-center content-start bg-linear-[135deg,var(--color-tdd21-bg1),var(--color-tdd21-bg2)] font-tdd21-sans text-tdd21-ink px-5 py-8" aria-label="Tab panel dropdown demo">
<nav class="relative z-10 flex w-[min(600px,100%)] items-center gap-1 rounded-2xl border border-tdd21-edge bg-white py-2.5 pr-2.5 pl-5 shadow-[0_4px_24px_oklch(0.7_0.16_163/0.12)]" aria-label="Primary">
<span class="mr-3 text-[17px] font-bold text-tdd21-acc">◈ Mosaic</span>
<div class="group relative">
<button type="button" id="tdd21-btn" aria-expanded="false" aria-controls="tdd21-drop" class="min-h-11 cursor-pointer rounded-lg border-0 bg-transparent px-3.5 py-2 font-tdd21-sans text-sm font-semibold text-tdd21-ink transition-colors duration-150 hover:bg-tdd21-tint focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd21-acc aria-expanded:bg-tdd21-tint aria-expanded:text-tdd21-acc">Explore ▾</button>
<div id="tdd21-drop" class="pointer-events-none invisible absolute top-[calc(100%+10px)] left-0 w-65 -translate-y-2 overflow-hidden rounded-2xl border border-tdd21-edge bg-white opacity-0 shadow-[0_12px_40px_oklch(0.7_0.16_163/0.14)] transition-[opacity,translate,visibility] duration-280 ease-tdd21 group-data-open:pointer-events-auto group-data-open:visible group-data-open:translate-y-0 group-data-open:opacity-100 motion-reduce:transition-none">
<div role="tablist" aria-label="Explore sections" class="flex gap-0.5 border-b border-tdd21-edge px-1.5 pt-1.5">
<button type="button" role="tab" aria-selected="true" aria-controls="tdd21-p1" class="-mb-px flex-1 cursor-pointer rounded-t-lg border-0 border-b-2 border-b-transparent bg-transparent px-1.5 py-2 font-tdd21-sans text-[13px] font-semibold text-tdd21-mut transition-colors duration-150 hover:bg-tdd21-tint2 hover:text-tdd21-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tdd21-acc aria-selected:border-b-tdd21-acc aria-selected:bg-tdd21-tint2 aria-selected:text-tdd21-acc">Popular</button>
<button type="button" role="tab" aria-selected="false" aria-controls="tdd21-p2" class="-mb-px flex-1 cursor-pointer rounded-t-lg border-0 border-b-2 border-b-transparent bg-transparent px-1.5 py-2 font-tdd21-sans text-[13px] font-semibold text-tdd21-mut transition-colors duration-150 hover:bg-tdd21-tint2 hover:text-tdd21-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tdd21-acc aria-selected:border-b-tdd21-acc aria-selected:bg-tdd21-tint2 aria-selected:text-tdd21-acc">New</button>
<button type="button" role="tab" aria-selected="false" aria-controls="tdd21-p3" class="-mb-px flex-1 cursor-pointer rounded-t-lg border-0 border-b-2 border-b-transparent bg-transparent px-1.5 py-2 font-tdd21-sans text-[13px] font-semibold text-tdd21-mut transition-colors duration-150 hover:bg-tdd21-tint2 hover:text-tdd21-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tdd21-acc aria-selected:border-b-tdd21-acc aria-selected:bg-tdd21-tint2 aria-selected:text-tdd21-acc">Topics</button>
</div>
<div class="min-h-35 p-1.5">
<div role="tabpanel" id="tdd21-p1" class="flex flex-col gap-0.5 [&:not([hidden])]:animate-tdd21-fade">
<a href="#most-visited" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🔥 Most Visited</a>
<a href="#editor-picks" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🌟 Editor Picks</a>
<a href="#trending" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">📊 Trending Now</a>
<a href="#all-time" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🏆 All-Time Best</a>
</div>
<div role="tabpanel" id="tdd21-p2" hidden class="flex flex-col gap-0.5 [&:not([hidden])]:animate-tdd21-fade">
<a href="#just-launched" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🚀 Just Launched</a>
<a href="#this-week" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🕐 This Week</a>
<a href="#this-month" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">📅 This Month</a>
<a href="#coming-soon" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🆕 Coming Soon</a>
</div>
<div role="tabpanel" id="tdd21-p3" hidden class="flex flex-col gap-0.5 [&:not([hidden])]:animate-tdd21-fade">
<a href="#design" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🌟 Design</a>
<a href="#engineering" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">💻 Engineering</a>
<a href="#product" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🚀 Product</a>
<a href="#business" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">📚 Business</a>
</div>
</div>
</div>
</div>
<a href="#pricing" class="rounded-lg px-3.5 py-2 text-sm font-medium text-tdd21-mut no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd21-acc">Pricing</a>
<a href="#blog" class="rounded-lg px-3.5 py-2 text-sm font-medium text-tdd21-mut no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd21-acc">Blog</a>
</nav>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tdd21-bg1: #ecfdf5;
--color-tdd21-bg1: oklch(0.98 0.02 165);
--color-tdd21-bg2: #d1fae5;
--color-tdd21-bg2: oklch(0.92 0.06 165);
--color-tdd21-ink: #064e3b;
--color-tdd21-ink: oklch(0.36 0.08 165);
--color-tdd21-mut: #6b7280;
--color-tdd21-mut: oklch(0.55 0.02 265);
--color-tdd21-edge: #d1fae5;
--color-tdd21-edge: oklch(0.92 0.06 165);
--color-tdd21-tint: #ecfdf5;
--color-tdd21-tint: oklch(0.98 0.02 165);
--color-tdd21-tint2: #f0fdf4;
--color-tdd21-tint2: oklch(0.98 0.02 150);
--color-tdd21-acc: #10b981;
--color-tdd21-acc: oklch(0.7 0.16 163);
--font-tdd21-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--ease-tdd21: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tdd21-fade: tdd21-fade 0.22s ease;
}
/* Keyframes are the one thing utilities cannot express; the token above turns
this into a real animate-tdd21-fade utility. */
@keyframes tdd21-fade {
from {
opacity: 0;
transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.tdd-21 {
width: 100%;
min-height: 100svh;
}
.tdd-21 ::selection {
background: var(--color-tdd21-acc);
color: #fff;
}
```
## JavaScript
```js
(function () {
document.querySelectorAll('.tdd-21').forEach(function (root) {
if (root.dataset.bound) return; root.dataset.bound = '1';
var wrap = root.querySelector('.group'),
btn = root.querySelector('#tdd21-btn'),
drop = root.querySelector('#tdd21-drop');
if (!wrap || !btn || !drop) return;
var tabs = Array.from(drop.querySelectorAll('[role="tab"]'));
function select(tab) {
tabs.forEach(function (t) {
var on = t === tab;
t.setAttribute('aria-selected', String(on));
var panel = root.querySelector('#' + t.getAttribute('aria-controls'));
if (panel) panel.hidden = !on;
});
}
function setOpen(open) {
wrap.toggleAttribute('data-open', open);
btn.setAttribute('aria-expanded', String(open));
}
tabs.forEach(function (tab, i) {
tab.addEventListener('click', function () { select(tab); });
tab.addEventListener('keydown', function (e) {
if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') return;
e.preventDefault();
var n = e.key === 'ArrowRight' ? (i + 1) % tabs.length : (i - 1 + tabs.length) % tabs.length;
tabs[n].focus(); select(tabs[n]);
});
});
btn.addEventListener('click', function (e) {
e.stopPropagation();
setOpen(btn.getAttribute('aria-expanded') !== 'true');
});
document.addEventListener('click', function (e) { if (!wrap.contains(e.target)) setOpen(false); });
root.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && wrap.hasAttribute('data-open')) { setOpen(false); btn.focus(); }
});
});
})();
```Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Tab Panel Dropdown
Source: https://codefronts.com/navigation/tailwind-dropdown-menus/tailwind-tab-panel-dropdown/
A dropdown containing a real tabbed interface. Both states read off ARIA: the panel opens on the trigger's aria-expanded, and the tabs style themselves from aria-selected — so the tab strip's appearance and its announcement can never disagree.
## HTML
```html
<section class="tdd-21 w-full min-h-svh grid justify-center content-start bg-linear-[135deg,var(--color-tdd21-bg1),var(--color-tdd21-bg2)] font-tdd21-sans text-tdd21-ink px-5 py-8" aria-label="Tab panel dropdown demo">
<nav class="relative z-10 flex w-[min(600px,100%)] items-center gap-1 rounded-2xl border border-tdd21-edge bg-white py-2.5 pr-2.5 pl-5 shadow-[0_4px_24px_oklch(0.7_0.16_163/0.12)]" aria-label="Primary">
<span class="mr-3 text-[17px] font-bold text-tdd21-acc">◈ Mosaic</span>
<div class="group relative">
<button type="button" id="tdd21-btn" aria-expanded="false" aria-controls="tdd21-drop" class="min-h-11 cursor-pointer rounded-lg border-0 bg-transparent px-3.5 py-2 font-tdd21-sans text-sm font-semibold text-tdd21-ink transition-colors duration-150 hover:bg-tdd21-tint focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd21-acc aria-expanded:bg-tdd21-tint aria-expanded:text-tdd21-acc">Explore ▾</button>
<div id="tdd21-drop" class="pointer-events-none invisible absolute top-[calc(100%+10px)] left-0 w-65 -translate-y-2 overflow-hidden rounded-2xl border border-tdd21-edge bg-white opacity-0 shadow-[0_12px_40px_oklch(0.7_0.16_163/0.14)] transition-[opacity,translate,visibility] duration-280 ease-tdd21 group-data-open:pointer-events-auto group-data-open:visible group-data-open:translate-y-0 group-data-open:opacity-100 motion-reduce:transition-none">
<div role="tablist" aria-label="Explore sections" class="flex gap-0.5 border-b border-tdd21-edge px-1.5 pt-1.5">
<button type="button" role="tab" aria-selected="true" aria-controls="tdd21-p1" class="-mb-px flex-1 cursor-pointer rounded-t-lg border-0 border-b-2 border-b-transparent bg-transparent px-1.5 py-2 font-tdd21-sans text-[13px] font-semibold text-tdd21-mut transition-colors duration-150 hover:bg-tdd21-tint2 hover:text-tdd21-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tdd21-acc aria-selected:border-b-tdd21-acc aria-selected:bg-tdd21-tint2 aria-selected:text-tdd21-acc">Popular</button>
<button type="button" role="tab" aria-selected="false" aria-controls="tdd21-p2" class="-mb-px flex-1 cursor-pointer rounded-t-lg border-0 border-b-2 border-b-transparent bg-transparent px-1.5 py-2 font-tdd21-sans text-[13px] font-semibold text-tdd21-mut transition-colors duration-150 hover:bg-tdd21-tint2 hover:text-tdd21-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tdd21-acc aria-selected:border-b-tdd21-acc aria-selected:bg-tdd21-tint2 aria-selected:text-tdd21-acc">New</button>
<button type="button" role="tab" aria-selected="false" aria-controls="tdd21-p3" class="-mb-px flex-1 cursor-pointer rounded-t-lg border-0 border-b-2 border-b-transparent bg-transparent px-1.5 py-2 font-tdd21-sans text-[13px] font-semibold text-tdd21-mut transition-colors duration-150 hover:bg-tdd21-tint2 hover:text-tdd21-ink focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tdd21-acc aria-selected:border-b-tdd21-acc aria-selected:bg-tdd21-tint2 aria-selected:text-tdd21-acc">Topics</button>
</div>
<div class="min-h-35 p-1.5">
<div role="tabpanel" id="tdd21-p1" class="flex flex-col gap-0.5 [&:not([hidden])]:animate-tdd21-fade">
<a href="#most-visited" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🔥 Most Visited</a>
<a href="#editor-picks" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🌟 Editor Picks</a>
<a href="#trending" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">📊 Trending Now</a>
<a href="#all-time" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🏆 All-Time Best</a>
</div>
<div role="tabpanel" id="tdd21-p2" hidden class="flex flex-col gap-0.5 [&:not([hidden])]:animate-tdd21-fade">
<a href="#just-launched" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🚀 Just Launched</a>
<a href="#this-week" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🕐 This Week</a>
<a href="#this-month" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">📅 This Month</a>
<a href="#coming-soon" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🆕 Coming Soon</a>
</div>
<div role="tabpanel" id="tdd21-p3" hidden class="flex flex-col gap-0.5 [&:not([hidden])]:animate-tdd21-fade">
<a href="#design" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🌟 Design</a>
<a href="#engineering" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">💻 Engineering</a>
<a href="#product" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">🚀 Product</a>
<a href="#business" class="flex items-center gap-2 rounded-[9px] px-3 py-2.5 text-[13.5px] font-medium text-tdd21-ink no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-acc focus-visible:bg-tdd21-tint focus-visible:outline-none">📚 Business</a>
</div>
</div>
</div>
</div>
<a href="#pricing" class="rounded-lg px-3.5 py-2 text-sm font-medium text-tdd21-mut no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd21-acc">Pricing</a>
<a href="#blog" class="rounded-lg px-3.5 py-2 text-sm font-medium text-tdd21-mut no-underline transition-colors duration-150 hover:bg-tdd21-tint hover:text-tdd21-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd21-acc">Blog</a>
</nav>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tdd21-bg1: #ecfdf5;
--color-tdd21-bg1: oklch(0.98 0.02 165);
--color-tdd21-bg2: #d1fae5;
--color-tdd21-bg2: oklch(0.92 0.06 165);
--color-tdd21-ink: #064e3b;
--color-tdd21-ink: oklch(0.36 0.08 165);
--color-tdd21-mut: #6b7280;
--color-tdd21-mut: oklch(0.55 0.02 265);
--color-tdd21-edge: #d1fae5;
--color-tdd21-edge: oklch(0.92 0.06 165);
--color-tdd21-tint: #ecfdf5;
--color-tdd21-tint: oklch(0.98 0.02 165);
--color-tdd21-tint2: #f0fdf4;
--color-tdd21-tint2: oklch(0.98 0.02 150);
--color-tdd21-acc: #10b981;
--color-tdd21-acc: oklch(0.7 0.16 163);
--font-tdd21-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--ease-tdd21: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tdd21-fade: tdd21-fade 0.22s ease;
}
/* Keyframes are the one thing utilities cannot express; the token above turns
this into a real animate-tdd21-fade utility. */
@keyframes tdd21-fade {
from {
opacity: 0;
transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.tdd-21 {
width: 100%;
min-height: 100svh;
}
.tdd-21 ::selection {
background: var(--color-tdd21-acc);
color: #fff;
}
```
## JavaScript
```js
(function () {
document.querySelectorAll('.tdd-21').forEach(function (root) {
if (root.dataset.bound) return; root.dataset.bound = '1';
var wrap = root.querySelector('.group'),
btn = root.querySelector('#tdd21-btn'),
drop = root.querySelector('#tdd21-drop');
if (!wrap || !btn || !drop) return;
var tabs = Array.from(drop.querySelectorAll('[role="tab"]'));
function select(tab) {
tabs.forEach(function (t) {
var on = t === tab;
t.setAttribute('aria-selected', String(on));
var panel = root.querySelector('#' + t.getAttribute('aria-controls'));
if (panel) panel.hidden = !on;
});
}
function setOpen(open) {
wrap.toggleAttribute('data-open', open);
btn.setAttribute('aria-expanded', String(open));
}
tabs.forEach(function (tab, i) {
tab.addEventListener('click', function () { select(tab); });
tab.addEventListener('keydown', function (e) {
if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') return;
e.preventDefault();
var n = e.key === 'ArrowRight' ? (i + 1) % tabs.length : (i - 1 + tabs.length) % tabs.length;
tabs[n].focus(); select(tabs[n]);
});
});
btn.addEventListener('click', function (e) {
e.stopPropagation();
setOpen(btn.getAttribute('aria-expanded') !== 'true');
});
document.addEventListener('click', function (e) { if (!wrap.contains(e.target)) setOpen(false); });
root.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && wrap.hasAttribute('data-open')) { setOpen(false); btn.focus(); }
});
});
})();
```How this works
Two independent state machines, both declarative. The dropdown reads data-open on its wrapper; the tabs read their own aria-selected. Neither knows about the other.
<button role="tab" aria-selected="true"
class="text-tdd21-mut aria-selected:text-tdd21-acc
aria-selected:border-b-tdd21-acc">The fade is a theme animation. --animate-tdd21-fade in @theme mints animate-tdd21-fade, and the keyframes stay in the stylesheet because keyframes are the one thing utilities genuinely cannot express.
Panels switch with hidden, not a class, so a hidden panel is invisible to screen readers as well as to the eye.
The strip has a fixed min-h so switching to a shorter tab does not make the dropdown jump.
Make it yours
- Sliding indicator: measure the active tab and translate one absolute bar; the ARIA layer is unaffected.
- Scrollable strip:
overflow-x-auto whitespace-nowrapon the tablist for many tabs. - Remember the last tab: the controller already reads
aria-selected, so nothing resets on close. - Icons above labels: the tabs are flex containers; add
flex-coland an icon span.
Gotchas — read before shipping
- Panels of different heights make the dropdown jump;
min-h-35on the panel area is the fix. - Arrow-key movement between tabs is part of the tab pattern, not an extra — without it keyboard users must Tab through every panel link.
- An
overflow-hiddenancestor clips the absolute dropdown; the nav isrelativeand clips nothing. aria-selectedbelongs on the tab, never on the panel.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
The ARIA tab pattern is supported by all major screen readers; attribute variants compile to plain selectors.