Skip to content

Instantly share code, notes, and snippets.

@mg901
Last active June 10, 2026 18:26
Show Gist options
  • Select an option

  • Save mg901/9b54778289db3547211235abd182d290 to your computer and use it in GitHub Desktop.

Select an option

Save mg901/9b54778289db3547211235abd182d290 to your computer and use it in GitHub Desktop.
import { useId, useRef, useState, useCallback } from "react";
export default function Accordion({ sections }) {
const baseId = useId();
const buttonsRef = useRef([]);
const [openSections, setOpenSections] = useState(new Set());
const n = sections.length;
const handleKeyDown = useCallback((e, index) => {
const { key } = e;
if (key === "ArrowUp") {
const prevIndex = (index - 1 + n) % n;
buttonsRef.current[prevIndex].focus();
e.preventDefault();
} else if (key === "ArrowDown") {
const nextIndex = (index + 1) % n;
buttonsRef.current[nextIndex].focus();
e.preventDefault();
} else if (key === "Home") {
buttonsRef.current[0].focus();
e.preventDefault();
} else if (key === "End") {
buttonsRef.current[n - 1].focus();
e.preventDefault();
}
}, []);
const setActive = useCallback((index) => {
setOpenSections((prev) => {
const next = new Set(prev);
if (next.has(index)) {
next.delete(index);
} else {
next.add(index);
}
return next;
});
}, []);
return (
<div className="accordion">
{sections.map(({ value, title, contents }, index) => {
const isExpanded = openSections.has(index);
const headerId = `${baseId}-header-${value}`;
const panelId = `${baseId}-panel-${value}`;
return (
<div className="accordion-item" key={value}>
<button
id={headerId}
aria-controls={panelId}
aria-expanded={isExpanded}
className="accordion-item-title"
onClick={() => setActive(index)}
onKeyDown={(e) => handleKeyDown(e, index)}
ref={(node) => {
if (node) {
buttonsRef.current[index] = node;
}
}}
>
{title}{" "}
<span
aria-hidden={true}
data-active={isExpanded}
className="accordion-icon"
/>
</button>
<div
id={panelId}
aria-labelledby={headerId}
role="region"
hidden={!isExpanded}
>
{contents}
</div>
</div>
);
})}
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment