components/we-inc/single-open-react-accordion-with-aria-controls.tsx
import { useId, useState } from "react";
const sections = [
{ title: "Return window", body: "Unused gear can come back within 60 days of delivery. Tents and sleeping bags that have been slept in once are still accepted if they are clean and dry." },
{ title: "Repairs before replacements", body: "Torn seams, stuck zips and snapped poles are fixed free in our Kendal workshop for the life of the product. Most repairs are posted back inside ten working days." },
{ title: "How refunds are paid", body: "Refunds go back to the card you paid with within three working days of the parcel reaching us. Store credit is added instantly if you prefer it." },
{ title: "Items we cannot take back", body: "Worn base layers, opened gas canisters and anything cut to length, such as guy line or webbing, cannot be returned for hygiene and safety reasons." },
];
export default function SingleOpenReactAccordionWithAriaControls() {
const [open, setOpen] = useState<number | null>(0);
const base = useId();
return (
<section className="soa bg-white px-5 py-16 sm:py-20">
<style>{`
.soa-panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .3s cubic-bezier(.2,.7,.2,1); }
.soa-panel[data-open] { grid-template-rows: 1fr; }
@media (prefers-reduced-motion: reduce) { .soa-panel, .soa-chev { transition: none !important; } }
`}</style>
<div className="mx-auto max-w-2xl">
<h2 className="text-2xl font-semibold tracking-tight text-slate-900 sm:text-3xl">Returns and repairs</h2>
<p className="mt-2 text-[15px] text-slate-600">Fellside Outfitters. One section opens at a time.</p>
<div className="mt-8 space-y-3">
{sections.map((s, i) => {
const isOpen = open === i;
const btn = `${base}-b${i}`;
const panel = `${base}-p${i}`;
return (
<div key={s.title} className={`rounded-xl border transition-colors ${isOpen ? "border-emerald-600 bg-emerald-50/60" : "border-slate-200 bg-white"}`}>
<h3>
<button
id={btn}
type="button"
aria-expanded={isOpen}
aria-controls={panel}
onClick={() => setOpen(isOpen ? null : i)}
className="flex min-h-[56px] w-full items-center justify-between gap-4 rounded-xl px-5 py-4 text-left text-base font-semibold text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-700"
>
{s.title}
<svg className={`soa-chev h-5 w-5 shrink-0 transition-transform duration-300 ${isOpen ? "rotate-180 text-emerald-700" : "text-slate-500"}`} viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.8" aria-hidden="true"><path d="M5 7.5l5 5 5-5" strokeLinecap="round" strokeLinejoin="round" /></svg>
</button>
</h3>
<div id={panel} role="region" aria-labelledby={btn} data-open={isOpen ? "" : undefined} className="soa-panel">
<div className="overflow-hidden" {...(isOpen ? {} : { inert: "" })}>
<p className="px-5 pb-5 text-[15px] leading-relaxed text-slate-700">{s.body}</p>
</div>
</div>
</div>
);
})}
</div>
</div>
</section>
);
}