ナビゲーションメニュー
stimeo--navigation-menu
サイトのナビゲーション。リンクを並べたパネルを 1 つずつ開きます。アプリのメニューではありません。
サイトのナビゲーションです。上の各ボタンがリンクを並べたパネルを開き、開くのは一度に 1 つだけです。中身はただのリンクなので、フォーカスは閉じ込められず、Tab でそのまま辿れます。Esc で開いているパネルを閉じてボタンへフォーカスを戻し、外側のクリックやナビゲーションからフォーカスが出たときも閉じます。左右キーはボタンの間を移動しますが、Tab の順番は自然なままです。ホバーで開く動きは、必要なときだけ遅延つきで有効にできます。矢印キーで渡り歩くアプリのコマンドメニューが要るなら、メニューバーを使ってください。
キーボード操作
| キー | 動作 |
|---|---|
| → / ← | トップ項目間でフォーカスを移動(ループ)。RTL では左右が入れ替わる。 |
| Esc | 開いているパネルを閉じてトリガーへフォーカスを戻す。 |
| Tab | 自然に移動。ナビ外へ出たら開いているパネルを閉じる。 |
<%# Markup for the navigation-menu demo.
Two arrangements are shown.
1. Click-only (the default): 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.
2. Hover (opt-in): openOnHover plus a hoverArea wrapper — the APG "Disclosure
Navigation with Top-Level Links" arrangement, where each <li> holds a real
link, a small disclosure button, and the panel. Hovering anywhere over the
<li> opens that item's panel; every keyboard path stays identical.
Panel placement and styling are the consumer's CSS (demo.css). %>
<p class="nav-menu__caption"><%= t("components.navigation_menu.demo.click_caption") %></p>
<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="demo-baritem"
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 class="demo-menuitem" href="#analytics"><%= t(
"components.navigation_menu.demo.products.links.analytics"
) %></a>
<a class="demo-menuitem" href="#automation"><%= t(
"components.navigation_menu.demo.products.links.automation"
) %></a>
<a class="demo-menuitem" href="#reports"><%= t(
"components.navigation_menu.demo.products.links.reports"
) %></a>
</div>
</li>
<li class="nav-menu__item">
<button
type="button"
class="demo-baritem"
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 class="demo-menuitem" href="#about"><%= t(
"components.navigation_menu.demo.company.links.about"
) %></a>
<a class="demo-menuitem" href="#careers"><%= t(
"components.navigation_menu.demo.company.links.careers"
) %></a>
</div>
</li>
</ul>
</nav>
<p class="nav-menu__caption"><%= t("components.navigation_menu.demo.hover_caption") %></p>
<%# openOnHover is opt-in; hoverArea widens the hover region to the whole <li>, so
hovering the top-level link (not just the disclosure button) opens the panel.
The button keeps its own accessible name so keyboard and SR users can tell the
link and its disclosure apart. %>
<nav class="nav-menu" data-controller="stimeo--navigation-menu"
data-stimeo--navigation-menu-open-on-hover-value="true"
aria-label="<%= t("components.navigation_menu.demo.hover.label") %>">
<ul class="nav-menu__list">
<li class="nav-menu__item" data-stimeo--navigation-menu-target="hoverArea">
<a class="demo-baritem nav-menu__link" href="#solutions">
<%= t("components.navigation_menu.demo.hover.solutions.label") %>
</a>
<button
type="button"
class="demo-baritem nav-menu__disclosure"
aria-expanded="false"
aria-controls="nav-menu-solutions"
data-stimeo--navigation-menu-target="trigger"
data-action="click->stimeo--navigation-menu#toggle
keydown->stimeo--navigation-menu#onTriggerKeydown">
<span aria-hidden="true">▾</span>
<span class="visually-hidden"><%= t(
"components.navigation_menu.demo.hover.solutions.toggle"
) %></span>
</button>
<div id="nav-menu-solutions" class="nav-menu__panel"
data-stimeo--navigation-menu-target="panel" hidden>
<a class="demo-menuitem" href="#retail"><%= t(
"components.navigation_menu.demo.hover.solutions.links.retail"
) %></a>
<a class="demo-menuitem" href="#finance"><%= t(
"components.navigation_menu.demo.hover.solutions.links.finance"
) %></a>
</div>
</li>
<li class="nav-menu__item" data-stimeo--navigation-menu-target="hoverArea">
<a class="demo-baritem nav-menu__link" href="#resources">
<%= t("components.navigation_menu.demo.hover.resources.label") %>
</a>
<button
type="button"
class="demo-baritem nav-menu__disclosure"
aria-expanded="false"
aria-controls="nav-menu-resources"
data-stimeo--navigation-menu-target="trigger"
data-action="click->stimeo--navigation-menu#toggle
keydown->stimeo--navigation-menu#onTriggerKeydown">
<span aria-hidden="true">▾</span>
<span class="visually-hidden"><%= t(
"components.navigation_menu.demo.hover.resources.toggle"
) %></span>
</button>
<div id="nav-menu-resources" class="nav-menu__panel"
data-stimeo--navigation-menu-target="panel" hidden>
<a class="demo-menuitem" href="#guides"><%= t(
"components.navigation_menu.demo.hover.resources.links.guides"
) %></a>
<a class="demo-menuitem" href="#blog"><%= t(
"components.navigation_menu.demo.hover.resources.links.blog"
) %></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.
*/
/* Caption naming each arrangement (click-only, then the opt-in hover one). */
.nav-menu__caption {
margin: 0 0 0.5rem;
font-size: 0.85rem;
color: var(--color-text-muted);
}
/* Space the second arrangement away from the first nav's panels. */
.nav-menu + .nav-menu__caption {
margin-top: 1.75rem;
}
.nav-menu__list {
display: flex;
gap: 0.5rem;
margin: 0;
padding: 0;
list-style: none;
}
.nav-menu__item {
position: relative;
}
/* Top-level-links arrangement: a real link next to a small disclosure button,
both inside the <li> that acts as the hoverArea. */
/* Trimmed on the end side so the link and its disclosure button read as one pair. */
.nav-menu__link {
padding-inline-end: 0.25rem;
}
/* Narrower than a labelled bar item — it carries only the chevron — but the same
block padding, so it lines up with the link it belongs to. */
.nav-menu__disclosure {
padding-inline: 0.5rem;
}
.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;
}
このデモに固有の消費側 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" |
hoverArea
|
ホバー反応域を内包要素全体へ広げる任意のラッパー(例: トップレベルのリンク・トリガー・パネルを含む <li>)。ホバーすると内包するトリガーのパネルを開閉する。1 つのラッパーに内包するトリガーは 1 つだけにする。openOnHover が有効なときだけ働く。 |
data-stimeo--navigation-menu-target="hoverArea" |
値(Values)
| 名前 | 説明 | 属性 |
|---|---|---|
openOnHover
|
ホバーでパネルを開閉するか(オプトイン)。既定値は false。実行時に変更するとその場で配線・解除される。ポインタがそのトリガーの領域に留まっている間、開いているパネルのトリガーへのクリックは何もしない(閉じるとポインタが領域外へ出て戻るまで再オープンできなくなるため)。 | data-stimeo--navigation-menu-open-on-hover-value |
hoverDelay
|
ホバーでパネルを開閉するまでの遅延(ミリ秒)。既定値は 150。 | data-stimeo--navigation-menu-hover-delay-value |
アクション
| 名前 | 説明 | アクション |
|---|---|---|
onTriggerKeydown
|
ArrowLeft/ArrowRight でトリガー間のフォーカスを移動する。自然な Tab 順は維持する。hidden や disabled のトリガーは飛ばし、修飾キー付きの矢印(Alt+← / Alt+→)はブラウザに渡す。 | stimeo--navigation-menu#onTriggerKeydown |
toggle
|
クリックされたトリガーのパネルを開閉する(同時に一つだけ開く)。保留中のホバー予約は打ち消す。 | stimeo--navigation-menu#toggle |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
aria-expanded |
トリガー | そのパネルの開閉状態(true/false)。 |
hidden |
パネル | 閉じているときは付与。 |