アコーディオン
stimeo--accordion
独立して開閉できる折りたたみセクション。見出しの間は矢印キーで行き来できます。
見出しを押すと中身が開く、折りたたみのセクションです。WAI-ARIA の Accordion パターンの実装で、複数のセクションを同時に開いたままにできます。どの見出しがいま開いているかは読み上げにも伝わります。見出しの間は矢印キーで行き来できます。
実行中
ご注文から2〜3営業日以内に発送いたします。
商品到着後30日以内は送料無料で返品を承ります。
1年間のメーカー限定保証が付帯します。
キーボード操作
| キー | 動作 |
|---|---|
| Enter / Space | フォーカス中のセクションを開閉する(ネイティブ button)。 |
| ↓ / ↑ | ヘッダ間でフォーカスを移動する(端で循環)。 |
| Home / End | 最初 / 最後のヘッダにフォーカスする。 |
<%# Markup for the accordion (APG Accordion) demo.
stimeo--accordion manages each header's aria-expanded and each panel's hidden,
and allows multiple panels open at once. Arrow keys move focus between headers.
Headers and panels are linked via aria-controls (= the panel's id), and each
panel (role="region") references its header button's id via aria-labelledby.
Each header also carries a "+" / "−" pair, and the library hides the half that
does not belong to its state. The glyphs are decorative, so they are aria-hidden
and the accessible name stays the section title. %>
<div class="accordion" data-controller="stimeo--accordion">
<h3 class="accordion__header">
<button type="button" id="acc-shipping-trigger" class="accordion__trigger"
aria-expanded="false" aria-controls="acc-shipping"
data-stimeo--accordion-target="trigger"
data-action="click->stimeo--accordion#toggle
keydown->stimeo--accordion#onKeydown">
<%= t("components.accordion.demo.shipping_title") %>
<span class="accordion__marker" aria-hidden="true"
data-stimeo--accordion-target="collapsedLabel">+</span>
<span class="accordion__marker" aria-hidden="true" hidden
data-stimeo--accordion-target="expandedLabel">−</span>
</button>
</h3>
<div class="accordion__panel" id="acc-shipping" role="region"
aria-labelledby="acc-shipping-trigger"
data-stimeo--accordion-target="panel" hidden>
<p><%= t("components.accordion.demo.shipping_desc") %></p>
</div>
<h3 class="accordion__header">
<button type="button" id="acc-returns-trigger" class="accordion__trigger"
aria-expanded="false" aria-controls="acc-returns"
data-stimeo--accordion-target="trigger"
data-action="click->stimeo--accordion#toggle
keydown->stimeo--accordion#onKeydown">
<%= t("components.accordion.demo.returns_title") %>
<span class="accordion__marker" aria-hidden="true"
data-stimeo--accordion-target="collapsedLabel">+</span>
<span class="accordion__marker" aria-hidden="true" hidden
data-stimeo--accordion-target="expandedLabel">−</span>
</button>
</h3>
<div class="accordion__panel" id="acc-returns" role="region"
aria-labelledby="acc-returns-trigger"
data-stimeo--accordion-target="panel" hidden>
<p><%= t("components.accordion.demo.returns_desc") %></p>
</div>
<h3 class="accordion__header">
<button type="button" id="acc-warranty-trigger" class="accordion__trigger"
aria-expanded="false" aria-controls="acc-warranty"
data-stimeo--accordion-target="trigger"
data-action="click->stimeo--accordion#toggle
keydown->stimeo--accordion#onKeydown">
<%= t("components.accordion.demo.warranty_title") %>
<span class="accordion__marker" aria-hidden="true"
data-stimeo--accordion-target="collapsedLabel">+</span>
<span class="accordion__marker" aria-hidden="true" hidden
data-stimeo--accordion-target="expandedLabel">−</span>
</button>
</h3>
<div class="accordion__panel" id="acc-warranty" role="region"
aria-labelledby="acc-warranty-trigger"
data-stimeo--accordion-target="panel" hidden>
<p><%= t("components.accordion.demo.warranty_desc") %></p>
</div>
</div>
<%# Readout: what the component just reported. Purely visual — the component
already announces what a screen reader needs, so this line is aria-hidden to
keep it from being read twice. The value is a code fragment (API identifiers
and values), so only the label is translated. %>
<p class="demo-readout" aria-hidden="true">
<span><%= t("catalog.readout.events") %></span>
<span class="demo-readout__value" data-readout>—</span>
</p>
/*
* Presentation-only styles for the accordion demo.
* The library owns each header's aria-expanded, each panel's hidden, and the
* hidden of the header's two marker halves. The styling is built here.
*/
.accordion {
width: min(28rem, 100%);
border: 1px solid var(--border);
border-radius: 0.375rem;
}
.accordion__header {
margin: 0;
}
.accordion__trigger {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 0.75rem 1rem;
font-size: 1rem;
text-align: left;
cursor: pointer;
background: var(--surface-card);
border: 0;
border-bottom: 1px solid var(--border);
}
/* The "+" / "−" marker halves each header carries. */
.accordion__marker {
color: var(--muted);
}
.accordion__panel {
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--border);
}
.accordion__panel:last-child,
.accordion__header:last-of-type .accordion__trigger {
border-bottom: 0;
}
// Every move of the open state is reported, with a `reason` saying what drove it
// — a trigger, Escape, a click outside, or a call from your own code. A real app
// subscribes to mirror the state elsewhere or to send an analytics event; this
// demo writes the event name and its reason into the readout so both are visible
// while you interact. `reason: "api"` is the one to watch for: a close you asked
// for comes back to your own listener.
// The page chrome uses some of these controllers too (the nav, the tab strip),
// so everything below is scoped to the demo itself.
const demoStage = document.querySelector(".demo-stage") ?? document;
const stateReadout = demoStage.querySelector("[data-readout]");
const stateRoot = demoStage.querySelector("[data-controller~='stimeo--accordion']");
for (const name of ["open", "close"]) {
stateRoot?.addEventListener(`stimeo--accordion:${name}`, (event) => {
const reason = event.detail?.reason;
if (stateReadout) {
stateReadout.textContent = reason
? `stimeo--accordion:${name} · reason: ${reason}`
: `stimeo--accordion:${name}`;
}
});
}
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--accordion"
ターゲット
| 名前 | 説明 | 属性 |
|---|---|---|
trigger
必須
|
対応するパネルを開閉するヘッダーボタン。aria-controls でパネルと紐づく。 |
data-stimeo--accordion-target="trigger" |
panel
必須
|
トリガーの aria-controls が参照する折りたたみ領域。hidden 属性で状態を反映。 |
data-stimeo--accordion-target="panel" |
expandedLabel
|
パネルが開いている間に出るヘッダの側。hidden は部品が持つ。ヘッダごとに解決し、同じヘッダの collapsedLabel と対になる。 |
data-stimeo--accordion-target="expandedLabel" |
collapsedLabel
|
パネルが閉じている間に出るヘッダの側。hidden は部品が持つ。ヘッダごとに解決し、同じヘッダの expandedLabel と対になる。 |
data-stimeo--accordion-target="collapsedLabel" |
アクション
| 名前 | 説明 | アクション |
|---|---|---|
collapseAll
|
全パネルを一括で閉じる。任意の「すべて閉じる」操作にバインドする。 | stimeo--accordion#collapseAll |
expandAll
|
全パネルを一括で開く。任意の「すべて開く」操作にバインドする。 | stimeo--accordion#expandAll |
onKeydown
|
ヘッダー間でフォーカスを移動。ArrowDown/Up で循環、Home/End で先頭/末尾へ。 | stimeo--accordion#onKeydown |
toggle
|
操作したヘッダーのパネルを開閉し、aria-expanded と hidden を同期する。 |
stimeo--accordion#toggle |
イベント
| 名前 | 説明 | イベント |
|---|---|---|
close
|
パネルを閉じた直後に、動いたペアごとに発火。detail に reason と index / trigger / panel を載せる。 | stimeo--accordion:close |
open
|
パネルを開いた直後に、動いたペアごとに発火。detail に reason と index / trigger / panel を載せる。 | stimeo--accordion:open |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
aria-expanded |
各ヘッダー | パネルが開いている間は "true"、閉じると "false"。 |
hidden |
各パネル | 開くと外れ、閉じると付与される。 |