Dark Card Accordion
Original · freeA dark, glassy card accordion.
Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.
npx shadcn@latest add https://webinnoventix.com/r/accordion-dark-card.json"use client";
import { useId, useState } from "react";
type FaqItem = {
id: string;
question: string;
answer: string;
tag: string;
};
const ITEMS: FaqItem[] = [
{
id: "billing",
tag: "Billing",
question: "When am I charged after the free trial ends?",
answer:
"Your card is charged on the 15th day, the morning your 14-day trial closes. We send a reminder email 72 hours before, and you can cancel from Settings → Plan with a single click until the moment the trial expires.",
},
{
id: "seats",
tag: "Teams",
question: "Can I add or remove seats mid-cycle?",
answer:
"Yes. Add seats instantly and we prorate the difference to your next invoice. Removing a seat frees it at the end of the current billing period, so you keep paid access until the date you already paid for.",
},
{
id: "export",
tag: "Data",
question: "Do I keep my data if I downgrade or leave?",
answer:
"Always. Export everything as CSV or JSON at any time, no lock-in. If you cancel, we keep your workspace in read-only cold storage for 30 days so you can reactivate or download before anything is deleted.",
},
{
id: "security",
tag: "Security",
question: "How is my workspace protected?",
answer:
"Data is encrypted in transit with TLS 1.3 and at rest with AES-256. We run SOC 2 Type II controls, enforce optional SSO and 2FA, and scope every access token to the least privilege it needs.",
},
];
export default function AccordionDarkCard() {
const [openId, setOpenId] = useState<string | null>(ITEMS[0].id);
const baseId = useId();
const toggle = (id: string) => {
setOpenId((current) => (current === id ? null : id));
};
return (
<section className="relative w-full overflow-hidden bg-slate-950 px-4 py-20 sm:px-6 sm:py-28">
<style>{`
@keyframes adc_rise {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes adc_sheen {
0% { transform: translateX(-120%); }
100% { transform: translateX(120%); }
}
@media (prefers-reduced-motion: reduce) {
.adc_animate { animation: none !important; }
.adc_panel { transition: none !important; }
}
`}</style>
{/* Ambient glow backdrop */}
<div
aria-hidden
className="pointer-events-none absolute -top-32 left-1/2 h-72 w-[36rem] -translate-x-1/2 rounded-full bg-indigo-500/20 blur-3xl"
/>
<div
aria-hidden
className="pointer-events-none absolute -bottom-40 right-0 h-72 w-72 rounded-full bg-violet-500/10 blur-3xl"
/>
<div className="relative mx-auto max-w-2xl">
<div className="mb-10 text-center">
<span className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-3 py-1 text-xs font-medium text-indigo-200 backdrop-blur">
<span className="h-1.5 w-1.5 rounded-full bg-emerald-400" />
Support Center
</span>
<h2 className="mt-4 text-3xl font-semibold tracking-tight text-white sm:text-4xl">
Questions, answered
</h2>
<p className="mt-3 text-sm text-slate-400 sm:text-base">
Everything about billing, teams, and keeping your data yours.
</p>
</div>
<div className="space-y-3">
{ITEMS.map((item, index) => {
const isOpen = openId === item.id;
const btnId = `${baseId}-btn-${item.id}`;
const panelId = `${baseId}-panel-${item.id}`;
return (
<div
key={item.id}
className={[
"adc_animate group relative overflow-hidden rounded-2xl border backdrop-blur-xl transition-colors duration-300",
isOpen
? "border-indigo-400/40 bg-white/[0.07] shadow-lg shadow-indigo-950/40"
: "border-white/10 bg-white/[0.03] hover:border-white/20 hover:bg-white/[0.05]",
].join(" ")}
style={{
animation: `adc_rise 0.5s ease-out ${index * 0.07}s both`,
}}
>
{/* top hairline highlight */}
<span
aria-hidden
className="pointer-events-none absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-white/25 to-transparent"
/>
<h3>
<button
id={btnId}
type="button"
aria-expanded={isOpen}
aria-controls={panelId}
onClick={() => toggle(item.id)}
className="flex w-full items-center gap-4 px-5 py-4 text-left outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-indigo-400/70 sm:px-6 sm:py-5"
>
<span className="flex-1">
<span className="mb-1 block text-[0.7rem] font-medium uppercase tracking-wider text-indigo-300/80">
{item.tag}
</span>
<span className="block text-base font-medium text-slate-100 sm:text-lg">
{item.question}
</span>
</span>
<span
aria-hidden
className={[
"flex h-8 w-8 flex-none items-center justify-center rounded-full border transition-all duration-300",
isOpen
? "rotate-180 border-indigo-400/50 bg-indigo-500/20 text-indigo-200"
: "border-white/15 bg-white/5 text-slate-300 group-hover:border-white/30",
].join(" ")}
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
className="h-4 w-4"
>
<path d="m6 9 6 6 6-6" />
</svg>
</span>
</button>
</h3>
{/* grid-rows height animation, no JS measuring */}
<div
id={panelId}
role="region"
aria-labelledby={btnId}
className="adc_panel grid transition-[grid-template-rows] duration-300 ease-out"
style={{ gridTemplateRows: isOpen ? "1fr" : "0fr" }}
>
<div className="overflow-hidden">
<div className="px-5 pb-5 sm:px-6 sm:pb-6">
<div className="mb-4 h-px w-full bg-gradient-to-r from-white/10 via-white/5 to-transparent" />
<p className="text-sm leading-relaxed text-slate-400 sm:text-[0.95rem]">
{item.answer}
</p>
</div>
</div>
</div>
</div>
);
})}
</div>
<p className="mt-8 text-center text-sm text-slate-500">
Still stuck?{" "}
<a
href="#contact"
className="font-medium text-indigo-300 underline decoration-indigo-400/40 underline-offset-4 outline-none transition-colors hover:text-indigo-200 focus-visible:ring-2 focus-visible:ring-indigo-400/70 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-950"
>
Talk to a human
</a>
.
</p>
</div>
</section>
);
}Dependencies
Licence
Built by Web Innoventix. Free for personal and commercial use, no attribution required.
Built by Web Innoventix
Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.
Get a free quoteSimilar components
Browse all →
Simple Accordion
OriginalA clean single-column accordion, one panel open at a time, chevron rotates.

Multi Accordion
OriginalAn accordion where multiple panels can be open at once.

Plus Minus Accordion
OriginalAccordion with a plus/minus icon that toggles per item.

Bordered Accordion
OriginalBordered card-style accordion panels with rounded corners.

Filled Accordion
OriginalFilled, tinted accordion panels; the active one is highlighted.

FAQ Two Col Accordion
OriginalA two-column FAQ accordion layout.

Numbered Accordion
OriginalA numbered-step accordion (01, 02, 03...).
Icon Left Accordion
OriginalAccordion with a leading icon per item and a right chevron.

Gradient Active Accordion
OriginalAccordion where the open panel gets a soft gradient background.

Nested Accordion
OriginalAn accordion with nested sub-accordions.

Flush Accordion
OriginalA borderless, flush accordion separated by hairlines.

Pill Accordion
OriginalAccordion with rounded pill-shaped headers.

