Skip to content

Instantly share code, notes, and snippets.

@kobitoDevelopment
Last active March 11, 2025 13:41
Show Gist options
  • Save kobitoDevelopment/b06522bfcec2ccb52976ea8fe6598161 to your computer and use it in GitHub Desktop.
Save kobitoDevelopment/b06522bfcec2ccb52976ea8fe6598161 to your computer and use it in GitHub Desktop.
.accordionTrigger {
position: relative;
width: 100%;
}
.accordionTrigger[aria-expanded="true"] + .accordionBody {
grid-template-rows: 1fr;
}
.accordionTrigger[aria-expanded="false"] + .accordionBody {
grid-template-rows: 0fr;
}
.accordionBody {
display: grid;
transition: grid-template-rows 0.3s;
}
.accordionInner {
overflow: hidden;
}
"use client";
import { useEffect, useRef, useState } from "react";
import styles from "./Accordion.module.css";
type AccordionProps = {
buttonName: string;
panelName: string;
open?: boolean;
title: React.ReactNode;
icon?: React.ReactNode;
content: React.ReactNode;
};
export default function Accordion({ buttonName, panelName, open = false, title, icon, content }: AccordionProps) {
const accordionTrigger = useRef<HTMLButtonElement | null>(null);
const accordionBody = useRef<HTMLDivElement | null>(null);
const [isOpen, setIsOpen] = useState(open);
useEffect(() => {
if (accordionBody.current && !open) {
accordionBody.current.setAttribute("hidden", "until-found");
}
}, [open]);
useEffect(() => {
const observer = new MutationObserver((mutationsList) => {
mutationsList.forEach(() => {
if (accordionBody.current && accordionTrigger.current && !accordionBody.current.getAttribute("hidden") && accordionTrigger.current.getAttribute("aria-expanded") === "false") {
setIsOpen((prev) => !prev);
}
});
});
if (accordionBody.current) {
observer.observe(accordionBody.current, { attributes: true });
}
return () => {
observer.disconnect();
};
}, []);
const changeExpanded = () => {
setIsOpen((prev) => !prev);
if (accordionBody.current) {
if (!isOpen) {
accordionBody.current.removeAttribute("hidden");
} else {
setTimeout(() => {
accordionBody.current?.setAttribute("hidden", "until-found");
}, 300);
}
}
};
return (
<>
<button id={buttonName} ref={accordionTrigger} className={styles.accordionTrigger} aria-expanded={isOpen} role="tab" aria-controls={panelName} type="button" onClick={changeExpanded}>
{title}
{icon}
</button>
<div id={panelName} ref={accordionBody} className={styles.accordionBody} role="tabpanel" aria-labelledby={buttonName}>
<div className={styles.accordionInner}>{content}</div>
</div>
</>
);
}
import Accordion from "@/app/components/accordion/Accordion";
export default function TestPage() {
return <Accordion buttonName="accordion-button-1" panelName="accordion-panel-1" title={<span>アコーディオンのタイトル</span>} icon={<span>▼</span>} content={<p>ここにアコーディオンの内容が入ります。</p>} />;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment