Accordion
アコーディオン要素を作成できるUIコンポーネントです。
<details> / <summary> ではなく、<div> / <button> ベースで構成されており、JavaScriptで開閉アニメーションを制御します。
パネルには hidden="until-found" を活用しているため、アコーディオンが閉じた状態でもブラウザのページ内検索でコンテンツを検出でき、ヒット時に自動的にパネルが展開されます。
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.
<Accordion.Root> <Accordion.Item> <Accordion.Heading> <Accordion.Button p='20'>Accordion Example</Accordion.Button> </Accordion.Heading> <Accordion.Panel p='20' pt='5'> <Dummy length='l' /> </Accordion.Panel> </Accordion.Item> <Accordion.Item bd-t> <Accordion.Heading> <Accordion.Button p='20'>Accordion Example 2</Accordion.Button> </Accordion.Heading> <Accordion.Panel p='20' pt='5'> <Dummy length='l' /> </Accordion.Panel> </Accordion.Item></Accordion.Root>基本構造
div.l--stack.c--accordion div.c--accordion_item div.c--accordion_heading[role="heading"] button.c--accordion_button[aria-controls="{id}"][aria-expanded="false"] Accordion Label span.c--accordion_icon.a--icon[aria-hidden="true"] div.c--accordion_panel#id[hidden="until-found"] div.c--accordion_content(.l--flow) ...Contents...<Accordion.Button> には、末尾に <Accordion.Icon> が自動的に配置されます。
アイコンはCSSの疑似要素(::before / ::after)で描画されるため、SVGの指定は不要です。
ソースコード
@layer lism.modules {
.c--accordion {
--duration: var(--acc-duration, 0.25s);
}
.c--accordion_item {
--_panel-h: 0px;
--_icon-transform: rotate(0deg);
}
/* 開いている時 */
.c--accordion_item[data-opened] {
--_icon-transform: rotate(90deg);
--_panel-h: auto; /* Note: アニメーション時、jsでセットされる */
}
/* .c--accordion_button {} */
.c--accordion_panel {
height: var(--_panel-h);
transition: height var(--duration);
}
/* コンテンツの paddingは __panel ではなくこっちにつける */
/* .c--accordion_content {} */
/* パネルが完全に閉じている時にabsoluteで飛ばしておくと、Chromeでも検索時に正常にハイライトされるようになる(なぜかは不明) */
[hidden] > .c--accordion_content {
position: absolute;
}
/* アイコンの描画 */
.c--accordion_icon {
display: grid;
width: 1em;
height: 1em;
&::before,
&::after {
content: '';
display: block;
grid-area: 1 / 1;
background-color: currentColor;
}
&::before {
width: 0.1em;
height: 100%;
transition: transform var(--duration);
transform: var(--_icon-transform);
}
&::after {
height: 0.1em;
width: 100%;
}
}
/* フォーカス時のアウトラインを少し内側に寄せる( overflow:hidden によって途切れてしまうので) */
.c--accordion_button:focus-visible {
outline: solid 2px currentColor;
outline-color: revert;
outline-offset: -3px; /* offset 調整だけだとブラウザ間の差が大きい */
}
/* --- 「視差効果を減らす」設定を考慮 --- */
@media (prefers-reduced-motion: reduce) {
.c--accordion_item {
--duration: 0s;
}
}
/* --- JSオフ環境の考慮 --- */
@media (scripting: none) {
.c--accordion_panel {
height: auto !important;
content-visibility: visible !important;
}
.c--accordion_content {
position: static !important;
}
}
} Import
@lism-css/ui パッケージで提供しています。
import { Accordion } from '@lism-css/ui/react'; 以下のコンポーネントが利用できます。
<Accordion.Root>: 複数のアコーディオンアイテムをラップするルート要素(Stackベース)<Accordion.Item>: 個別のアコーディオン要素<Accordion.Heading>: 見出しエリアのラッパー(デフォルトは<div role="heading">)<Accordion.Button>: 開閉トリガーのボタン(末尾に<Accordion.Icon>を自動で含みます)<Accordion.Icon>: 開閉状態を示すアイコン(CSS疑似要素で描画)<Accordion.Panel>: 開閉されるコンテンツパネル(hidden="until-found"を使用)
Props
| プロパティ | 説明 |
|---|---|
<Accordion.Root> allowMultiple | 複数のアコーディオンを同時に展開できるようにします。デフォルトでは一つしか展開できません。 |
<Accordion.Heading> as | 見出しのHTMLタグを指定します。デフォルトは div(role="heading" が自動付与されます)。h2〜h6 を指定すると role は付与されません。 |
<Accordion.Panel> flow | パネル内のコンテンツ領域(__content)に渡すフローレイアウトの設定。 |
Examples
見出しタグを変更する
<Accordion.Heading> はデフォルトでは <div role="heading"> として出力されますが、as に h3 などの見出しタグを指定することで、実際の見出し要素として出力できます。
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.
<Accordion.Root bd> <Accordion.Item> <Accordion.Heading as='h3'> <Accordion.Button p='20'>Accordion Label</Accordion.Button> </Accordion.Heading> <Accordion.Panel p='20' pt='5'> <Dummy length='l' /> </Accordion.Panel> </Accordion.Item> <Accordion.Item bd-t> <Accordion.Heading as='h3'> <Accordion.Button p='20'>Accordion Label</Accordion.Button> </Accordion.Heading> <Accordion.Panel p='20' pt='5'> <Dummy length='l' /> </Accordion.Panel> </Accordion.Item></Accordion.Root>アニメーション時間を変更し、複数同時展開を許可する
アコーディオンの開閉アニメーションの速度は --duration 変数で管理されており、デフォルトは 0.25s です。
デフォルトでは、同じ <Accordion.Root> 内で一つのアコーディオンしか同時に展開できません。
allowMultiple を指定することで、複数を同時に展開できるようになります。
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint. Occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis undeomnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam.
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.
<Accordion.Root g='1px' allowMultiple> <Accordion.Item style={{'--duration': '.15s'}}> <Accordion.Heading> <Accordion.Button p='20'>Accordion Label 1</Accordion.Button> </Accordion.Heading> <Accordion.Panel p='20' pt='5'> ... </Accordion.Panel> </Accordion.Item> <Accordion.Item style={{'--duration': '.15s'}}> <Accordion.Heading> <Accordion.Button p='20'>Accordion Label 2</Accordion.Button> </Accordion.Heading> <Accordion.Panel p='20' pt='5'> ... </Accordion.Panel> </Accordion.Item> ...</Accordion.Root>