Build a page-level tab bar with Tailwind CSS where the active underline slides. Wrap a max-w-4xl container: above the tabs, a small page title row with a heading and a muted record count. The tab strip is a relative flex row over a hairline bottom border with six tab buttons in small medium-weight text, plus an absolutely positioned two-pixel neutral-900 bar at the bottom that is moved and resized rather than re-rendered. Under the strip, a panel area showing a short paragraph and a bordered placeholder block for the active tab. Then add JavaScript that measures the active button offset and width and applies them to the indicator via inline left and width styles with a transition, switches the panel copy, keeps the indicator correct on window resize, and supports left and right arrow keys for moving between tabs.
Paste it into Claude, Cursor, v0 or any AI coding tool. Prefer the
finished markup? .
Build a page-level tab bar with Tailwind CSS where the active underline slides. Wrap a max-w-4xl container: above the tabs, a small page title row with a heading and a muted record count. The tab strip is a relative flex row over a hairline bottom border with six tab buttons in small medium-weight text, plus an absolutely positioned two-pixel neutral-900 bar at the bottom that is moved and resized rather than re-rendered. Under the strip, a panel area showing a short paragraph and a bordered placeholder block for the active tab. Then add JavaScript that measures the active button offset and width and applies them to the indicator via inline left and width styles with a transition, switches the panel copy, keeps the indicator correct on window resize, and supports left and right arrow keys for moving between tabs.
var COPY = {
all: "Every payment captured in the last 30 days, newest first.",
succeeded: "Payments that settled cleanly. Funds land in the next payout run.",
refunded: "Full and partial refunds, including the original charge reference.",
disputed: "Chargebacks awaiting evidence. The clock in each row is the bank deadline.",
failed: "Declines grouped by reason code so retries can be targeted.",
payouts: "Batches sent to your bank account, with the fee breakdown per batch.",
};
var tabs = Array.prototype.slice.call(document.querySelectorAll(".tab-btn"));
var indicator = document.getElementById("tabIndicator");
var copy = document.getElementById("tabCopy");
var index = 0;
function activate(i) {
index = i;
var tab = tabs[i];
tabs.forEach(function (t) {
var on = t === tab;
t.classList.toggle("text-neutral-900", on);
t.classList.toggle("text-neutral-500", !on);
});
// Move and resize one element rather than repainting six borders.
indicator.style.left = tab.offsetLeft + "px";
indicator.style.width = tab.offsetWidth + "px";
copy.textContent = COPY[tab.dataset.panel];
}
tabs.forEach(function (tab, i) {
tab.addEventListener("click", function () {
activate(i);
});
});
document.addEventListener("keydown", function (e) {
if (e.key !== "ArrowRight" && e.key !== "ArrowLeft") return;
if (tabs.indexOf(document.activeElement) === -1) return;
e.preventDefault();
var next = e.key === "ArrowRight" ? (index + 1) % tabs.length : (index - 1 + tabs.length) % tabs.length;
tabs[next].focus();
activate(next);
});
window.addEventListener("resize", function () {
activate(index);
});
activate(0);