Accordion basic
details/summary + JS によるアコーディオン。
2023年12月以降、すべてのブラウザーで利用可能です。
- Baseline
- BaselineWidely available
- 対応ブラウザ
- Chrome120+Edge120+Firefox117+Safari17.2+
プレビューを読み込み中
<div class="accordion-basic js-accordion-basic">
<div class="accordion-basic__list">
<details class="accordion-basic__item js-accordion-basic-item">
<summary class="accordion-basic__trigger">
<span class="accordion-basic__trigger-text">項目1</span>
<span class="accordion-basic__icon" aria-hidden="true">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M5 8l5 5 5-5"
stroke="currentColor"
stroke-width="1.8"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
</summary>
<div class="accordion-basic__panel js-accordion-basic-panel">
<div class="accordion-basic__panel-inner">
<ul class="accordion-basic__menu">
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク1</a>
</li>
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク2</a>
</li>
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク3</a>
</li>
</ul>
</div>
</div>
</details>
<details class="accordion-basic__item js-accordion-basic-item">
<summary class="accordion-basic__trigger">
<span class="accordion-basic__trigger-text">項目2</span>
<span class="accordion-basic__icon" aria-hidden="true">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M5 8l5 5 5-5"
stroke="currentColor"
stroke-width="1.8"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
</summary>
<div class="accordion-basic__panel js-accordion-basic-panel">
<div class="accordion-basic__panel-inner">
<ul class="accordion-basic__menu">
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク1</a>
</li>
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク2</a>
</li>
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク3</a>
</li>
</ul>
</div>
</div>
</details>
<details class="accordion-basic__item js-accordion-basic-item">
<summary class="accordion-basic__trigger">
<span class="accordion-basic__trigger-text">項目3</span>
<span class="accordion-basic__icon" aria-hidden="true">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M5 8l5 5 5-5"
stroke="currentColor"
stroke-width="1.8"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
</summary>
<div class="accordion-basic__panel js-accordion-basic-panel">
<div class="accordion-basic__panel-inner">
<ul class="accordion-basic__menu">
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク1</a>
</li>
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク2</a>
</li>
</ul>
</div>
</div>
</details>
</div>
</div>
<div class="accordion-basic js-accordion-basic">
<div class="accordion-basic__list">
<details class="accordion-basic__item js-accordion-basic-item">
<summary class="accordion-basic__trigger">
<span class="accordion-basic__trigger-text">項目1</span>
<span class="accordion-basic__icon" aria-hidden="true">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M5 8l5 5 5-5"
stroke="currentColor"
stroke-width="1.8"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
</summary>
<div class="accordion-basic__panel js-accordion-basic-panel">
<div class="accordion-basic__panel-inner">
<ul class="accordion-basic__menu">
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク1</a>
</li>
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク2</a>
</li>
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク3</a>
</li>
</ul>
</div>
</div>
</details>
<details class="accordion-basic__item js-accordion-basic-item">
<summary class="accordion-basic__trigger">
<span class="accordion-basic__trigger-text">項目2</span>
<span class="accordion-basic__icon" aria-hidden="true">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M5 8l5 5 5-5"
stroke="currentColor"
stroke-width="1.8"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
</summary>
<div class="accordion-basic__panel js-accordion-basic-panel">
<div class="accordion-basic__panel-inner">
<ul class="accordion-basic__menu">
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク1</a>
</li>
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク2</a>
</li>
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク3</a>
</li>
</ul>
</div>
</div>
</details>
<details class="accordion-basic__item js-accordion-basic-item">
<summary class="accordion-basic__trigger">
<span class="accordion-basic__trigger-text">項目3</span>
<span class="accordion-basic__icon" aria-hidden="true">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M5 8l5 5 5-5"
stroke="currentColor"
stroke-width="1.8"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
</summary>
<div class="accordion-basic__panel js-accordion-basic-panel">
<div class="accordion-basic__panel-inner">
<ul class="accordion-basic__menu">
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク1</a>
</li>
<li class="accordion-basic__menu-item">
<a class="accordion-basic__link" href="#">リンク2</a>
</li>
</ul>
</div>
</div>
</details>
</div>
</div>
/* reset */
:where(ul).accordion-basic__menu {
padding: 0;
margin: 0;
list-style: none;
}
:where(summary).accordion-basic__trigger,
:where(a).accordion-basic__link {
font: inherit;
text-decoration: none;
}
:where(summary).accordion-basic__trigger {
cursor: pointer;
list-style: none;
&::-webkit-details-marker {
display: none;
}
}
/* snippet */
.accordion-basic {
--accordion-border: #e7e7e7;
--accordion-trigger-color: #364153;
--accordion-link-color: #5e6775;
--accordion-icon-color: #6a7282;
--accordion-accent: #2b36f5;
--accordion-focus-color: var(--accordion-accent);
--accordion-radius: 4px;
--accordion-duration: 0.3s;
width: 100%;
max-width: 320px;
}
.accordion-basic__list {
display: grid;
}
.accordion-basic__item {
border-bottom: 1px solid var(--accordion-border);
}
.accordion-basic__trigger {
display: grid;
grid-template-columns: 1fr auto;
column-gap: 12px;
align-items: center;
min-height: var(--size-touch-target, 40px);
padding-block: 12px;
font-size: 14px;
font-weight: 700;
line-height: 1.5;
color: var(--accordion-trigger-color);
border-radius: var(--accordion-radius);
}
.accordion-basic__icon {
display: grid;
place-items: center;
color: var(--accordion-icon-color);
transition: transform var(--accordion-duration);
}
:is(
.accordion-basic:not(.accordion-basic--enhanced) .accordion-basic__item[open],
.accordion-basic--enhanced .accordion-basic__item--expanded
) {
& .accordion-basic__icon {
transform: rotate(180deg);
}
}
.accordion-basic__menu {
display: grid;
row-gap: 4px;
padding-block: 4px 12px;
}
.accordion-basic__link {
display: grid;
align-items: center;
min-height: var(--size-touch-target, 40px);
font-size: 14px;
line-height: 1.5;
color: var(--accordion-link-color);
border-radius: var(--accordion-radius);
}
/* open / close animation(JS 実行時) */
.accordion-basic--enhanced .accordion-basic__panel {
display: grid;
grid-template-rows: 0fr;
overflow: hidden;
}
.accordion-basic--enhanced .accordion-basic__panel-inner {
min-height: 0;
overflow: hidden;
}
.accordion-basic--enhanced
.accordion-basic__item--expanded
.accordion-basic__panel {
grid-template-rows: 1fr;
}
.accordion-basic--enhanced
.accordion-basic__item--animating
.accordion-basic__panel {
transition: grid-template-rows var(--accordion-duration) ease;
}
/* focus */
.accordion-basic .accordion-basic__trigger,
.accordion-basic .accordion-basic__link {
&:focus:not(:focus-visible) {
outline: none;
}
&:focus-visible {
outline: 2px solid var(--accordion-focus-color);
outline-offset: -2px;
}
}
/* reduced motion */
@media (prefers-reduced-motion: reduce) {
.accordion-basic__icon,
.accordion-basic--enhanced
.accordion-basic__item--animating
.accordion-basic__panel {
transition: none;
}
}
/* reset */
:where(ul).accordion-basic__menu {
padding: 0;
margin: 0;
list-style: none;
}
:where(summary).accordion-basic__trigger,
:where(a).accordion-basic__link {
font: inherit;
text-decoration: none;
}
:where(summary).accordion-basic__trigger {
cursor: pointer;
list-style: none;
&::-webkit-details-marker {
display: none;
}
}
/* snippet */
.accordion-basic {
--accordion-border: #e7e7e7;
--accordion-trigger-color: #364153;
--accordion-link-color: #5e6775;
--accordion-icon-color: #6a7282;
--accordion-accent: #2b36f5;
--accordion-focus-color: var(--accordion-accent);
--accordion-radius: 4px;
--accordion-duration: 0.3s;
width: 100%;
max-width: 320px;
}
.accordion-basic__list {
display: grid;
}
.accordion-basic__item {
border-bottom: 1px solid var(--accordion-border);
}
.accordion-basic__trigger {
display: grid;
grid-template-columns: 1fr auto;
column-gap: 12px;
align-items: center;
min-height: var(--size-touch-target, 40px);
padding-block: 12px;
font-size: 14px;
font-weight: 700;
line-height: 1.5;
color: var(--accordion-trigger-color);
border-radius: var(--accordion-radius);
}
.accordion-basic__icon {
display: grid;
place-items: center;
color: var(--accordion-icon-color);
transition: transform var(--accordion-duration);
}
:is(
.accordion-basic:not(.accordion-basic--enhanced) .accordion-basic__item[open],
.accordion-basic--enhanced .accordion-basic__item--expanded
) {
& .accordion-basic__icon {
transform: rotate(180deg);
}
}
.accordion-basic__menu {
display: grid;
row-gap: 4px;
padding-block: 4px 12px;
}
.accordion-basic__link {
display: grid;
align-items: center;
min-height: var(--size-touch-target, 40px);
font-size: 14px;
line-height: 1.5;
color: var(--accordion-link-color);
border-radius: var(--accordion-radius);
}
/* open / close animation(JS 実行時) */
.accordion-basic--enhanced .accordion-basic__panel {
display: grid;
grid-template-rows: 0fr;
overflow: hidden;
}
.accordion-basic--enhanced .accordion-basic__panel-inner {
min-height: 0;
overflow: hidden;
}
.accordion-basic--enhanced
.accordion-basic__item--expanded
.accordion-basic__panel {
grid-template-rows: 1fr;
}
.accordion-basic--enhanced
.accordion-basic__item--animating
.accordion-basic__panel {
transition: grid-template-rows var(--accordion-duration) ease;
}
/* focus */
.accordion-basic .accordion-basic__trigger,
.accordion-basic .accordion-basic__link {
&:focus:not(:focus-visible) {
outline: none;
}
&:focus-visible {
outline: 2px solid var(--accordion-focus-color);
outline-offset: -2px;
}
}
/* reduced motion */
@media (prefers-reduced-motion: reduce) {
.accordion-basic__icon,
.accordion-basic--enhanced
.accordion-basic__item--animating
.accordion-basic__panel {
transition: none;
}
}
(function () {
const reducedMotion = matchMedia("(prefers-reduced-motion: reduce)");
function getTransitionMs(root) {
const duration = getComputedStyle(root)
.getPropertyValue("--accordion-duration")
.trim();
if (!duration) return 0;
const ms = duration.endsWith("ms")
? parseFloat(duration)
: parseFloat(duration) * 1000;
return Number.isFinite(ms) ? ms : 0;
}
function panelHasTransition(panel) {
return getComputedStyle(panel)
.transitionDuration.split(",")
.some((duration) => parseFloat(duration) > 0);
}
function initAccordionBasic(root) {
if (root.dataset.accordionBasicInit === "true") return;
root.dataset.accordionBasicInit = "true";
root.classList.add("accordion-basic--enhanced");
root.querySelectorAll(".js-accordion-basic-item").forEach((item) => {
const summary = item.querySelector(".accordion-basic__trigger");
const panel = item.querySelector(".js-accordion-basic-panel");
if (!summary || !panel) return;
let transitionHandler = null;
let cancelHandler = null;
let fallbackTimer = 0;
function cleanupTransition() {
if (transitionHandler) {
panel.removeEventListener("transitionend", transitionHandler);
transitionHandler = null;
}
if (cancelHandler) {
panel.removeEventListener("transitioncancel", cancelHandler);
cancelHandler = null;
}
if (fallbackTimer) {
clearTimeout(fallbackTimer);
fallbackTimer = 0;
}
}
function setExpanded(open) {
item.classList.toggle("accordion-basic__item--expanded", open);
item.toggleAttribute("open", open);
}
if (item.hasAttribute("open")) {
item.classList.add("accordion-basic__item--expanded");
}
summary.addEventListener("click", (event) => {
event.preventDefault();
const willOpen = !item.classList.contains(
"accordion-basic__item--expanded",
);
if (reducedMotion.matches) {
cleanupTransition();
item.classList.remove("accordion-basic__item--animating");
setExpanded(willOpen);
return;
}
cleanupTransition();
item.classList.add("accordion-basic__item--animating");
void panel.offsetHeight;
let finished = false;
const finish = () => {
if (finished) return;
finished = true;
cleanupTransition();
item.classList.remove("accordion-basic__item--animating");
setExpanded(willOpen);
};
if (!panelHasTransition(panel)) {
setExpanded(willOpen);
finish();
return;
}
const onTransitionDone = (transitionEvent) => {
if (transitionEvent.target !== panel) return;
if (transitionEvent.propertyName !== "grid-template-rows") return;
finish();
};
transitionHandler = onTransitionDone;
cancelHandler = onTransitionDone;
panel.addEventListener("transitionend", transitionHandler);
panel.addEventListener("transitioncancel", cancelHandler);
fallbackTimer = window.setTimeout(finish, getTransitionMs(root) + 50);
if (willOpen) {
item.setAttribute("open", "");
requestAnimationFrame(() => {
requestAnimationFrame(() => {
item.classList.add("accordion-basic__item--expanded");
});
});
} else {
item.classList.remove("accordion-basic__item--expanded");
}
});
});
}
function initAll() {
document
.querySelectorAll(".js-accordion-basic")
.forEach(initAccordionBasic);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initAll);
} else {
initAll();
}
})();
(function () {
const reducedMotion = matchMedia("(prefers-reduced-motion: reduce)");
function getTransitionMs(root) {
const duration = getComputedStyle(root)
.getPropertyValue("--accordion-duration")
.trim();
if (!duration) return 0;
const ms = duration.endsWith("ms")
? parseFloat(duration)
: parseFloat(duration) * 1000;
return Number.isFinite(ms) ? ms : 0;
}
function panelHasTransition(panel) {
return getComputedStyle(panel)
.transitionDuration.split(",")
.some((duration) => parseFloat(duration) > 0);
}
function initAccordionBasic(root) {
if (root.dataset.accordionBasicInit === "true") return;
root.dataset.accordionBasicInit = "true";
root.classList.add("accordion-basic--enhanced");
root.querySelectorAll(".js-accordion-basic-item").forEach((item) => {
const summary = item.querySelector(".accordion-basic__trigger");
const panel = item.querySelector(".js-accordion-basic-panel");
if (!summary || !panel) return;
let transitionHandler = null;
let cancelHandler = null;
let fallbackTimer = 0;
function cleanupTransition() {
if (transitionHandler) {
panel.removeEventListener("transitionend", transitionHandler);
transitionHandler = null;
}
if (cancelHandler) {
panel.removeEventListener("transitioncancel", cancelHandler);
cancelHandler = null;
}
if (fallbackTimer) {
clearTimeout(fallbackTimer);
fallbackTimer = 0;
}
}
function setExpanded(open) {
item.classList.toggle("accordion-basic__item--expanded", open);
item.toggleAttribute("open", open);
}
if (item.hasAttribute("open")) {
item.classList.add("accordion-basic__item--expanded");
}
summary.addEventListener("click", (event) => {
event.preventDefault();
const willOpen = !item.classList.contains(
"accordion-basic__item--expanded",
);
if (reducedMotion.matches) {
cleanupTransition();
item.classList.remove("accordion-basic__item--animating");
setExpanded(willOpen);
return;
}
cleanupTransition();
item.classList.add("accordion-basic__item--animating");
void panel.offsetHeight;
let finished = false;
const finish = () => {
if (finished) return;
finished = true;
cleanupTransition();
item.classList.remove("accordion-basic__item--animating");
setExpanded(willOpen);
};
if (!panelHasTransition(panel)) {
setExpanded(willOpen);
finish();
return;
}
const onTransitionDone = (transitionEvent) => {
if (transitionEvent.target !== panel) return;
if (transitionEvent.propertyName !== "grid-template-rows") return;
finish();
};
transitionHandler = onTransitionDone;
cancelHandler = onTransitionDone;
panel.addEventListener("transitionend", transitionHandler);
panel.addEventListener("transitioncancel", cancelHandler);
fallbackTimer = window.setTimeout(finish, getTransitionMs(root) + 50);
if (willOpen) {
item.setAttribute("open", "");
requestAnimationFrame(() => {
requestAnimationFrame(() => {
item.classList.add("accordion-basic__item--expanded");
});
});
} else {
item.classList.remove("accordion-basic__item--expanded");
}
});
});
}
function initAll() {
document
.querySelectorAll(".js-accordion-basic")
.forEach(initAccordionBasic);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initAll);
} else {
initAll();
}
})();