ドロップダウン
stimeo--dropdown
ボタンで開閉する領域。開閉状態の読み上げ、Esc、外側クリックを引き受けます。
ボタンで領域を開閉する、いちばん素直な開閉です。中身は自由で、リンク集でもフォームでも構いません。開いているかどうかは読み上げにも伝わります。Esc で閉じるとフォーカスはボタンへ戻り、外側をクリックしても閉じます。矢印キーで項目を渡り歩くアプリのメニューが要るなら、メニューのほうを使ってください。
実行中
キーボード操作
| キー | 動作 |
|---|---|
| Enter / Space | トリガーにフォーカスがある状態でメニューを開閉する(ネイティブ button)。 |
| Esc | メニューを閉じ、フォーカスをトリガーへ戻す。 |
<%# Markup for the dropdown demo.
The single source used for both the live render and the copy-paste code.
Connect to stimeo--dropdown via data-controller / data-*-target / data-action.
The behavior is the library's; the look (classes) is the Playground's CSS. %>
<div class="dropdown" data-controller="stimeo--dropdown">
<button
type="button"
class="demo-trigger"
aria-expanded="false"
data-stimeo--dropdown-target="trigger"
data-action="click->stimeo--dropdown#toggle">
<%= t("components.dropdown.demo.trigger") %>
</button>
<%# The toggled menu. Starts hidden (closed).
Each placeholder link closes the menu and prevents navigation so the event
readout stays visible. Real destination links can omit :prevent.
(The links are inside the controller element, so an outside click won't close
it — wire it explicitly.) %>
<ul class="dropdown__menu" data-stimeo--dropdown-target="menu" hidden>
<li><a class="demo-menuitem" href="#profile"
data-action="click->stimeo--dropdown#close:prevent"><%= t(
"components.dropdown.demo.profile"
) %></a></li>
<li><a class="demo-menuitem" href="#settings"
data-action="click->stimeo--dropdown#close:prevent"><%= t(
"components.dropdown.demo.settings"
) %></a></li>
<li><a class="demo-menuitem" href="#sign-out"
data-action="click->stimeo--dropdown#close:prevent"><%= t(
"components.dropdown.demo.sign_out"
) %></a></li>
</ul>
</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 dropdown demo.
* Stimeo UI itself ships behavior only (no CSS), so styling that makes the demo
* readable lives here. When adding a new component, create its
* demos/<name>/demo.css the same way.
*/
.dropdown {
position: relative;
display: inline-block;
}
.dropdown__menu {
position: absolute;
left: 0;
margin: 0.25rem 0 0;
padding: 0.25rem;
list-style: none;
min-width: 12rem;
background: var(--surface-card);
border: 1px solid var(--border-strong);
border-radius: 0.375rem;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.dropdown__menu li {
margin: 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--dropdown']");
for (const name of ["open", "close"]) {
stateRoot?.addEventListener(`stimeo--dropdown:${name}`, (event) => {
const reason = event.detail?.reason;
if (stateReadout) {
stateReadout.textContent = reason
? `stimeo--dropdown:${name} · reason: ${reason}`
: `stimeo--dropdown:${name}`;
}
});
}
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--dropdown"
ターゲット
| 名前 | 説明 | 属性 |
|---|---|---|
trigger
必須
|
メニューを開閉するボタン。aria-expanded で開閉状態を反映する。 |
data-stimeo--dropdown-target="trigger" |
menu
必須
|
開示される領域。hidden 属性で表示状態を反映する。 | data-stimeo--dropdown-target="menu" |
アクション
| 名前 | 説明 | アクション |
|---|---|---|
close
|
メニューを隠し、トリガーの aria-expanded を false にする。 |
stimeo--dropdown#close |
open
|
メニューを表示し、トリガーの aria-expanded を true にする。 |
stimeo--dropdown#open |
toggle
|
メニューの開閉を切り替える。 | stimeo--dropdown#toggle |
イベント
| 名前 | 説明 | イベント |
|---|---|---|
close
|
メニューを閉じた直後に発火。detail の reason に、何がそうさせたかを載せる。 | stimeo--dropdown:close |
open
|
メニューを開いた直後に発火。detail の reason に、何がそうさせたかを載せる。 | stimeo--dropdown:open |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
aria-expanded |
トリガー | メニューが開いている間は "true"、閉じると "false"。 |
hidden |
メニュー | 開くと外れ、閉じると付与される。 |