04 / 18
All 18 in 18 Tailwind CSS Settings Pages
- 01Tailwind Account Settings Page Layout
- 02Tailwind Billing and Plan Settings Page
- 03Tailwind Usage Page with Quota Meters
- 04Tailwind Invoice History Table
- 05Tailwind Payment Method Settings Card
- 06Tailwind Team Members Page with Roles
- 07Tailwind Invite Teammates Form
- 08Tailwind Roles and Permissions Matrix
- 09Tailwind API Keys Management Page
- 10Tailwind Two-Factor Authentication Setup
- 11Tailwind Active Sessions Settings Page
- 12Tailwind Notification Preferences Page
- 13Tailwind Connected Apps Settings Page
- 14Tailwind Appearance Settings Page
- 15Tailwind Data Export Request Page
- 16Tailwind Audit Log Activity Page
- 17Tailwind Webhooks Settings Page
- 18Tailwind Danger Zone Delete Account
Tailwind Invoice History Table
A billing-history table with zero-JS status filter chips, row selection that tints the row and raises a bulk-action bar, a native indeterminate select-all, status pills for Paid, Open, Failed and Refunded, and per-row download buttons that cycle idle → generating → done before handing over a real file.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section data-tsp04 class="tsp-04 group/inv w-full min-h-svh bg-tsp04-bg px-4 py-10 font-tsp04-sans text-tsp04-ink antialiased sm:px-8 sm:py-14" aria-labelledby="tsp-04-h">
<div class="mx-auto grid w-full max-w-6xl gap-6">
<header class="flex flex-wrap items-end justify-between gap-4">
<div><h1 id="tsp-04-h" class="text-3xl font-bold tracking-tight">Invoices</h1><p class="mt-1 text-tsp04-mut">Sent to <strong class="font-semibold text-tsp04-ink">billing@northwind.co</strong> · <a href="#tsp-04-h" class="font-semibold text-tsp04-acc underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-tsp04-acc">Change</a></p></div>
<label class="grid gap-1 text-sm"><span class="sr-only">Year</span><select class="min-h-11 rounded-xl border border-tsp04-line bg-white px-3 text-sm font-medium shadow-xs outline-none focus:ring-4 focus:ring-tsp04-acc/15"><option>2026</option><option>2025</option><option>2024</option></select></label>
</header>
<div role="alert" class="flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-800">
<p class="flex items-center gap-2"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 flex-none"><circle cx="12" cy="12" r="9"/><path d="M12 8v4M12 16h.01"/></svg><span><strong class="font-semibold">INV-2026-008 failed.</strong> Visa ending 4242 was declined on Aug 1.</span></p>
<a href="#tsp-04-h" class="inline-flex min-h-11 items-center rounded-xl bg-rose-600 px-4 font-semibold text-white transition hover:bg-rose-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-rose-600">Update payment method</a>
</div>
<div class="flex flex-wrap items-center justify-between gap-3">
<fieldset class="flex flex-wrap gap-2">
<legend class="sr-only">Filter by status</legend>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-all" class="sr-only" checked>All <span class="tabular-nums opacity-60">8</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-paid" class="sr-only"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid <span class="tabular-nums opacity-60">5</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-open" class="sr-only"><span class="size-1.5 rounded-full bg-amber-500"></span>Open <span class="tabular-nums opacity-60">1</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-failed" class="sr-only"><span class="size-1.5 rounded-full bg-rose-500"></span>Failed <span class="tabular-nums opacity-60">1</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-refunded" class="sr-only"><span class="size-1.5 rounded-full bg-zinc-400"></span>Refunded <span class="tabular-nums opacity-60">1</span></label>
</fieldset>
<div class="hidden items-center gap-3 rounded-full bg-tsp04-acc/10 py-1 pl-4 pr-1 text-sm font-semibold text-tsp04-acc group-has-[[data-row-check]:checked]/inv:flex" aria-live="polite"><span data-sel-count>0 selected</span><button type="button" data-bulk class="inline-flex min-h-10 items-center gap-2 rounded-full bg-tsp04-acc px-4 text-white transition hover:bg-tsp04-acc/90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tsp04-acc">Download selected</button></div>
</div>
<div class="overflow-x-auto rounded-3xl border border-tsp04-line bg-white shadow-[0_1px_2px_rgb(0_0_0/0.04)]">
<table class="w-full min-w-[46rem] border-collapse text-left text-sm">
<thead class="border-b border-tsp04-line bg-tsp04-bg/60 text-xs font-semibold uppercase tracking-wide text-tsp04-mut">
<tr><th scope="col" class="w-14 pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-all class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select all visible invoices"></label></th><th scope="col" class="py-3 pr-4">Invoice</th><th scope="col" class="py-3 pr-4">Date</th><th scope="col" class="py-3 pr-4">Description</th><th scope="col" class="py-3 pr-4">Status</th><th scope="col" class="py-3 pr-4 text-right">Amount</th><th scope="col" class="py-3 pr-5 text-right"><span class="sr-only">Download</span></th></tr>
</thead>
<tbody class="divide-y divide-tsp04-line group-has-[#tsp-04-f-paid:checked]/inv:[&>tr:not([data-status=paid])]:hidden group-has-[#tsp-04-f-open:checked]/inv:[&>tr:not([data-status=open])]:hidden group-has-[#tsp-04-f-failed:checked]/inv:[&>tr:not([data-status=failed])]:hidden group-has-[#tsp-04-f-refunded:checked]/inv:[&>tr:not([data-status=refunded])]:hidden">
<tr data-status="open" data-id="INV-2026-011" data-amount="$288.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-011"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-011</th><td class="py-3 pr-4 text-tsp04-mut">Due Oct 30, 2026</td><td class="py-3 pr-4">Pro · 12 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-amber-50 px-2.5 py-1 text-xs font-semibold text-amber-700 ring-1 ring-amber-600/15"><span class="size-1.5 rounded-full bg-amber-500"></span>Open</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$288.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-011"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-010" data-amount="$288.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-010"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-010</th><td class="py-3 pr-4 text-tsp04-mut">Oct 1, 2026</td><td class="py-3 pr-4">Pro · 12 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$288.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-010"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-009" data-amount="$288.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-009"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-009</th><td class="py-3 pr-4 text-tsp04-mut">Sep 1, 2026</td><td class="py-3 pr-4">Pro · 12 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$288.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-009"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="failed" data-id="INV-2026-008" data-amount="$264.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-008"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-008</th><td class="py-3 pr-4 text-tsp04-mut">Aug 1, 2026</td><td class="py-3 pr-4">Pro · 11 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-rose-50 px-2.5 py-1 text-xs font-semibold text-rose-700 ring-1 ring-rose-600/15"><span class="size-1.5 rounded-full bg-rose-500"></span>Failed</span> <a href="#tsp-04-h" class="ml-1 text-xs font-semibold text-tsp04-acc hover:underline">Retry</a></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$264.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-008"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-007" data-amount="$264.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-007"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-007</th><td class="py-3 pr-4 text-tsp04-mut">Jul 1, 2026</td><td class="py-3 pr-4">Pro · 11 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$264.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-007"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="refunded" data-id="INV-2026-006" data-amount="$40.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-006"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-006</th><td class="py-3 pr-4 text-tsp04-mut">Jun 15, 2026</td><td class="py-3 pr-4">Add-on · 10k AI credits</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-zinc-100 px-2.5 py-1 text-xs font-semibold text-zinc-600 ring-1 ring-zinc-500/15"><span class="size-1.5 rounded-full bg-zinc-400"></span>Refunded</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums text-tsp04-mut line-through decoration-zinc-400">$40.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-006"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-005" data-amount="$240.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-005"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-005</th><td class="py-3 pr-4 text-tsp04-mut">Jun 1, 2026</td><td class="py-3 pr-4">Pro · 10 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$240.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-005"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-004" data-amount="$240.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-004"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-004</th><td class="py-3 pr-4 text-tsp04-mut">May 1, 2026</td><td class="py-3 pr-4">Pro · 10 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$240.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-004"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
</tbody>
</table>
</div>
<p class="text-sm text-tsp04-mut">Need a VAT invoice or a different billing address? Update your <a href="#tsp-04-h" class="font-semibold text-tsp04-acc underline-offset-4 hover:underline">billing details</a> — future invoices follow.</p>
</div>
</section><section data-tsp04 class="tsp-04 group/inv w-full min-h-svh bg-tsp04-bg px-4 py-10 font-tsp04-sans text-tsp04-ink antialiased sm:px-8 sm:py-14" aria-labelledby="tsp-04-h">
<div class="mx-auto grid w-full max-w-6xl gap-6">
<header class="flex flex-wrap items-end justify-between gap-4">
<div><h1 id="tsp-04-h" class="text-3xl font-bold tracking-tight">Invoices</h1><p class="mt-1 text-tsp04-mut">Sent to <strong class="font-semibold text-tsp04-ink">billing@northwind.co</strong> · <a href="#tsp-04-h" class="font-semibold text-tsp04-acc underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-tsp04-acc">Change</a></p></div>
<label class="grid gap-1 text-sm"><span class="sr-only">Year</span><select class="min-h-11 rounded-xl border border-tsp04-line bg-white px-3 text-sm font-medium shadow-xs outline-none focus:ring-4 focus:ring-tsp04-acc/15"><option>2026</option><option>2025</option><option>2024</option></select></label>
</header>
<div role="alert" class="flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-800">
<p class="flex items-center gap-2"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 flex-none"><circle cx="12" cy="12" r="9"/><path d="M12 8v4M12 16h.01"/></svg><span><strong class="font-semibold">INV-2026-008 failed.</strong> Visa ending 4242 was declined on Aug 1.</span></p>
<a href="#tsp-04-h" class="inline-flex min-h-11 items-center rounded-xl bg-rose-600 px-4 font-semibold text-white transition hover:bg-rose-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-rose-600">Update payment method</a>
</div>
<div class="flex flex-wrap items-center justify-between gap-3">
<fieldset class="flex flex-wrap gap-2">
<legend class="sr-only">Filter by status</legend>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-all" class="sr-only" checked>All <span class="tabular-nums opacity-60">8</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-paid" class="sr-only"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid <span class="tabular-nums opacity-60">5</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-open" class="sr-only"><span class="size-1.5 rounded-full bg-amber-500"></span>Open <span class="tabular-nums opacity-60">1</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-failed" class="sr-only"><span class="size-1.5 rounded-full bg-rose-500"></span>Failed <span class="tabular-nums opacity-60">1</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-refunded" class="sr-only"><span class="size-1.5 rounded-full bg-zinc-400"></span>Refunded <span class="tabular-nums opacity-60">1</span></label>
</fieldset>
<div class="hidden items-center gap-3 rounded-full bg-tsp04-acc/10 py-1 pl-4 pr-1 text-sm font-semibold text-tsp04-acc group-has-[[data-row-check]:checked]/inv:flex" aria-live="polite"><span data-sel-count>0 selected</span><button type="button" data-bulk class="inline-flex min-h-10 items-center gap-2 rounded-full bg-tsp04-acc px-4 text-white transition hover:bg-tsp04-acc/90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tsp04-acc">Download selected</button></div>
</div>
<div class="overflow-x-auto rounded-3xl border border-tsp04-line bg-white shadow-[0_1px_2px_rgb(0_0_0/0.04)]">
<table class="w-full min-w-[46rem] border-collapse text-left text-sm">
<thead class="border-b border-tsp04-line bg-tsp04-bg/60 text-xs font-semibold uppercase tracking-wide text-tsp04-mut">
<tr><th scope="col" class="w-14 pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-all class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select all visible invoices"></label></th><th scope="col" class="py-3 pr-4">Invoice</th><th scope="col" class="py-3 pr-4">Date</th><th scope="col" class="py-3 pr-4">Description</th><th scope="col" class="py-3 pr-4">Status</th><th scope="col" class="py-3 pr-4 text-right">Amount</th><th scope="col" class="py-3 pr-5 text-right"><span class="sr-only">Download</span></th></tr>
</thead>
<tbody class="divide-y divide-tsp04-line group-has-[#tsp-04-f-paid:checked]/inv:[&>tr:not([data-status=paid])]:hidden group-has-[#tsp-04-f-open:checked]/inv:[&>tr:not([data-status=open])]:hidden group-has-[#tsp-04-f-failed:checked]/inv:[&>tr:not([data-status=failed])]:hidden group-has-[#tsp-04-f-refunded:checked]/inv:[&>tr:not([data-status=refunded])]:hidden">
<tr data-status="open" data-id="INV-2026-011" data-amount="$288.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-011"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-011</th><td class="py-3 pr-4 text-tsp04-mut">Due Oct 30, 2026</td><td class="py-3 pr-4">Pro · 12 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-amber-50 px-2.5 py-1 text-xs font-semibold text-amber-700 ring-1 ring-amber-600/15"><span class="size-1.5 rounded-full bg-amber-500"></span>Open</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$288.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-011"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-010" data-amount="$288.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-010"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-010</th><td class="py-3 pr-4 text-tsp04-mut">Oct 1, 2026</td><td class="py-3 pr-4">Pro · 12 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$288.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-010"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-009" data-amount="$288.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-009"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-009</th><td class="py-3 pr-4 text-tsp04-mut">Sep 1, 2026</td><td class="py-3 pr-4">Pro · 12 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$288.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-009"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="failed" data-id="INV-2026-008" data-amount="$264.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-008"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-008</th><td class="py-3 pr-4 text-tsp04-mut">Aug 1, 2026</td><td class="py-3 pr-4">Pro · 11 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-rose-50 px-2.5 py-1 text-xs font-semibold text-rose-700 ring-1 ring-rose-600/15"><span class="size-1.5 rounded-full bg-rose-500"></span>Failed</span> <a href="#tsp-04-h" class="ml-1 text-xs font-semibold text-tsp04-acc hover:underline">Retry</a></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$264.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-008"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-007" data-amount="$264.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-007"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-007</th><td class="py-3 pr-4 text-tsp04-mut">Jul 1, 2026</td><td class="py-3 pr-4">Pro · 11 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$264.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-007"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="refunded" data-id="INV-2026-006" data-amount="$40.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-006"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-006</th><td class="py-3 pr-4 text-tsp04-mut">Jun 15, 2026</td><td class="py-3 pr-4">Add-on · 10k AI credits</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-zinc-100 px-2.5 py-1 text-xs font-semibold text-zinc-600 ring-1 ring-zinc-500/15"><span class="size-1.5 rounded-full bg-zinc-400"></span>Refunded</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums text-tsp04-mut line-through decoration-zinc-400">$40.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-006"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-005" data-amount="$240.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-005"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-005</th><td class="py-3 pr-4 text-tsp04-mut">Jun 1, 2026</td><td class="py-3 pr-4">Pro · 10 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$240.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-005"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-004" data-amount="$240.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-004"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-004</th><td class="py-3 pr-4 text-tsp04-mut">May 1, 2026</td><td class="py-3 pr-4">Pro · 10 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$240.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-004"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
</tbody>
</table>
</div>
<p class="text-sm text-tsp04-mut">Need a VAT invoice or a different billing address? Update your <a href="#tsp-04-h" class="font-semibold text-tsp04-acc underline-offset-4 hover:underline">billing details</a> — future invoices follow.</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-tsp04-bg: #f6f6f8;
--color-tsp04-bg: oklch(0.974 0.003 286);
--color-tsp04-ink: #17171c;
--color-tsp04-ink: oklch(0.205 0.008 286);
--color-tsp04-mut: #61616c;
--color-tsp04-mut: oklch(0.5 0.015 286);
--color-tsp04-line: #e7e7ec;
--color-tsp04-line: oklch(0.925 0.006 286);
--color-tsp04-acc: #4f46e5;
--color-tsp04-acc: oklch(0.511 0.23 277);
--font-tsp04-sans: 'DM Sans', system-ui, sans-serif;
--font-tsp04-mono: 'DM Mono', ui-monospace, monospace;
}
.tsp-04 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tsp04-bg: #f6f6f8;
--color-tsp04-bg: oklch(0.974 0.003 286);
--color-tsp04-ink: #17171c;
--color-tsp04-ink: oklch(0.205 0.008 286);
--color-tsp04-mut: #61616c;
--color-tsp04-mut: oklch(0.5 0.015 286);
--color-tsp04-line: #e7e7ec;
--color-tsp04-line: oklch(0.925 0.006 286);
--color-tsp04-acc: #4f46e5;
--color-tsp04-acc: oklch(0.511 0.23 277);
--font-tsp04-sans: 'DM Sans', system-ui, sans-serif;
--font-tsp04-mono: 'DM Mono', ui-monospace, monospace;
}
.tsp-04 {
width: 100%;
min-height: 100svh;
}(() => {
const root = document.querySelector('[data-tsp04]');
if (!root || root.dataset.wired) return;
root.dataset.wired = '1';
const all = root.querySelector('[data-all]');
const count = root.querySelector('[data-sel-count]');
const rows = () => [...root.querySelectorAll('tbody tr')].filter((r) => r.offsetParent !== null);
const sync = () => {
const vis = rows();
const on = vis.filter((r) => r.querySelector('[data-row-check]').checked).length;
all.checked = vis.length > 0 && on === vis.length;
all.indeterminate = on > 0 && on < vis.length;
const total = root.querySelectorAll('[data-row-check]:checked').length;
count.textContent = total + ' selected';
};
all.addEventListener('change', () => { rows().forEach((r) => { r.querySelector('[data-row-check]').checked = all.checked; }); sync(); });
root.addEventListener('change', (e) => { if (e.target.matches('[data-row-check], [name=tsp-04-f]')) setTimeout(sync); });
const save = (row) => {
const lines = ['NORTHWIND CO — INVOICE', '', 'Invoice: ' + row.dataset.id, 'Status: ' + row.dataset.status, 'Amount: ' + row.dataset.amount, '', 'Thank you for your business.'];
const url = URL.createObjectURL(new Blob([lines.join('\n')], { type: 'text/plain' }));
const a = document.createElement('a');
a.href = url; a.download = row.dataset.id + '.txt';
document.body.appendChild(a); a.click(); a.remove();
setTimeout(() => URL.revokeObjectURL(url), 1000);
};
const run = (btn) => {
if (btn.dataset.state === 'loading') return;
btn.dataset.state = 'loading';
setTimeout(() => {
save(btn.closest('tr'));
btn.dataset.state = 'done';
setTimeout(() => { btn.dataset.state = 'idle'; }, 1800);
}, 900);
};
root.querySelectorAll('[data-dl]').forEach((b) => b.addEventListener('click', () => run(b)));
root.querySelector('[data-bulk]').addEventListener('click', () => {
root.querySelectorAll('[data-row-check]:checked').forEach((c, i) => setTimeout(() => run(c.closest('tr').querySelector('[data-dl]')), i * 250));
});
sync();
})();(() => {
const root = document.querySelector('[data-tsp04]');
if (!root || root.dataset.wired) return;
root.dataset.wired = '1';
const all = root.querySelector('[data-all]');
const count = root.querySelector('[data-sel-count]');
const rows = () => [...root.querySelectorAll('tbody tr')].filter((r) => r.offsetParent !== null);
const sync = () => {
const vis = rows();
const on = vis.filter((r) => r.querySelector('[data-row-check]').checked).length;
all.checked = vis.length > 0 && on === vis.length;
all.indeterminate = on > 0 && on < vis.length;
const total = root.querySelectorAll('[data-row-check]:checked').length;
count.textContent = total + ' selected';
};
all.addEventListener('change', () => { rows().forEach((r) => { r.querySelector('[data-row-check]').checked = all.checked; }); sync(); });
root.addEventListener('change', (e) => { if (e.target.matches('[data-row-check], [name=tsp-04-f]')) setTimeout(sync); });
const save = (row) => {
const lines = ['NORTHWIND CO — INVOICE', '', 'Invoice: ' + row.dataset.id, 'Status: ' + row.dataset.status, 'Amount: ' + row.dataset.amount, '', 'Thank you for your business.'];
const url = URL.createObjectURL(new Blob([lines.join('\n')], { type: 'text/plain' }));
const a = document.createElement('a');
a.href = url; a.download = row.dataset.id + '.txt';
document.body.appendChild(a); a.click(); a.remove();
setTimeout(() => URL.revokeObjectURL(url), 1000);
};
const run = (btn) => {
if (btn.dataset.state === 'loading') return;
btn.dataset.state = 'loading';
setTimeout(() => {
save(btn.closest('tr'));
btn.dataset.state = 'done';
setTimeout(() => { btn.dataset.state = 'idle'; }, 1800);
}, 900);
};
root.querySelectorAll('[data-dl]').forEach((b) => b.addEventListener('click', () => run(b)));
root.querySelector('[data-bulk]').addEventListener('click', () => {
root.querySelectorAll('[data-row-check]:checked').forEach((c, i) => setTimeout(() => run(c.closest('tr').querySelector('[data-dl]')), i * 250));
});
sync();
})();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-tsp04-bg: #f6f6f8;
--color-tsp04-bg: oklch(0.974 0.003 286);
--color-tsp04-ink: #17171c;
--color-tsp04-ink: oklch(0.205 0.008 286);
--color-tsp04-mut: #61616c;
--color-tsp04-mut: oklch(0.5 0.015 286);
--color-tsp04-line: #e7e7ec;
--color-tsp04-line: oklch(0.925 0.006 286);
--color-tsp04-acc: #4f46e5;
--color-tsp04-acc: oklch(0.511 0.23 277);
--font-tsp04-sans: 'DM Sans', system-ui, sans-serif;
--font-tsp04-mono: 'DM Mono', ui-monospace, monospace;
}
.tsp-04 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tsp04-bg: #f6f6f8;
--color-tsp04-bg: oklch(0.974 0.003 286);
--color-tsp04-ink: #17171c;
--color-tsp04-ink: oklch(0.205 0.008 286);
--color-tsp04-mut: #61616c;
--color-tsp04-mut: oklch(0.5 0.015 286);
--color-tsp04-line: #e7e7ec;
--color-tsp04-line: oklch(0.925 0.006 286);
--color-tsp04-acc: #4f46e5;
--color-tsp04-acc: oklch(0.511 0.23 277);
--font-tsp04-sans: 'DM Sans', system-ui, sans-serif;
--font-tsp04-mono: 'DM Mono', ui-monospace, monospace;
}
.tsp-04 {
width: 100%;
min-height: 100svh;
}<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=DM+Sans:opsz,wght@9..40,400..700&family=DM+Mono:wght@400;500&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=DM+Sans:opsz,wght@9..40,400..700&family=DM+Mono:wght@400;500&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The CSS original's
.csp-04:has(#paid:checked) tbody tr:not([data-status=paid])rules became four stacked variants on the<tbody>— the filter lives where it acts. - The CSS-counter 'N selected' label moved into the script, which also had to own
indeterminate; the bulk bar's visibility stayed CSS viagroup-has-[[data-row-check]:checked]/inv:flex. - UI upgrade from the CSS original: a failed-payment alert with a direct fix, a Retry link on the failed row and struck-through refunded amounts.
- UI upgrade: 'Download selected' staggers real downloads through each row's own idle → spinner → check button.
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 Invoice History Table
Source: https://codefronts.com/layouts/tailwind-css-settings-pages/tailwind-invoice-history-table/
A billing-history table with zero-JS status filter chips, row selection that tints the row and raises a bulk-action bar, a native indeterminate select-all, status pills for Paid, Open, Failed and Refunded, and per-row download buttons that cycle idle → generating → done before handing over a real file.
## HTML
```html
<section data-tsp04 class="tsp-04 group/inv w-full min-h-svh bg-tsp04-bg px-4 py-10 font-tsp04-sans text-tsp04-ink antialiased sm:px-8 sm:py-14" aria-labelledby="tsp-04-h">
<div class="mx-auto grid w-full max-w-6xl gap-6">
<header class="flex flex-wrap items-end justify-between gap-4">
<div><h1 id="tsp-04-h" class="text-3xl font-bold tracking-tight">Invoices</h1><p class="mt-1 text-tsp04-mut">Sent to <strong class="font-semibold text-tsp04-ink">billing@northwind.co</strong> · <a href="#tsp-04-h" class="font-semibold text-tsp04-acc underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-tsp04-acc">Change</a></p></div>
<label class="grid gap-1 text-sm"><span class="sr-only">Year</span><select class="min-h-11 rounded-xl border border-tsp04-line bg-white px-3 text-sm font-medium shadow-xs outline-none focus:ring-4 focus:ring-tsp04-acc/15"><option>2026</option><option>2025</option><option>2024</option></select></label>
</header>
<div role="alert" class="flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-800">
<p class="flex items-center gap-2"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 flex-none"><circle cx="12" cy="12" r="9"/><path d="M12 8v4M12 16h.01"/></svg><span><strong class="font-semibold">INV-2026-008 failed.</strong> Visa ending 4242 was declined on Aug 1.</span></p>
<a href="#tsp-04-h" class="inline-flex min-h-11 items-center rounded-xl bg-rose-600 px-4 font-semibold text-white transition hover:bg-rose-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-rose-600">Update payment method</a>
</div>
<div class="flex flex-wrap items-center justify-between gap-3">
<fieldset class="flex flex-wrap gap-2">
<legend class="sr-only">Filter by status</legend>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-all" class="sr-only" checked>All <span class="tabular-nums opacity-60">8</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-paid" class="sr-only"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid <span class="tabular-nums opacity-60">5</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-open" class="sr-only"><span class="size-1.5 rounded-full bg-amber-500"></span>Open <span class="tabular-nums opacity-60">1</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-failed" class="sr-only"><span class="size-1.5 rounded-full bg-rose-500"></span>Failed <span class="tabular-nums opacity-60">1</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-refunded" class="sr-only"><span class="size-1.5 rounded-full bg-zinc-400"></span>Refunded <span class="tabular-nums opacity-60">1</span></label>
</fieldset>
<div class="hidden items-center gap-3 rounded-full bg-tsp04-acc/10 py-1 pl-4 pr-1 text-sm font-semibold text-tsp04-acc group-has-[[data-row-check]:checked]/inv:flex" aria-live="polite"><span data-sel-count>0 selected</span><button type="button" data-bulk class="inline-flex min-h-10 items-center gap-2 rounded-full bg-tsp04-acc px-4 text-white transition hover:bg-tsp04-acc/90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tsp04-acc">Download selected</button></div>
</div>
<div class="overflow-x-auto rounded-3xl border border-tsp04-line bg-white shadow-[0_1px_2px_rgb(0_0_0/0.04)]">
<table class="w-full min-w-[46rem] border-collapse text-left text-sm">
<thead class="border-b border-tsp04-line bg-tsp04-bg/60 text-xs font-semibold uppercase tracking-wide text-tsp04-mut">
<tr><th scope="col" class="w-14 pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-all class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select all visible invoices"></label></th><th scope="col" class="py-3 pr-4">Invoice</th><th scope="col" class="py-3 pr-4">Date</th><th scope="col" class="py-3 pr-4">Description</th><th scope="col" class="py-3 pr-4">Status</th><th scope="col" class="py-3 pr-4 text-right">Amount</th><th scope="col" class="py-3 pr-5 text-right"><span class="sr-only">Download</span></th></tr>
</thead>
<tbody class="divide-y divide-tsp04-line group-has-[#tsp-04-f-paid:checked]/inv:[&>tr:not([data-status=paid])]:hidden group-has-[#tsp-04-f-open:checked]/inv:[&>tr:not([data-status=open])]:hidden group-has-[#tsp-04-f-failed:checked]/inv:[&>tr:not([data-status=failed])]:hidden group-has-[#tsp-04-f-refunded:checked]/inv:[&>tr:not([data-status=refunded])]:hidden">
<tr data-status="open" data-id="INV-2026-011" data-amount="$288.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-011"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-011</th><td class="py-3 pr-4 text-tsp04-mut">Due Oct 30, 2026</td><td class="py-3 pr-4">Pro · 12 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-amber-50 px-2.5 py-1 text-xs font-semibold text-amber-700 ring-1 ring-amber-600/15"><span class="size-1.5 rounded-full bg-amber-500"></span>Open</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$288.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-011"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-010" data-amount="$288.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-010"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-010</th><td class="py-3 pr-4 text-tsp04-mut">Oct 1, 2026</td><td class="py-3 pr-4">Pro · 12 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$288.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-010"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-009" data-amount="$288.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-009"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-009</th><td class="py-3 pr-4 text-tsp04-mut">Sep 1, 2026</td><td class="py-3 pr-4">Pro · 12 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$288.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-009"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="failed" data-id="INV-2026-008" data-amount="$264.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-008"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-008</th><td class="py-3 pr-4 text-tsp04-mut">Aug 1, 2026</td><td class="py-3 pr-4">Pro · 11 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-rose-50 px-2.5 py-1 text-xs font-semibold text-rose-700 ring-1 ring-rose-600/15"><span class="size-1.5 rounded-full bg-rose-500"></span>Failed</span> <a href="#tsp-04-h" class="ml-1 text-xs font-semibold text-tsp04-acc hover:underline">Retry</a></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$264.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-008"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-007" data-amount="$264.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-007"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-007</th><td class="py-3 pr-4 text-tsp04-mut">Jul 1, 2026</td><td class="py-3 pr-4">Pro · 11 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$264.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-007"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="refunded" data-id="INV-2026-006" data-amount="$40.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-006"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-006</th><td class="py-3 pr-4 text-tsp04-mut">Jun 15, 2026</td><td class="py-3 pr-4">Add-on · 10k AI credits</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-zinc-100 px-2.5 py-1 text-xs font-semibold text-zinc-600 ring-1 ring-zinc-500/15"><span class="size-1.5 rounded-full bg-zinc-400"></span>Refunded</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums text-tsp04-mut line-through decoration-zinc-400">$40.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-006"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-005" data-amount="$240.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-005"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-005</th><td class="py-3 pr-4 text-tsp04-mut">Jun 1, 2026</td><td class="py-3 pr-4">Pro · 10 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$240.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-005"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-004" data-amount="$240.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-004"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-004</th><td class="py-3 pr-4 text-tsp04-mut">May 1, 2026</td><td class="py-3 pr-4">Pro · 10 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$240.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-004"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
</tbody>
</table>
</div>
<p class="text-sm text-tsp04-mut">Need a VAT invoice or a different billing address? Update your <a href="#tsp-04-h" class="font-semibold text-tsp04-acc underline-offset-4 hover:underline">billing details</a> — future invoices follow.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tsp04-bg: #f6f6f8;
--color-tsp04-bg: oklch(0.974 0.003 286);
--color-tsp04-ink: #17171c;
--color-tsp04-ink: oklch(0.205 0.008 286);
--color-tsp04-mut: #61616c;
--color-tsp04-mut: oklch(0.5 0.015 286);
--color-tsp04-line: #e7e7ec;
--color-tsp04-line: oklch(0.925 0.006 286);
--color-tsp04-acc: #4f46e5;
--color-tsp04-acc: oklch(0.511 0.23 277);
--font-tsp04-sans: 'DM Sans', system-ui, sans-serif;
--font-tsp04-mono: 'DM Mono', ui-monospace, monospace;
}
.tsp-04 {
width: 100%;
min-height: 100svh;
}
```
## JavaScript
```js
(() => {
const root = document.querySelector('[data-tsp04]');
if (!root || root.dataset.wired) return;
root.dataset.wired = '1';
const all = root.querySelector('[data-all]');
const count = root.querySelector('[data-sel-count]');
const rows = () => [...root.querySelectorAll('tbody tr')].filter((r) => r.offsetParent !== null);
const sync = () => {
const vis = rows();
const on = vis.filter((r) => r.querySelector('[data-row-check]').checked).length;
all.checked = vis.length > 0 && on === vis.length;
all.indeterminate = on > 0 && on < vis.length;
const total = root.querySelectorAll('[data-row-check]:checked').length;
count.textContent = total + ' selected';
};
all.addEventListener('change', () => { rows().forEach((r) => { r.querySelector('[data-row-check]').checked = all.checked; }); sync(); });
root.addEventListener('change', (e) => { if (e.target.matches('[data-row-check], [name=tsp-04-f]')) setTimeout(sync); });
const save = (row) => {
const lines = ['NORTHWIND CO — INVOICE', '', 'Invoice: ' + row.dataset.id, 'Status: ' + row.dataset.status, 'Amount: ' + row.dataset.amount, '', 'Thank you for your business.'];
const url = URL.createObjectURL(new Blob([lines.join('\n')], { type: 'text/plain' }));
const a = document.createElement('a');
a.href = url; a.download = row.dataset.id + '.txt';
document.body.appendChild(a); a.click(); a.remove();
setTimeout(() => URL.revokeObjectURL(url), 1000);
};
const run = (btn) => {
if (btn.dataset.state === 'loading') return;
btn.dataset.state = 'loading';
setTimeout(() => {
save(btn.closest('tr'));
btn.dataset.state = 'done';
setTimeout(() => { btn.dataset.state = 'idle'; }, 1800);
}, 900);
};
root.querySelectorAll('[data-dl]').forEach((b) => b.addEventListener('click', () => run(b)));
root.querySelector('[data-bulk]').addEventListener('click', () => {
root.querySelectorAll('[data-row-check]:checked').forEach((c, i) => setTimeout(() => run(c.closest('tr').querySelector('[data-dl]')), i * 250));
});
sync();
})();
```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 Invoice History Table
Source: https://codefronts.com/layouts/tailwind-css-settings-pages/tailwind-invoice-history-table/
A billing-history table with zero-JS status filter chips, row selection that tints the row and raises a bulk-action bar, a native indeterminate select-all, status pills for Paid, Open, Failed and Refunded, and per-row download buttons that cycle idle → generating → done before handing over a real file.
## HTML
```html
<section data-tsp04 class="tsp-04 group/inv w-full min-h-svh bg-tsp04-bg px-4 py-10 font-tsp04-sans text-tsp04-ink antialiased sm:px-8 sm:py-14" aria-labelledby="tsp-04-h">
<div class="mx-auto grid w-full max-w-6xl gap-6">
<header class="flex flex-wrap items-end justify-between gap-4">
<div><h1 id="tsp-04-h" class="text-3xl font-bold tracking-tight">Invoices</h1><p class="mt-1 text-tsp04-mut">Sent to <strong class="font-semibold text-tsp04-ink">billing@northwind.co</strong> · <a href="#tsp-04-h" class="font-semibold text-tsp04-acc underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-tsp04-acc">Change</a></p></div>
<label class="grid gap-1 text-sm"><span class="sr-only">Year</span><select class="min-h-11 rounded-xl border border-tsp04-line bg-white px-3 text-sm font-medium shadow-xs outline-none focus:ring-4 focus:ring-tsp04-acc/15"><option>2026</option><option>2025</option><option>2024</option></select></label>
</header>
<div role="alert" class="flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-800">
<p class="flex items-center gap-2"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 flex-none"><circle cx="12" cy="12" r="9"/><path d="M12 8v4M12 16h.01"/></svg><span><strong class="font-semibold">INV-2026-008 failed.</strong> Visa ending 4242 was declined on Aug 1.</span></p>
<a href="#tsp-04-h" class="inline-flex min-h-11 items-center rounded-xl bg-rose-600 px-4 font-semibold text-white transition hover:bg-rose-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-rose-600">Update payment method</a>
</div>
<div class="flex flex-wrap items-center justify-between gap-3">
<fieldset class="flex flex-wrap gap-2">
<legend class="sr-only">Filter by status</legend>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-all" class="sr-only" checked>All <span class="tabular-nums opacity-60">8</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-paid" class="sr-only"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid <span class="tabular-nums opacity-60">5</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-open" class="sr-only"><span class="size-1.5 rounded-full bg-amber-500"></span>Open <span class="tabular-nums opacity-60">1</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-failed" class="sr-only"><span class="size-1.5 rounded-full bg-rose-500"></span>Failed <span class="tabular-nums opacity-60">1</span></label>
<label class="inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-tsp04-line bg-white px-4 text-sm font-semibold text-tsp04-mut shadow-xs transition hover:text-tsp04-ink has-checked:border-transparent has-checked:bg-tsp04-ink has-checked:text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-tsp04-acc"><input type="radio" name="tsp-04-f" id="tsp-04-f-refunded" class="sr-only"><span class="size-1.5 rounded-full bg-zinc-400"></span>Refunded <span class="tabular-nums opacity-60">1</span></label>
</fieldset>
<div class="hidden items-center gap-3 rounded-full bg-tsp04-acc/10 py-1 pl-4 pr-1 text-sm font-semibold text-tsp04-acc group-has-[[data-row-check]:checked]/inv:flex" aria-live="polite"><span data-sel-count>0 selected</span><button type="button" data-bulk class="inline-flex min-h-10 items-center gap-2 rounded-full bg-tsp04-acc px-4 text-white transition hover:bg-tsp04-acc/90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tsp04-acc">Download selected</button></div>
</div>
<div class="overflow-x-auto rounded-3xl border border-tsp04-line bg-white shadow-[0_1px_2px_rgb(0_0_0/0.04)]">
<table class="w-full min-w-[46rem] border-collapse text-left text-sm">
<thead class="border-b border-tsp04-line bg-tsp04-bg/60 text-xs font-semibold uppercase tracking-wide text-tsp04-mut">
<tr><th scope="col" class="w-14 pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-all class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select all visible invoices"></label></th><th scope="col" class="py-3 pr-4">Invoice</th><th scope="col" class="py-3 pr-4">Date</th><th scope="col" class="py-3 pr-4">Description</th><th scope="col" class="py-3 pr-4">Status</th><th scope="col" class="py-3 pr-4 text-right">Amount</th><th scope="col" class="py-3 pr-5 text-right"><span class="sr-only">Download</span></th></tr>
</thead>
<tbody class="divide-y divide-tsp04-line group-has-[#tsp-04-f-paid:checked]/inv:[&>tr:not([data-status=paid])]:hidden group-has-[#tsp-04-f-open:checked]/inv:[&>tr:not([data-status=open])]:hidden group-has-[#tsp-04-f-failed:checked]/inv:[&>tr:not([data-status=failed])]:hidden group-has-[#tsp-04-f-refunded:checked]/inv:[&>tr:not([data-status=refunded])]:hidden">
<tr data-status="open" data-id="INV-2026-011" data-amount="$288.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-011"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-011</th><td class="py-3 pr-4 text-tsp04-mut">Due Oct 30, 2026</td><td class="py-3 pr-4">Pro · 12 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-amber-50 px-2.5 py-1 text-xs font-semibold text-amber-700 ring-1 ring-amber-600/15"><span class="size-1.5 rounded-full bg-amber-500"></span>Open</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$288.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-011"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-010" data-amount="$288.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-010"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-010</th><td class="py-3 pr-4 text-tsp04-mut">Oct 1, 2026</td><td class="py-3 pr-4">Pro · 12 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$288.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-010"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-009" data-amount="$288.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-009"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-009</th><td class="py-3 pr-4 text-tsp04-mut">Sep 1, 2026</td><td class="py-3 pr-4">Pro · 12 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$288.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-009"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="failed" data-id="INV-2026-008" data-amount="$264.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-008"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-008</th><td class="py-3 pr-4 text-tsp04-mut">Aug 1, 2026</td><td class="py-3 pr-4">Pro · 11 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-rose-50 px-2.5 py-1 text-xs font-semibold text-rose-700 ring-1 ring-rose-600/15"><span class="size-1.5 rounded-full bg-rose-500"></span>Failed</span> <a href="#tsp-04-h" class="ml-1 text-xs font-semibold text-tsp04-acc hover:underline">Retry</a></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$264.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-008"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-007" data-amount="$264.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-007"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-007</th><td class="py-3 pr-4 text-tsp04-mut">Jul 1, 2026</td><td class="py-3 pr-4">Pro · 11 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$264.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-007"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="refunded" data-id="INV-2026-006" data-amount="$40.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-006"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-006</th><td class="py-3 pr-4 text-tsp04-mut">Jun 15, 2026</td><td class="py-3 pr-4">Add-on · 10k AI credits</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-zinc-100 px-2.5 py-1 text-xs font-semibold text-zinc-600 ring-1 ring-zinc-500/15"><span class="size-1.5 rounded-full bg-zinc-400"></span>Refunded</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums text-tsp04-mut line-through decoration-zinc-400">$40.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-006"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-005" data-amount="$240.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-005"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-005</th><td class="py-3 pr-4 text-tsp04-mut">Jun 1, 2026</td><td class="py-3 pr-4">Pro · 10 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$240.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-005"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
<tr data-status="paid" data-id="INV-2026-004" data-amount="$240.00" class="transition hover:bg-tsp04-bg/60 has-checked:bg-tsp04-acc/5"><td class="pl-3"><label class="grid size-11 cursor-pointer place-items-center"><input type="checkbox" data-row-check class="size-4 cursor-pointer accent-tsp04-acc" aria-label="Select INV-2026-004"></label></td><th scope="row" class="py-3 pr-4 font-tsp04-mono font-medium">INV-2026-004</th><td class="py-3 pr-4 text-tsp04-mut">May 1, 2026</td><td class="py-3 pr-4">Pro · 10 seats · monthly</td><td class="py-3 pr-4"><span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-semibold text-emerald-700 ring-1 ring-emerald-600/15"><span class="size-1.5 rounded-full bg-emerald-500"></span>Paid</span></td><td class="py-3 pr-4 text-right font-semibold tabular-nums">$240.00</td><td class="py-2 pr-3 text-right"><button type="button" data-dl data-state="idle" class="group/dl ml-auto grid size-11 place-items-center rounded-xl text-tsp04-mut transition hover:bg-tsp04-bg hover:text-tsp04-ink focus-visible:outline-2 focus-visible:outline-tsp04-acc data-[state=done]:text-emerald-600" aria-label="Download INV-2026-004"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 group-data-[state=loading]/dl:hidden group-data-[state=done]/dl:hidden"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="hidden size-4 animate-spin group-data-[state=loading]/dl:block"><path d="M12 3a9 9 0 1 0 9 9"/></svg><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="hidden size-4 group-data-[state=done]/dl:block"><path d="M20 6 9 17l-5-5"/></svg></button></td></tr>
</tbody>
</table>
</div>
<p class="text-sm text-tsp04-mut">Need a VAT invoice or a different billing address? Update your <a href="#tsp-04-h" class="font-semibold text-tsp04-acc underline-offset-4 hover:underline">billing details</a> — future invoices follow.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tsp04-bg: #f6f6f8;
--color-tsp04-bg: oklch(0.974 0.003 286);
--color-tsp04-ink: #17171c;
--color-tsp04-ink: oklch(0.205 0.008 286);
--color-tsp04-mut: #61616c;
--color-tsp04-mut: oklch(0.5 0.015 286);
--color-tsp04-line: #e7e7ec;
--color-tsp04-line: oklch(0.925 0.006 286);
--color-tsp04-acc: #4f46e5;
--color-tsp04-acc: oklch(0.511 0.23 277);
--font-tsp04-sans: 'DM Sans', system-ui, sans-serif;
--font-tsp04-mono: 'DM Mono', ui-monospace, monospace;
}
.tsp-04 {
width: 100%;
min-height: 100svh;
}
```
## JavaScript
```js
(() => {
const root = document.querySelector('[data-tsp04]');
if (!root || root.dataset.wired) return;
root.dataset.wired = '1';
const all = root.querySelector('[data-all]');
const count = root.querySelector('[data-sel-count]');
const rows = () => [...root.querySelectorAll('tbody tr')].filter((r) => r.offsetParent !== null);
const sync = () => {
const vis = rows();
const on = vis.filter((r) => r.querySelector('[data-row-check]').checked).length;
all.checked = vis.length > 0 && on === vis.length;
all.indeterminate = on > 0 && on < vis.length;
const total = root.querySelectorAll('[data-row-check]:checked').length;
count.textContent = total + ' selected';
};
all.addEventListener('change', () => { rows().forEach((r) => { r.querySelector('[data-row-check]').checked = all.checked; }); sync(); });
root.addEventListener('change', (e) => { if (e.target.matches('[data-row-check], [name=tsp-04-f]')) setTimeout(sync); });
const save = (row) => {
const lines = ['NORTHWIND CO — INVOICE', '', 'Invoice: ' + row.dataset.id, 'Status: ' + row.dataset.status, 'Amount: ' + row.dataset.amount, '', 'Thank you for your business.'];
const url = URL.createObjectURL(new Blob([lines.join('\n')], { type: 'text/plain' }));
const a = document.createElement('a');
a.href = url; a.download = row.dataset.id + '.txt';
document.body.appendChild(a); a.click(); a.remove();
setTimeout(() => URL.revokeObjectURL(url), 1000);
};
const run = (btn) => {
if (btn.dataset.state === 'loading') return;
btn.dataset.state = 'loading';
setTimeout(() => {
save(btn.closest('tr'));
btn.dataset.state = 'done';
setTimeout(() => { btn.dataset.state = 'idle'; }, 1800);
}, 900);
};
root.querySelectorAll('[data-dl]').forEach((b) => b.addEventListener('click', () => run(b)));
root.querySelector('[data-bulk]').addEventListener('click', () => {
root.querySelectorAll('[data-row-check]:checked').forEach((c, i) => setTimeout(() => run(c.closest('tr').querySelector('[data-dl]')), i * 250));
});
sync();
})();
```How this works
Filtering is CSS. The chips are a radio group. Each row carries data-status, and the <tbody> holds one stacked variant per status — when this chip is checked, hide every child row that isn't this status. 'All' needs no rule at all.
The script handles what CSS can't count. It keeps the select-all box honest (including indeterminate), writes the 'N selected' label, and drives each download button through data-state. The file itself is a Blob URL, so the demo downloads a real document without a server.
Make it yours
- Add a status: give rows a new
data-status, add a chip radio and one moregroup-has-[…]/inv:[&>tr:not(…)]:hiddenclass on the tbody. - Swap the text receipt for a PDF by pointing the download handler at your invoice endpoint.
- Retint selection and focus with
--color-tsp04-acc.
Gotchas — read before shipping
- Arbitrary selectors can't contain spaces — Tailwind turns underscores into spaces, so
[&>tr:not([data-status=paid])]is written with none. - The table scrolls horizontally on phones inside
overflow-x-auto; keepmin-w-*on the table so columns never crush. - Hidden rows stay selectable through select-all — the script only counts visible rows.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
:has() needs Chrome 105 / Safari 15.4 / Firefox 121. Without it every row stays visible and the bulk bar never appears; downloads still work.