More Layouts Snippets
Feature Tabs Showcase — Free HTML CSS JS Tabs Snippet
Feature Tabs Showcase · Layouts · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Feature Tabs Showcase — Sliding Indicator and Animated Panels

The feature-tabs showcase is the product-marketing pattern where one section presents several capabilities behind a row of tabs — click a tab and the copy plus an illustrative graphic swap in. This snippet builds the complete interaction in plain HTML, CSS, and vanilla JavaScript: a sliding gradient indicator that glides under the active tab, panels that fade and lift as they enter, distinct artwork per feature, and full keyboard support.
The sliding gradient indicator
The tab strip contains the three buttons plus a single .ft-ink element absolutely positioned behind them. On every activation, moveInk reads the chosen tab's offsetLeft and offsetWidth and writes them as a translateX and width, so the gradient pill slides to sit exactly under the active tab. Because it animates transform and width with a cubic-bezier ease, the indicator glides smoothly between tabs of different widths instead of snapping. The active tab's text turns white while inactive labels stay muted, reinforcing the selection.
Panel swaps that animate in
Each panel is laid out as a two-column grid: descriptive copy with a checkmarked feature list on one side, a custom illustration on the other. Switching panels is a two-step dance that makes the entrance animate. The outgoing panel loses its .active class and gets the hidden attribute; the incoming panel has hidden removed first, then — inside a requestAnimationFrame so the browser registers the visible state — gets .active added, which transitions it from opacity:0 and translateY(10px) to fully visible. Without that rAF gap, the browser would batch the changes and skip the transition.
Bespoke artwork per feature
Rather than placeholder images, each panel ships a tiny CSS illustration: the Analytics tab animates a row of bars that grow from zero with a ftGrow keyframe; the Automation tab draws nodes connected by gradient wires; the Collaboration tab shows a cluster of mention chips with an overflow "+4". These are pure HTML and CSS, so they stay crisp at any size and weigh nothing. Swap them for real screenshots by replacing the .ft-art contents.
Keyboard and ARIA
The strip is a role="tablist" of role="tab" buttons, each with aria-selected that flips on change, and each panel is a role="tabpanel". Arrow keys move focus and selection: ArrowRight and ArrowLeft wrap around the tab list and activate the newly focused tab, matching the WAI-ARIA tabs pattern so keyboard and screen-reader users get the same experience as mouse users.
Staying aligned
Tab widths depend on their text and the viewport, so a resize listener re-measures and repositions the indicator under the current active tab. On first paint the indicator is placed inside a requestAnimationFrame to ensure the tabs have been laid out before offsetLeft is read.
Customizing it
Add a fourth tab by copying a button and a panel and the logic adapts automatically — tabs and panels are read from the DOM. Recolor the indicator gradient to match your brand, replace the CSS artwork with product screenshots, or change the panel grid to stack copy above art. On screens under 560px the tabs stretch full width and the panels collapse to a single column. Pair it with a testimonial wall below or a pricing toggle to round out a feature page.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA three-tab feature section renders with the first tab active.
- 2Click a tabThe gradient indicator slides under it and the panel swaps in.
- 3Watch the entranceThe new panel fades and lifts into place; bars or wires animate.
- 4Use arrow keysLeft and right move focus and selection between tabs.
- 5Resize the windowThe indicator re-measures to stay under the active tab.
- 6Swap in real artReplace the CSS illustrations with product screenshots.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
A single absolutely-positioned ink element sits behind the buttons. moveInk reads the active tab's offsetLeft and offsetWidth and applies them as translateX and width, animating transform and width with a cubic-bezier ease so the gradient pill glides between tabs of different widths rather than snapping.
The incoming panel starts hidden at opacity:0 and translateY(10px). If you removed hidden and added the active class in the same tick, the browser would batch both and skip the transition. Removing hidden first, then adding active inside a requestAnimationFrame, lets the browser paint the hidden-but-visible start state so the fade-and-lift actually animates.
No — each panel's art is pure HTML and CSS. The analytics bars grow from zero with a keyframe, the automation nodes connect via gradient wire divs, and the collaboration chips are styled spans. They stay sharp at any resolution and add no network weight. Replace the .ft-art contents with screenshots if you prefer real imagery.
Yes. The strip uses the WAI-ARIA tabs pattern: a tablist of tab buttons with aria-selected and tabpanels. ArrowRight and ArrowLeft move focus and activate the next or previous tab, wrapping around the ends, so keyboard and screen-reader users get the same behavior as mouse users.
Keep an activeIndex in state and render tabs and panels from arrays. Compute the indicator position from a ref to the active tab in a layout effect (useLayoutEffect / onMounted / ngAfterViewInit) and recompute on resize. Conditionally render or toggle a CSS class for the active panel instead of the hidden attribute. In Tailwind, build the indicator with an absolute gradient element and transition-transform.
Feature Tabs Showcase — 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>Feature Tabs Showcase</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#0b1120;color:#e2e8f0;padding:40px 18px;display:flex;justify-content:center}
.ft-wrap{width:100%;max-width:760px}
.ft-tabs{position:relative;display:flex;gap:4px;background:#111827;border:1px solid #1f2937;border-radius:12px;padding:5px;margin:0 auto 22px;width:fit-content}
.ft-tab{position:relative;z-index:1;border:none;background:none;font-family:inherit;font-size:13.5px;font-weight:600;color:#94a3b8;padding:9px 18px;border-radius:8px;cursor:pointer;transition:color .2s;white-space:nowrap}
.ft-tab.active{color:#fff}
.ft-ink{position:absolute;top:5px;height:calc(100% - 10px);border-radius:8px;background:linear-gradient(120deg,#6366f1,#8b5cf6);transition:transform .32s cubic-bezier(.4,0,.2,1),width .32s;z-index:0}
.ft-panels{position:relative;background:#111827;border:1px solid #1f2937;border-radius:16px;overflow:hidden;min-height:260px}
.ft-panel{display:grid;grid-template-columns:1fr 1fr;gap:22px;padding:26px;opacity:0;transform:translateY(10px);transition:opacity .3s,transform .3s}
.ft-panel.active{opacity:1;transform:none}
.ft-panel[hidden]{display:none}
.ft-copy h3{font-size:20px;font-weight:800;margin-bottom:8px}
.ft-copy p{font-size:13.5px;color:#94a3b8;line-height:1.55;margin-bottom:14px}
.ft-copy ul{list-style:none;display:flex;flex-direction:column;gap:8px}
.ft-copy li{font-size:13px;color:#cbd5e1;padding-left:22px;position:relative}
.ft-copy li::before{content:'✓';position:absolute;left:0;color:#8b5cf6;font-weight:900}
.ft-art{border-radius:12px;background:#0b1120;border:1px solid #1f2937;display:flex;align-items:center;justify-content:center;gap:10px;padding:18px;min-height:180px}
.ft-art-0{align-items:flex-end}
.ft-bar{flex:1;height:var(--h);background:linear-gradient(180deg,#8b5cf6,#6366f1);border-radius:6px 6px 0 0;animation:ftGrow .5s ease both}
@keyframes ftGrow{from{height:0}}
.ft-node{width:46px;height:46px;border-radius:12px;background:#1e293b;border:1px solid #334155;display:flex;align-items:center;justify-content:center;font-size:20px}
.ft-wire{width:26px;height:2px;background:linear-gradient(90deg,#6366f1,#8b5cf6)}
.ft-chip{background:#1e293b;border:1px solid #334155;border-radius:999px;padding:8px 14px;font-size:13px;font-weight:600;color:#c4b5fd}
.ft-chip-add{background:#6366f1;color:#fff;border-color:#6366f1}
@media(max-width:560px){.ft-panel{grid-template-columns:1fr}.ft-tabs{width:100%}.ft-tab{flex:1}}
</style>
</head>
<body>
<section class="ft-wrap">
<div class="ft-tabs" role="tablist" id="ftTabs">
<button class="ft-tab active" role="tab" aria-selected="true" data-i="0">Analytics</button>
<button class="ft-tab" role="tab" aria-selected="false" data-i="1">Automation</button>
<button class="ft-tab" role="tab" aria-selected="false" data-i="2">Collaboration</button>
<span class="ft-ink" id="ftInk"></span>
</div>
<div class="ft-panels" id="ftPanels">
<div class="ft-panel active" role="tabpanel">
<div class="ft-copy"><h3>Real-time analytics</h3><p>Watch every metric update live with charts that never need a refresh.</p><ul><li>Live event stream</li><li>Custom dashboards</li><li>Export to CSV</li></ul></div>
<div class="ft-art ft-art-0"><div class="ft-bar" style="--h:40%"></div><div class="ft-bar" style="--h:70%"></div><div class="ft-bar" style="--h:55%"></div><div class="ft-bar" style="--h:90%"></div><div class="ft-bar" style="--h:65%"></div></div>
</div>
<div class="ft-panel" role="tabpanel" hidden>
<div class="ft-copy"><h3>Workflow automation</h3><p>Chain triggers and actions so repetitive work runs itself.</p><ul><li>Visual rule builder</li><li>200+ integrations</li><li>Error retries</li></ul></div>
<div class="ft-art ft-art-1"><div class="ft-node">⚡</div><div class="ft-wire"></div><div class="ft-node">⚙</div><div class="ft-wire"></div><div class="ft-node">✓</div></div>
</div>
<div class="ft-panel" role="tabpanel" hidden>
<div class="ft-copy"><h3>Team collaboration</h3><p>Comment, mention, and resolve right where the work happens.</p><ul><li>Inline threads</li><li>Live presence</li><li>Role permissions</li></ul></div>
<div class="ft-art ft-art-2"><div class="ft-chip">@maya</div><div class="ft-chip">@devon</div><div class="ft-chip">@priya</div><div class="ft-chip ft-chip-add">+4</div></div>
</div>
</div>
</section>
<script>
var tabsWrap = document.getElementById('ftTabs');
var tabs = Array.prototype.slice.call(tabsWrap.querySelectorAll('.ft-tab'));
var ink = document.getElementById('ftInk');
var panels = Array.prototype.slice.call(document.querySelectorAll('.ft-panel'));
function moveInk(tab) {
ink.style.width = tab.offsetWidth + 'px';
ink.style.transform = 'translateX(' + (tab.offsetLeft - 5) + 'px)';
}
function activate(i) {
tabs.forEach(function (t, j) {
var on = j === i;
t.classList.toggle('active', on);
t.setAttribute('aria-selected', on ? 'true' : 'false');
});
panels.forEach(function (p, j) {
if (j === i) { p.hidden = false; requestAnimationFrame(function () { p.classList.add('active'); }); }
else { p.classList.remove('active'); p.hidden = true; }
});
moveInk(tabs[i]);
}
tabs.forEach(function (tab, i) {
tab.addEventListener('click', function () { activate(i); });
// Arrow-key navigation between tabs for accessibility.
tab.addEventListener('keydown', function (e) {
if (e.key === 'ArrowRight') { e.preventDefault(); tabs[(i + 1) % tabs.length].focus(); activate((i + 1) % tabs.length); }
if (e.key === 'ArrowLeft') { e.preventDefault(); var p = (i - 1 + tabs.length) % tabs.length; tabs[p].focus(); activate(p); }
});
});
requestAnimationFrame(function () { moveInk(tabs[0]); });
window.addEventListener('resize', function () {
var active = tabs.filter(function (t) { return t.classList.contains('active'); })[0];
moveInk(active);
});
</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>Feature Tabs Showcase</title>
<!-- Tailwind CSS v3+ — arbitrary value classes (e.g. bg-[#0f172a]) are valid -->
<script src="https://cdn.tailwindcss.com"></script>
<style>
@keyframes ftGrow{from{height:0}}
* {
box-sizing:border-box;margin:0;padding:0
}
body {
font-family:system-ui,-apple-system,sans-serif;background:#0b1120;color:#e2e8f0;padding:40px 18px;display:flex;justify-content:center
}
.ft-tab.active {
color:#fff
}
.ft-panel.active {
opacity:1;transform:none
}
.ft-panel[hidden] {
display:none
}
.ft-copy h3 {
font-size:20px;font-weight:800;margin-bottom:8px
}
.ft-copy p {
font-size:13.5px;color:#94a3b8;line-height:1.55;margin-bottom:14px
}
.ft-copy ul {
list-style:none;display:flex;flex-direction:column;gap:8px
}
.ft-copy li {
font-size:13px;color:#cbd5e1;padding-left:22px;position:relative
}
.ft-copy li::before {
content:'✓';position:absolute;left:0;color:#8b5cf6;font-weight:900
}
</style>
</head>
<body>
<section class="w-full max-w-[760px]">
<div class="relative flex gap-1 bg-[#111827] border border-[#1f2937] rounded-xl p-[5px] mt-0 mx-auto mb-[22px] w-fit max-[560px]:w-full" role="tablist" id="ftTabs">
<button class="ft-tab relative z-[1] border-0 bg-transparent font-[inherit] text-[13.5px] font-semibold text-[#94a3b8] py-[9px] px-[18px] rounded-lg cursor-pointer [transition:color_.2s] whitespace-nowrap max-[560px]:flex-1 active" role="tab" aria-selected="true" data-i="0">Analytics</button>
<button class="ft-tab relative z-[1] border-0 bg-transparent font-[inherit] text-[13.5px] font-semibold text-[#94a3b8] py-[9px] px-[18px] rounded-lg cursor-pointer [transition:color_.2s] whitespace-nowrap max-[560px]:flex-1" role="tab" aria-selected="false" data-i="1">Automation</button>
<button class="ft-tab relative z-[1] border-0 bg-transparent font-[inherit] text-[13.5px] font-semibold text-[#94a3b8] py-[9px] px-[18px] rounded-lg cursor-pointer [transition:color_.2s] whitespace-nowrap max-[560px]:flex-1" role="tab" aria-selected="false" data-i="2">Collaboration</button>
<span class="absolute top-[5px] h-[calc(100% - 10px)] rounded-lg [background:linear-gradient(120deg,#6366f1,#8b5cf6)] [transition:transform_.32s_cubic-bezier(.4,0,.2,1),width_.32s] z-0" id="ftInk"></span>
</div>
<div class="relative bg-[#111827] border border-[#1f2937] rounded-2xl overflow-hidden min-h-[260px]" id="ftPanels">
<div class="ft-panel grid grid-cols-2 gap-[22px] p-[26px] opacity-0 [transform:translateY(10px)] [transition:opacity_.3s,transform_.3s] max-[560px]:grid-cols-1 active" role="tabpanel">
<div class="ft-copy"><h3>Real-time analytics</h3><p>Watch every metric update live with charts that never need a refresh.</p><ul><li>Live event stream</li><li>Custom dashboards</li><li>Export to CSV</li></ul></div>
<div class="rounded-xl bg-[#0b1120] border border-[#1f2937] flex items-center justify-center gap-2.5 p-[18px] min-h-[180px] items-end"><div class="flex-1 h-[var(--h)] [background:linear-gradient(180deg,#8b5cf6,#6366f1)] rounded-[6px 6px 0 0] [animation:ftGrow_.5s_ease_both]" style="--h:40%"></div><div class="flex-1 h-[var(--h)] [background:linear-gradient(180deg,#8b5cf6,#6366f1)] rounded-[6px 6px 0 0] [animation:ftGrow_.5s_ease_both]" style="--h:70%"></div><div class="flex-1 h-[var(--h)] [background:linear-gradient(180deg,#8b5cf6,#6366f1)] rounded-[6px 6px 0 0] [animation:ftGrow_.5s_ease_both]" style="--h:55%"></div><div class="flex-1 h-[var(--h)] [background:linear-gradient(180deg,#8b5cf6,#6366f1)] rounded-[6px 6px 0 0] [animation:ftGrow_.5s_ease_both]" style="--h:90%"></div><div class="flex-1 h-[var(--h)] [background:linear-gradient(180deg,#8b5cf6,#6366f1)] rounded-[6px 6px 0 0] [animation:ftGrow_.5s_ease_both]" style="--h:65%"></div></div>
</div>
<div class="ft-panel grid grid-cols-2 gap-[22px] p-[26px] opacity-0 [transform:translateY(10px)] [transition:opacity_.3s,transform_.3s] max-[560px]:grid-cols-1" role="tabpanel" hidden>
<div class="ft-copy"><h3>Workflow automation</h3><p>Chain triggers and actions so repetitive work runs itself.</p><ul><li>Visual rule builder</li><li>200+ integrations</li><li>Error retries</li></ul></div>
<div class="rounded-xl bg-[#0b1120] border border-[#1f2937] flex items-center justify-center gap-2.5 p-[18px] min-h-[180px] ft-art-1"><div class="w-[46px] h-[46px] rounded-xl bg-[#1e293b] border border-[#334155] flex items-center justify-center text-xl">⚡</div><div class="w-[26px] h-0.5 [background:linear-gradient(90deg,#6366f1,#8b5cf6)]"></div><div class="w-[46px] h-[46px] rounded-xl bg-[#1e293b] border border-[#334155] flex items-center justify-center text-xl">⚙</div><div class="w-[26px] h-0.5 [background:linear-gradient(90deg,#6366f1,#8b5cf6)]"></div><div class="w-[46px] h-[46px] rounded-xl bg-[#1e293b] border border-[#334155] flex items-center justify-center text-xl">✓</div></div>
</div>
<div class="ft-panel grid grid-cols-2 gap-[22px] p-[26px] opacity-0 [transform:translateY(10px)] [transition:opacity_.3s,transform_.3s] max-[560px]:grid-cols-1" role="tabpanel" hidden>
<div class="ft-copy"><h3>Team collaboration</h3><p>Comment, mention, and resolve right where the work happens.</p><ul><li>Inline threads</li><li>Live presence</li><li>Role permissions</li></ul></div>
<div class="rounded-xl bg-[#0b1120] border border-[#1f2937] flex items-center justify-center gap-2.5 p-[18px] min-h-[180px] ft-art-2"><div class="bg-[#1e293b] border border-[#334155] rounded-[999px] py-2 px-3.5 text-[13px] font-semibold text-[#c4b5fd]">@maya</div><div class="bg-[#1e293b] border border-[#334155] rounded-[999px] py-2 px-3.5 text-[13px] font-semibold text-[#c4b5fd]">@devon</div><div class="bg-[#1e293b] border border-[#334155] rounded-[999px] py-2 px-3.5 text-[13px] font-semibold text-[#c4b5fd]">@priya</div><div class="bg-[#1e293b] border border-[#334155] rounded-[999px] py-2 px-3.5 text-[13px] font-semibold text-[#c4b5fd] bg-[#6366f1] text-[#fff] border-[#6366f1]">+4</div></div>
</div>
</div>
</section>
<script>
var tabsWrap = document.getElementById('ftTabs');
var tabs = Array.prototype.slice.call(tabsWrap.querySelectorAll('.ft-tab'));
var ink = document.getElementById('ftInk');
var panels = Array.prototype.slice.call(document.querySelectorAll('.ft-panel'));
function moveInk(tab) {
ink.style.width = tab.offsetWidth + 'px';
ink.style.transform = 'translateX(' + (tab.offsetLeft - 5) + 'px)';
}
function activate(i) {
tabs.forEach(function (t, j) {
var on = j === i;
t.classList.toggle('active', on);
t.setAttribute('aria-selected', on ? 'true' : 'false');
});
panels.forEach(function (p, j) {
if (j === i) { p.hidden = false; requestAnimationFrame(function () { p.classList.add('active'); }); }
else { p.classList.remove('active'); p.hidden = true; }
});
moveInk(tabs[i]);
}
tabs.forEach(function (tab, i) {
tab.addEventListener('click', function () { activate(i); });
// Arrow-key navigation between tabs for accessibility.
tab.addEventListener('keydown', function (e) {
if (e.key === 'ArrowRight') { e.preventDefault(); tabs[(i + 1) % tabs.length].focus(); activate((i + 1) % tabs.length); }
if (e.key === 'ArrowLeft') { e.preventDefault(); var p = (i - 1 + tabs.length) % tabs.length; tabs[p].focus(); activate(p); }
});
});
requestAnimationFrame(function () { moveInk(tabs[0]); });
window.addEventListener('resize', function () {
var active = tabs.filter(function (t) { return t.classList.contains('active'); })[0];
moveInk(active);
});
</script>
</body>
</html>import React, { useEffect } from 'react';
// CSS — optionally move to FeatureTabsShowcase.module.css
const css = `
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#0b1120;color:#e2e8f0;padding:40px 18px;display:flex;justify-content:center}
.ft-wrap{width:100%;max-width:760px}
.ft-tabs{position:relative;display:flex;gap:4px;background:#111827;border:1px solid #1f2937;border-radius:12px;padding:5px;margin:0 auto 22px;width:fit-content}
.ft-tab{position:relative;z-index:1;border:none;background:none;font-family:inherit;font-size:13.5px;font-weight:600;color:#94a3b8;padding:9px 18px;border-radius:8px;cursor:pointer;transition:color .2s;white-space:nowrap}
.ft-tab.active{color:#fff}
.ft-ink{position:absolute;top:5px;height:calc(100% - 10px);border-radius:8px;background:linear-gradient(120deg,#6366f1,#8b5cf6);transition:transform .32s cubic-bezier(.4,0,.2,1),width .32s;z-index:0}
.ft-panels{position:relative;background:#111827;border:1px solid #1f2937;border-radius:16px;overflow:hidden;min-height:260px}
.ft-panel{display:grid;grid-template-columns:1fr 1fr;gap:22px;padding:26px;opacity:0;transform:translateY(10px);transition:opacity .3s,transform .3s}
.ft-panel.active{opacity:1;transform:none}
.ft-panel[hidden]{display:none}
.ft-copy h3{font-size:20px;font-weight:800;margin-bottom:8px}
.ft-copy p{font-size:13.5px;color:#94a3b8;line-height:1.55;margin-bottom:14px}
.ft-copy ul{list-style:none;display:flex;flex-direction:column;gap:8px}
.ft-copy li{font-size:13px;color:#cbd5e1;padding-left:22px;position:relative}
.ft-copy li::before{content:'✓';position:absolute;left:0;color:#8b5cf6;font-weight:900}
.ft-art{border-radius:12px;background:#0b1120;border:1px solid #1f2937;display:flex;align-items:center;justify-content:center;gap:10px;padding:18px;min-height:180px}
.ft-art-0{align-items:flex-end}
.ft-bar{flex:1;height:var(--h);background:linear-gradient(180deg,#8b5cf6,#6366f1);border-radius:6px 6px 0 0;animation:ftGrow .5s ease both}
@keyframes ftGrow{from{height:0}}
.ft-node{width:46px;height:46px;border-radius:12px;background:#1e293b;border:1px solid #334155;display:flex;align-items:center;justify-content:center;font-size:20px}
.ft-wire{width:26px;height:2px;background:linear-gradient(90deg,#6366f1,#8b5cf6)}
.ft-chip{background:#1e293b;border:1px solid #334155;border-radius:999px;padding:8px 14px;font-size:13px;font-weight:600;color:#c4b5fd}
.ft-chip-add{background:#6366f1;color:#fff;border-color:#6366f1}
@media(max-width:560px){.ft-panel{grid-template-columns:1fr}.ft-tabs{width:100%}.ft-tab{flex:1}}
`;
export default function FeatureTabsShowcase() {
// 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);
};
var tabsWrap = document.getElementById('ftTabs');
var tabs = Array.prototype.slice.call(tabsWrap.querySelectorAll('.ft-tab'));
var ink = document.getElementById('ftInk');
var panels = Array.prototype.slice.call(document.querySelectorAll('.ft-panel'));
function moveInk(tab) {
ink.style.width = tab.offsetWidth + 'px';
ink.style.transform = 'translateX(' + (tab.offsetLeft - 5) + 'px)';
}
function activate(i) {
tabs.forEach(function (t, j) {
var on = j === i;
t.classList.toggle('active', on);
t.setAttribute('aria-selected', on ? 'true' : 'false');
});
panels.forEach(function (p, j) {
if (j === i) { p.hidden = false; requestAnimationFrame(function () { p.classList.add('active'); }); }
else { p.classList.remove('active'); p.hidden = true; }
});
moveInk(tabs[i]);
}
tabs.forEach(function (tab, i) {
tab.addEventListener('click', function () { activate(i); });
// Arrow-key navigation between tabs for accessibility.
tab.addEventListener('keydown', function (e) {
if (e.key === 'ArrowRight') { e.preventDefault(); tabs[(i + 1) % tabs.length].focus(); activate((i + 1) % tabs.length); }
if (e.key === 'ArrowLeft') { e.preventDefault(); var p = (i - 1 + tabs.length) % tabs.length; tabs[p].focus(); activate(p); }
});
});
requestAnimationFrame(function () { moveInk(tabs[0]); });
window.addEventListener('resize', function () {
var active = tabs.filter(function (t) { return t.classList.contains('active'); })[0];
moveInk(active);
});
// 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>
<section className="ft-wrap">
<div className="ft-tabs" role="tablist" id="ftTabs">
<button className="ft-tab active" role="tab" aria-selected="true" data-i="0">Analytics</button>
<button className="ft-tab" role="tab" aria-selected="false" data-i="1">Automation</button>
<button className="ft-tab" role="tab" aria-selected="false" data-i="2">Collaboration</button>
<span className="ft-ink" id="ftInk"></span>
</div>
<div className="ft-panels" id="ftPanels">
<div className="ft-panel active" role="tabpanel">
<div className="ft-copy"><h3>Real-time analytics</h3><p>Watch every metric update live with charts that never need a refresh.</p><ul><li>Live event stream</li><li>Custom dashboards</li><li>Export to CSV</li></ul></div>
<div className="ft-art ft-art-0"><div className="ft-bar" style={{ '--h': '40%' }}></div><div className="ft-bar" style={{ '--h': '70%' }}></div><div className="ft-bar" style={{ '--h': '55%' }}></div><div className="ft-bar" style={{ '--h': '90%' }}></div><div className="ft-bar" style={{ '--h': '65%' }}></div></div>
</div>
<div className="ft-panel" role="tabpanel" hidden>
<div className="ft-copy"><h3>Workflow automation</h3><p>Chain triggers and actions so repetitive work runs itself.</p><ul><li>Visual rule builder</li><li>200+ integrations</li><li>Error retries</li></ul></div>
<div className="ft-art ft-art-1"><div className="ft-node">⚡</div><div className="ft-wire"></div><div className="ft-node">⚙</div><div className="ft-wire"></div><div className="ft-node">✓</div></div>
</div>
<div className="ft-panel" role="tabpanel" hidden>
<div className="ft-copy"><h3>Team collaboration</h3><p>Comment, mention, and resolve right where the work happens.</p><ul><li>Inline threads</li><li>Live presence</li><li>Role permissions</li></ul></div>
<div className="ft-art ft-art-2"><div className="ft-chip">@maya</div><div className="ft-chip">@devon</div><div className="ft-chip">@priya</div><div className="ft-chip ft-chip-add">+4</div></div>
</div>
</div>
</section>
</>
);
}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 FeatureTabsShowcase() {
// 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);
};
var tabsWrap = document.getElementById('ftTabs');
var tabs = Array.prototype.slice.call(tabsWrap.querySelectorAll('.ft-tab'));
var ink = document.getElementById('ftInk');
var panels = Array.prototype.slice.call(document.querySelectorAll('.ft-panel'));
function moveInk(tab) {
ink.style.width = tab.offsetWidth + 'px';
ink.style.transform = 'translateX(' + (tab.offsetLeft - 5) + 'px)';
}
function activate(i) {
tabs.forEach(function (t, j) {
var on = j === i;
t.classList.toggle('active', on);
t.setAttribute('aria-selected', on ? 'true' : 'false');
});
panels.forEach(function (p, j) {
if (j === i) { p.hidden = false; requestAnimationFrame(function () { p.classList.add('active'); }); }
else { p.classList.remove('active'); p.hidden = true; }
});
moveInk(tabs[i]);
}
tabs.forEach(function (tab, i) {
tab.addEventListener('click', function () { activate(i); });
// Arrow-key navigation between tabs for accessibility.
tab.addEventListener('keydown', function (e) {
if (e.key === 'ArrowRight') { e.preventDefault(); tabs[(i + 1) % tabs.length].focus(); activate((i + 1) % tabs.length); }
if (e.key === 'ArrowLeft') { e.preventDefault(); var p = (i - 1 + tabs.length) % tabs.length; tabs[p].focus(); activate(p); }
});
});
requestAnimationFrame(function () { moveInk(tabs[0]); });
window.addEventListener('resize', function () {
var active = tabs.filter(function (t) { return t.classList.contains('active'); })[0];
moveInk(active);
});
// 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>{`
@keyframes ftGrow{from{height:0}}
* {
box-sizing:border-box;margin:0;padding:0
}
body {
font-family:system-ui,-apple-system,sans-serif;background:#0b1120;color:#e2e8f0;padding:40px 18px;display:flex;justify-content:center
}
.ft-tab.active {
color:#fff
}
.ft-panel.active {
opacity:1;transform:none
}
.ft-panel[hidden] {
display:none
}
.ft-copy h3 {
font-size:20px;font-weight:800;margin-bottom:8px
}
.ft-copy p {
font-size:13.5px;color:#94a3b8;line-height:1.55;margin-bottom:14px
}
.ft-copy ul {
list-style:none;display:flex;flex-direction:column;gap:8px
}
.ft-copy li {
font-size:13px;color:#cbd5e1;padding-left:22px;position:relative
}
.ft-copy li::before {
content:'✓';position:absolute;left:0;color:#8b5cf6;font-weight:900
}
`}</style>
<section className="w-full max-w-[760px]">
<div className="relative flex gap-1 bg-[#111827] border border-[#1f2937] rounded-xl p-[5px] mt-0 mx-auto mb-[22px] w-fit max-[560px]:w-full" role="tablist" id="ftTabs">
<button className="ft-tab relative z-[1] border-0 bg-transparent font-[inherit] text-[13.5px] font-semibold text-[#94a3b8] py-[9px] px-[18px] rounded-lg cursor-pointer [transition:color_.2s] whitespace-nowrap max-[560px]:flex-1 active" role="tab" aria-selected="true" data-i="0">Analytics</button>
<button className="ft-tab relative z-[1] border-0 bg-transparent font-[inherit] text-[13.5px] font-semibold text-[#94a3b8] py-[9px] px-[18px] rounded-lg cursor-pointer [transition:color_.2s] whitespace-nowrap max-[560px]:flex-1" role="tab" aria-selected="false" data-i="1">Automation</button>
<button className="ft-tab relative z-[1] border-0 bg-transparent font-[inherit] text-[13.5px] font-semibold text-[#94a3b8] py-[9px] px-[18px] rounded-lg cursor-pointer [transition:color_.2s] whitespace-nowrap max-[560px]:flex-1" role="tab" aria-selected="false" data-i="2">Collaboration</button>
<span className="absolute top-[5px] h-[calc(100% - 10px)] rounded-lg [background:linear-gradient(120deg,#6366f1,#8b5cf6)] [transition:transform_.32s_cubic-bezier(.4,0,.2,1),width_.32s] z-0" id="ftInk"></span>
</div>
<div className="relative bg-[#111827] border border-[#1f2937] rounded-2xl overflow-hidden min-h-[260px]" id="ftPanels">
<div className="ft-panel grid grid-cols-2 gap-[22px] p-[26px] opacity-0 [transform:translateY(10px)] [transition:opacity_.3s,transform_.3s] max-[560px]:grid-cols-1 active" role="tabpanel">
<div className="ft-copy"><h3>Real-time analytics</h3><p>Watch every metric update live with charts that never need a refresh.</p><ul><li>Live event stream</li><li>Custom dashboards</li><li>Export to CSV</li></ul></div>
<div className="rounded-xl bg-[#0b1120] border border-[#1f2937] flex items-center justify-center gap-2.5 p-[18px] min-h-[180px] items-end"><div className="flex-1 h-[var(--h)] [background:linear-gradient(180deg,#8b5cf6,#6366f1)] rounded-[6px 6px 0 0] [animation:ftGrow_.5s_ease_both]" style={{ '--h': '40%' }}></div><div className="flex-1 h-[var(--h)] [background:linear-gradient(180deg,#8b5cf6,#6366f1)] rounded-[6px 6px 0 0] [animation:ftGrow_.5s_ease_both]" style={{ '--h': '70%' }}></div><div className="flex-1 h-[var(--h)] [background:linear-gradient(180deg,#8b5cf6,#6366f1)] rounded-[6px 6px 0 0] [animation:ftGrow_.5s_ease_both]" style={{ '--h': '55%' }}></div><div className="flex-1 h-[var(--h)] [background:linear-gradient(180deg,#8b5cf6,#6366f1)] rounded-[6px 6px 0 0] [animation:ftGrow_.5s_ease_both]" style={{ '--h': '90%' }}></div><div className="flex-1 h-[var(--h)] [background:linear-gradient(180deg,#8b5cf6,#6366f1)] rounded-[6px 6px 0 0] [animation:ftGrow_.5s_ease_both]" style={{ '--h': '65%' }}></div></div>
</div>
<div className="ft-panel grid grid-cols-2 gap-[22px] p-[26px] opacity-0 [transform:translateY(10px)] [transition:opacity_.3s,transform_.3s] max-[560px]:grid-cols-1" role="tabpanel" hidden>
<div className="ft-copy"><h3>Workflow automation</h3><p>Chain triggers and actions so repetitive work runs itself.</p><ul><li>Visual rule builder</li><li>200+ integrations</li><li>Error retries</li></ul></div>
<div className="rounded-xl bg-[#0b1120] border border-[#1f2937] flex items-center justify-center gap-2.5 p-[18px] min-h-[180px] ft-art-1"><div className="w-[46px] h-[46px] rounded-xl bg-[#1e293b] border border-[#334155] flex items-center justify-center text-xl">⚡</div><div className="w-[26px] h-0.5 [background:linear-gradient(90deg,#6366f1,#8b5cf6)]"></div><div className="w-[46px] h-[46px] rounded-xl bg-[#1e293b] border border-[#334155] flex items-center justify-center text-xl">⚙</div><div className="w-[26px] h-0.5 [background:linear-gradient(90deg,#6366f1,#8b5cf6)]"></div><div className="w-[46px] h-[46px] rounded-xl bg-[#1e293b] border border-[#334155] flex items-center justify-center text-xl">✓</div></div>
</div>
<div className="ft-panel grid grid-cols-2 gap-[22px] p-[26px] opacity-0 [transform:translateY(10px)] [transition:opacity_.3s,transform_.3s] max-[560px]:grid-cols-1" role="tabpanel" hidden>
<div className="ft-copy"><h3>Team collaboration</h3><p>Comment, mention, and resolve right where the work happens.</p><ul><li>Inline threads</li><li>Live presence</li><li>Role permissions</li></ul></div>
<div className="rounded-xl bg-[#0b1120] border border-[#1f2937] flex items-center justify-center gap-2.5 p-[18px] min-h-[180px] ft-art-2"><div className="bg-[#1e293b] border border-[#334155] rounded-[999px] py-2 px-3.5 text-[13px] font-semibold text-[#c4b5fd]">@maya</div><div className="bg-[#1e293b] border border-[#334155] rounded-[999px] py-2 px-3.5 text-[13px] font-semibold text-[#c4b5fd]">@devon</div><div className="bg-[#1e293b] border border-[#334155] rounded-[999px] py-2 px-3.5 text-[13px] font-semibold text-[#c4b5fd]">@priya</div><div className="bg-[#1e293b] border border-[#334155] rounded-[999px] py-2 px-3.5 text-[13px] font-semibold text-[#c4b5fd] bg-[#6366f1] text-[#fff] border-[#6366f1]">+4</div></div>
</div>
</div>
</section>
</>
);
}<template>
<section class="ft-wrap">
<div class="ft-tabs" role="tablist" id="ftTabs">
<button class="ft-tab active" role="tab" aria-selected="true" data-i="0">Analytics</button>
<button class="ft-tab" role="tab" aria-selected="false" data-i="1">Automation</button>
<button class="ft-tab" role="tab" aria-selected="false" data-i="2">Collaboration</button>
<span class="ft-ink" id="ftInk"></span>
</div>
<div class="ft-panels" id="ftPanels">
<div class="ft-panel active" role="tabpanel">
<div class="ft-copy"><h3>Real-time analytics</h3><p>Watch every metric update live with charts that never need a refresh.</p><ul><li>Live event stream</li><li>Custom dashboards</li><li>Export to CSV</li></ul></div>
<div class="ft-art ft-art-0"><div class="ft-bar" style="--h:40%"></div><div class="ft-bar" style="--h:70%"></div><div class="ft-bar" style="--h:55%"></div><div class="ft-bar" style="--h:90%"></div><div class="ft-bar" style="--h:65%"></div></div>
</div>
<div class="ft-panel" role="tabpanel" hidden>
<div class="ft-copy"><h3>Workflow automation</h3><p>Chain triggers and actions so repetitive work runs itself.</p><ul><li>Visual rule builder</li><li>200+ integrations</li><li>Error retries</li></ul></div>
<div class="ft-art ft-art-1"><div class="ft-node">⚡</div><div class="ft-wire"></div><div class="ft-node">⚙</div><div class="ft-wire"></div><div class="ft-node">✓</div></div>
</div>
<div class="ft-panel" role="tabpanel" hidden>
<div class="ft-copy"><h3>Team collaboration</h3><p>Comment, mention, and resolve right where the work happens.</p><ul><li>Inline threads</li><li>Live presence</li><li>Role permissions</li></ul></div>
<div class="ft-art ft-art-2"><div class="ft-chip">@maya</div><div class="ft-chip">@devon</div><div class="ft-chip">@priya</div><div class="ft-chip ft-chip-add">+4</div></div>
</div>
</div>
</section>
</template>
<script setup>
import { onMounted } from 'vue';
let tabsWrap;
let tabs;
let ink;
var panels = Array.prototype.slice.call(document.querySelectorAll('.ft-panel'));
function moveInk(tab) {
ink.style.width = tab.offsetWidth + 'px';
ink.style.transform = 'translateX(' + (tab.offsetLeft - 5) + 'px)';
}
function activate(i) {
tabs.forEach(function (t, j) {
var on = j === i;
t.classList.toggle('active', on);
t.setAttribute('aria-selected', on ? 'true' : 'false');
});
panels.forEach(function (p, j) {
if (j === i) { p.hidden = false; requestAnimationFrame(function () { p.classList.add('active'); }); }
else { p.classList.remove('active'); p.hidden = true; }
});
moveInk(tabs[i]);
}
onMounted(() => {
tabsWrap = document.getElementById('ftTabs');
tabs = Array.prototype.slice.call(tabsWrap.querySelectorAll('.ft-tab'));
ink = document.getElementById('ftInk');
tabs.forEach(function (tab, i) {
tab.addEventListener('click', function () { activate(i); });
// Arrow-key navigation between tabs for accessibility.
tab.addEventListener('keydown', function (e) {
if (e.key === 'ArrowRight') { e.preventDefault(); tabs[(i + 1) % tabs.length].focus(); activate((i + 1) % tabs.length); }
if (e.key === 'ArrowLeft') { e.preventDefault(); var p = (i - 1 + tabs.length) % tabs.length; tabs[p].focus(); activate(p); }
});
});
requestAnimationFrame(function () { moveInk(tabs[0]); });
window.addEventListener('resize', function () {
var active = tabs.filter(function (t) { return t.classList.contains('active'); })[0];
moveInk(active);
});
});
</script>
<style scoped>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#0b1120;color:#e2e8f0;padding:40px 18px;display:flex;justify-content:center}
.ft-wrap{width:100%;max-width:760px}
.ft-tabs{position:relative;display:flex;gap:4px;background:#111827;border:1px solid #1f2937;border-radius:12px;padding:5px;margin:0 auto 22px;width:fit-content}
.ft-tab{position:relative;z-index:1;border:none;background:none;font-family:inherit;font-size:13.5px;font-weight:600;color:#94a3b8;padding:9px 18px;border-radius:8px;cursor:pointer;transition:color .2s;white-space:nowrap}
.ft-tab.active{color:#fff}
.ft-ink{position:absolute;top:5px;height:calc(100% - 10px);border-radius:8px;background:linear-gradient(120deg,#6366f1,#8b5cf6);transition:transform .32s cubic-bezier(.4,0,.2,1),width .32s;z-index:0}
.ft-panels{position:relative;background:#111827;border:1px solid #1f2937;border-radius:16px;overflow:hidden;min-height:260px}
.ft-panel{display:grid;grid-template-columns:1fr 1fr;gap:22px;padding:26px;opacity:0;transform:translateY(10px);transition:opacity .3s,transform .3s}
.ft-panel.active{opacity:1;transform:none}
.ft-panel[hidden]{display:none}
.ft-copy h3{font-size:20px;font-weight:800;margin-bottom:8px}
.ft-copy p{font-size:13.5px;color:#94a3b8;line-height:1.55;margin-bottom:14px}
.ft-copy ul{list-style:none;display:flex;flex-direction:column;gap:8px}
.ft-copy li{font-size:13px;color:#cbd5e1;padding-left:22px;position:relative}
.ft-copy li::before{content:'✓';position:absolute;left:0;color:#8b5cf6;font-weight:900}
.ft-art{border-radius:12px;background:#0b1120;border:1px solid #1f2937;display:flex;align-items:center;justify-content:center;gap:10px;padding:18px;min-height:180px}
.ft-art-0{align-items:flex-end}
.ft-bar{flex:1;height:var(--h);background:linear-gradient(180deg,#8b5cf6,#6366f1);border-radius:6px 6px 0 0;animation:ftGrow .5s ease both}
@keyframes ftGrow{from{height:0}}
.ft-node{width:46px;height:46px;border-radius:12px;background:#1e293b;border:1px solid #334155;display:flex;align-items:center;justify-content:center;font-size:20px}
.ft-wire{width:26px;height:2px;background:linear-gradient(90deg,#6366f1,#8b5cf6)}
.ft-chip{background:#1e293b;border:1px solid #334155;border-radius:999px;padding:8px 14px;font-size:13px;font-weight:600;color:#c4b5fd}
.ft-chip-add{background:#6366f1;color:#fff;border-color:#6366f1}
@media(max-width:560px){.ft-panel{grid-template-columns:1fr}.ft-tabs{width:100%}.ft-tab{flex:1}}
</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-feature-tabs-showcase',
standalone: true,
imports: [CommonModule],
encapsulation: ViewEncapsulation.None,
template: `
<section class="ft-wrap">
<div class="ft-tabs" role="tablist" id="ftTabs">
<button class="ft-tab active" role="tab" aria-selected="true" data-i="0">Analytics</button>
<button class="ft-tab" role="tab" aria-selected="false" data-i="1">Automation</button>
<button class="ft-tab" role="tab" aria-selected="false" data-i="2">Collaboration</button>
<span class="ft-ink" id="ftInk"></span>
</div>
<div class="ft-panels" id="ftPanels">
<div class="ft-panel active" role="tabpanel">
<div class="ft-copy"><h3>Real-time analytics</h3><p>Watch every metric update live with charts that never need a refresh.</p><ul><li>Live event stream</li><li>Custom dashboards</li><li>Export to CSV</li></ul></div>
<div class="ft-art ft-art-0"><div class="ft-bar" style="--h:40%"></div><div class="ft-bar" style="--h:70%"></div><div class="ft-bar" style="--h:55%"></div><div class="ft-bar" style="--h:90%"></div><div class="ft-bar" style="--h:65%"></div></div>
</div>
<div class="ft-panel" role="tabpanel" hidden>
<div class="ft-copy"><h3>Workflow automation</h3><p>Chain triggers and actions so repetitive work runs itself.</p><ul><li>Visual rule builder</li><li>200+ integrations</li><li>Error retries</li></ul></div>
<div class="ft-art ft-art-1"><div class="ft-node">⚡</div><div class="ft-wire"></div><div class="ft-node">⚙</div><div class="ft-wire"></div><div class="ft-node">✓</div></div>
</div>
<div class="ft-panel" role="tabpanel" hidden>
<div class="ft-copy"><h3>Team collaboration</h3><p>Comment, mention, and resolve right where the work happens.</p><ul><li>Inline threads</li><li>Live presence</li><li>Role permissions</li></ul></div>
<div class="ft-art ft-art-2"><div class="ft-chip">@maya</div><div class="ft-chip">@devon</div><div class="ft-chip">@priya</div><div class="ft-chip ft-chip-add">+4</div></div>
</div>
</div>
</section>
`,
styles: [`
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#0b1120;color:#e2e8f0;padding:40px 18px;display:flex;justify-content:center}
.ft-wrap{width:100%;max-width:760px}
.ft-tabs{position:relative;display:flex;gap:4px;background:#111827;border:1px solid #1f2937;border-radius:12px;padding:5px;margin:0 auto 22px;width:fit-content}
.ft-tab{position:relative;z-index:1;border:none;background:none;font-family:inherit;font-size:13.5px;font-weight:600;color:#94a3b8;padding:9px 18px;border-radius:8px;cursor:pointer;transition:color .2s;white-space:nowrap}
.ft-tab.active{color:#fff}
.ft-ink{position:absolute;top:5px;height:calc(100% - 10px);border-radius:8px;background:linear-gradient(120deg,#6366f1,#8b5cf6);transition:transform .32s cubic-bezier(.4,0,.2,1),width .32s;z-index:0}
.ft-panels{position:relative;background:#111827;border:1px solid #1f2937;border-radius:16px;overflow:hidden;min-height:260px}
.ft-panel{display:grid;grid-template-columns:1fr 1fr;gap:22px;padding:26px;opacity:0;transform:translateY(10px);transition:opacity .3s,transform .3s}
.ft-panel.active{opacity:1;transform:none}
.ft-panel[hidden]{display:none}
.ft-copy h3{font-size:20px;font-weight:800;margin-bottom:8px}
.ft-copy p{font-size:13.5px;color:#94a3b8;line-height:1.55;margin-bottom:14px}
.ft-copy ul{list-style:none;display:flex;flex-direction:column;gap:8px}
.ft-copy li{font-size:13px;color:#cbd5e1;padding-left:22px;position:relative}
.ft-copy li::before{content:'✓';position:absolute;left:0;color:#8b5cf6;font-weight:900}
.ft-art{border-radius:12px;background:#0b1120;border:1px solid #1f2937;display:flex;align-items:center;justify-content:center;gap:10px;padding:18px;min-height:180px}
.ft-art-0{align-items:flex-end}
.ft-bar{flex:1;height:var(--h);background:linear-gradient(180deg,#8b5cf6,#6366f1);border-radius:6px 6px 0 0;animation:ftGrow .5s ease both}
@keyframes ftGrow{from{height:0}}
.ft-node{width:46px;height:46px;border-radius:12px;background:#1e293b;border:1px solid #334155;display:flex;align-items:center;justify-content:center;font-size:20px}
.ft-wire{width:26px;height:2px;background:linear-gradient(90deg,#6366f1,#8b5cf6)}
.ft-chip{background:#1e293b;border:1px solid #334155;border-radius:999px;padding:8px 14px;font-size:13px;font-weight:600;color:#c4b5fd}
.ft-chip-add{background:#6366f1;color:#fff;border-color:#6366f1}
@media(max-width:560px){.ft-panel{grid-template-columns:1fr}.ft-tabs{width:100%}.ft-tab{flex:1}}
`]
})
export class FeatureTabsShowcaseComponent implements AfterViewInit {
ngAfterViewInit(): void {
var tabsWrap = document.getElementById('ftTabs');
var tabs = Array.prototype.slice.call(tabsWrap.querySelectorAll('.ft-tab'));
var ink = document.getElementById('ftInk');
var panels = Array.prototype.slice.call(document.querySelectorAll('.ft-panel'));
function moveInk(tab) {
ink.style.width = tab.offsetWidth + 'px';
ink.style.transform = 'translateX(' + (tab.offsetLeft - 5) + 'px)';
}
function activate(i) {
tabs.forEach(function (t, j) {
var on = j === i;
t.classList.toggle('active', on);
t.setAttribute('aria-selected', on ? 'true' : 'false');
});
panels.forEach(function (p, j) {
if (j === i) { p.hidden = false; requestAnimationFrame(function () { p.classList.add('active'); }); }
else { p.classList.remove('active'); p.hidden = true; }
});
moveInk(tabs[i]);
}
tabs.forEach(function (tab, i) {
tab.addEventListener('click', function () { activate(i); });
// Arrow-key navigation between tabs for accessibility.
tab.addEventListener('keydown', function (e) {
if (e.key === 'ArrowRight') { e.preventDefault(); tabs[(i + 1) % tabs.length].focus(); activate((i + 1) % tabs.length); }
if (e.key === 'ArrowLeft') { e.preventDefault(); var p = (i - 1 + tabs.length) % tabs.length; tabs[p].focus(); activate(p); }
});
});
requestAnimationFrame(function () { moveInk(tabs[0]); });
window.addEventListener('resize', function () {
var active = tabs.filter(function (t) { return t.classList.contains('active'); })[0];
moveInk(active);
});
// Bind inline-handler functions to the component so the template can call them
Object.assign(this, { moveInk, activate });
}
}