ナビゲーションメニュー
stimeo--navigation-menu
Disclosure ナビ。リンク集のサブパネルを単一オープン。role=menu は使わない。
stimeo--navigation-menu コントローラは WAI-ARIA の Disclosure ナビゲーションパターンを実装する。各トップ項目のボタンがサブパネルを開閉し(aria-expanded と hidden を同期)、同時に開くのは 1 つだけで、パネルの中身はリンク集(role=menu は使わない)。フォーカスはトラップせず、Tab でリンクを自然に辿れる。Escape は開いているパネルを閉じてトリガーへフォーカスを戻し、外側クリックやナビからのフォーカス離脱でも閉じる。左右キーは tabindex を書き換えずにトップ項目間でフォーカスを移す(自然な Tab 順を保つ)。ホバー開閉は openOnHover(hoverDelay 付き)で opt-in。パネルのレイアウトや装飾は利用側 CSS、動的配置は opt-in の stimeo-ui/positioning に委譲する。矢印ロービングと role=menu のアプリメニューが要るなら menubar を使う。
キーボード操作
| キー | 動作 |
|---|---|
| → / ← | トップ項目間でフォーカスを移動(ループ)。 |
| Esc | 開いているパネルを閉じてトリガーへフォーカスを戻す。 |
| Tab | 自然に移動。ナビ外へ出たら開いているパネルを閉じる。 |
<%# Markup for the navigation-menu demo.
A global nav where each top item opens/closes a sub-panel (a set of links). The
contents are links and don't use role="menu" (APG Disclosure navigation). The
library handles syncing aria-expanded, single-open behavior, closing on Escape /
outside click / focus leaving, and moving between top items with the left/right
keys. Panel placement and styling are the consumer's CSS (demo.css). %>
<nav class="nav-menu" data-controller="stimeo--navigation-menu"
aria-label="<%= t("components.navigation_menu.demo.label") %>">
<ul class="nav-menu__list">
<li class="nav-menu__item">
<button
type="button"
class="nav-menu__trigger"
aria-expanded="false"
aria-controls="nav-menu-products"
data-stimeo--navigation-menu-target="trigger"
data-action="click->stimeo--navigation-menu#toggle
keydown->stimeo--navigation-menu#onTriggerKeydown">
<%= t("components.navigation_menu.demo.products.label") %>
</button>
<div id="nav-menu-products" class="nav-menu__panel"
data-stimeo--navigation-menu-target="panel" hidden>
<a href="#analytics"><%= t(
"components.navigation_menu.demo.products.links.analytics"
) %></a>
<a href="#automation"><%= t(
"components.navigation_menu.demo.products.links.automation"
) %></a>
<a href="#reports"><%= t("components.navigation_menu.demo.products.links.reports") %></a>
</div>
</li>
<li class="nav-menu__item">
<button
type="button"
class="nav-menu__trigger"
aria-expanded="false"
aria-controls="nav-menu-company"
data-stimeo--navigation-menu-target="trigger"
data-action="click->stimeo--navigation-menu#toggle
keydown->stimeo--navigation-menu#onTriggerKeydown">
<%= t("components.navigation_menu.demo.company.label") %>
</button>
<div id="nav-menu-company" class="nav-menu__panel"
data-stimeo--navigation-menu-target="panel" hidden>
<a href="#about"><%= t("components.navigation_menu.demo.company.links.about") %></a>
<a href="#careers"><%= t("components.navigation_menu.demo.company.links.careers") %></a>
</div>
</li>
</ul>
</nav>
/*
* Presentation-only styles for the navigation-menu demo.
* The library toggles the panel's hidden and the top items' aria-expanded. Panel
* placement (directly below the trigger) is static and the consumer's CSS
* responsibility; use stimeo-ui/positioning for dynamic flip.
*/
.nav-menu__list {
display: flex;
gap: 0.5rem;
margin: 0;
padding: 0;
list-style: none;
}
.nav-menu__item {
position: relative;
}
.nav-menu__trigger {
padding: 0.5rem 0.75rem;
font: inherit;
cursor: pointer;
border: 1px solid transparent;
border-radius: 0.375rem;
background: transparent;
color: var(--color-text);
}
.nav-menu__trigger:hover,
.nav-menu__trigger[aria-expanded="true"] {
border-color: var(--border-strong);
background: var(--surface-subtle);
}
.nav-menu__panel {
position: absolute;
top: calc(100% + 0.25rem);
left: 0;
z-index: 10;
display: flex;
flex-direction: column;
min-width: 12rem;
padding: 0.5rem;
background: var(--surface-card);
border: 1px solid var(--border-strong);
border-radius: 0.375rem;
box-shadow: 0 8px 24px rgb(15 23 42 / 0.12);
}
.nav-menu__panel[hidden] {
display: none;
}
.nav-menu__panel a {
padding: 0.4rem 0.5rem;
border-radius: 0.25rem;
color: var(--color-text);
text-decoration: none;
}
.nav-menu__panel a:hover,
.nav-menu__panel a:focus {
background: var(--vital-100);
outline: none;
}
このデモに固有の消費側 JS はありません(挙動はコントローラが担います)。
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。 共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--navigation-menu"
ターゲット
| 名前 | 説明 | 属性 |
|---|---|---|
trigger
必須
|
サブパネルを開閉するトップレベルのディスクロージャーボタン。aria-expanded が同期される。 |
data-stimeo--navigation-menu-target="trigger" |
panel
必須
|
トリガーが制御するリンクのサブパネル(aria-controls/id)。hidden 状態が同期される。 |
data-stimeo--navigation-menu-target="panel" |
値(Values)
| 名前 | 説明 | 属性 |
|---|---|---|
openOnHover
|
ホバーでパネルを開閉するか(オプトイン)。既定値は false。 | data-stimeo--navigation-menu-open-on-hover-value |
hoverDelay
|
ホバーでパネルを開閉するまでの遅延(ミリ秒)。既定値は 150。 | data-stimeo--navigation-menu-hover-delay-value |
アクション
| 名前 | 説明 | アクション |
|---|---|---|
onTriggerKeydown
|
ArrowLeft/ArrowRight でトリガー間のフォーカスを移動する。自然な Tab 順は維持する。 | stimeo--navigation-menu#onTriggerKeydown |
toggle
|
クリックされたトリガーのパネルを開閉する(同時に一つだけ開く)。 | stimeo--navigation-menu#toggle |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
aria-expanded |
トリガー | そのパネルの開閉状態(true/false)。 |
hidden |
パネル | 閉じているときは付与。 |