コンボボックス
hc-combobox は、入力で絞り込むドロップダウンつきのアクセシブルな
単一選択入力です。
WAI-ARIA 1.2 コンボボックスパターンに
従い、<input> が role="combobox" を持ち、ドロップダウンは
<ul role="listbox" popover> です。キーボードナビゲーションは
aria-activedescendant を使うため、実際の DOM フォーカスは入力に
留まったまま、見えるハイライトだけがユーザーの選択とともに動きます。
hc-menu や hc-tooltip と同じアーキテクチャのプリミティブです:
- 表示 / 非表示と Escape / 外側クリックによる解散は HTML の
popover属性。 - 入力直下へのリストボックス配置は CSS Anchor Positioning。
installCombobox()がフィルタ、キーボード、選択、ARIA の帳簿を 配線。
別名: オートコンプリート、検索付きセレクト。
ブラウザのベースライン
Section titled “ブラウザのベースライン”| プリミティブ | 必要バージョン |
|---|---|
HTML popover | Chrome 114、Edge 114、Firefox 125、Safari 17 |
| CSS Anchor Positioning | Chrome 125、Edge 125、Firefox 147、Safari 26 |
アンカー未対応のブラウザは getBoundingClientRect による配置フックへ
フォールバックします。
基本の HTML
Section titled “基本の HTML”- Japan
- United States
- United Kingdom
- France
- Germany (coming soon)
<div class="hc-combobox"> <input class="hc-combobox__input hc-input" type="text" role="combobox" aria-controls="country-list" aria-label="Country">
<ul class="hc-combobox__listbox" id="country-list" role="listbox"> <li class="hc-combobox__option" role="option" data-value="jp">Japan</li> <li class="hc-combobox__option" role="option" data-value="us">United States</li> <li class="hc-combobox__option" role="option" data-value="gb">United Kingdom</li> <li class="hc-combobox__option" role="option" data-value="fr">France</li> <li class="hc-combobox__option" role="option" data-value="de" aria-disabled="true">Germany</li> </ul></div>import { installCombobox } from '@hypermedia-components/core';installCombobox();installCombobox() は冪等で、アンインストーラを返します。ゼロ設定の
@hypermedia-components/core/behaviors エントリが自動インストールし、
htmx でスワップされた内容も自動で拾います。
installCombobox() がすること
Section titled “installCombobox() がすること”- 入力への ARIA 配線:
aria-haspopup="listbox"、aria-autocomplete="list"、aria-expanded(popover の状態と連動)、 なければaria-controls、ハイライト中のオプションを追跡するaria-activedescendant。 - 作者が値を与えていなければ、リストボックスに
popover="manual"を 自動設定。 - 入力のインライン
anchor-nameとリストボックスのposition-anchorによるアンカー結合で、ポップオーバーが入力の 直下に着地。 - フィルタ: 入力のキーストロークごとに、入力文字列を含まない
テキストのオプションを隠します(大文字小文字無視)。何も一致しない
ときは
.hc-combobox__emptyの<li role="presentation">が現れ ます。 - キーボード:
↓は開く / 次の見えるオプションへ移動;↑は前へ移動;Home/Endは最初 / 最後の見える有効なオプションへジャンプ;Enterは選択;Escapeは入力値を変えずにリストボックスを閉じる;Tabは閉じて次のタブストップへ譲る。
- マウス: オプションのクリックで選択。
aria-disabled="true"の オプションはスキップされます。 - 選択: 入力値を埋め、
detailに{ value, label, option, input }を運ぶバブリングするhc:comboboxselectイベントを発火します。
フォームへの参加
Section titled “フォームへの参加”コンボボックスの入力は本物の <input> です — name を付ければ
そのままネイティブにフォーム送信されます:
<div class="hc-combobox"> <input class="hc-combobox__input hc-input" type="text" name="country" role="combobox" aria-controls="cb-list" aria-label="Country" /> <!-- リストボックスは上の例と同じ --></div>送信されるのは表示テキストです。 選択時に installCombobox() は
オプションのラベル(Japan)を入力へ書き込みます —
data-value(jp)ではありません。ユーザーは自由入力もできるため、
ワイヤ上の値は常に「見えているものそのまま」です。サーバー側では
通常のテキストフィールドと同じように検証してください。
サーバーがコード値を必要とする場合は、選択イベント(detail は
{ value, label } を運びます)から hidden 入力へ写してください:
<input type="hidden" name="country" id="country-code">document.addEventListener('hc:comboboxselect', (e) => { document.getElementById('country-code').value = e.detail.value ?? '';});htmx の往復では同じイベントでリクエストを駆動できます — 次節を 参照してください。
htmx での利用
Section titled “htmx での利用”選択は入力上で hc:comboboxselect を発火します。htmx をそのイベントに
配線して、選択をサーバへ送ります。
<input class="hc-combobox__input hc-input" type="text" role="combobox" aria-controls="user-list" aria-label="Assignee" data-hx-post="/issues/123/assignee" data-hx-trigger="hc:comboboxselect" data-hx-vals='js:{ value: event.detail.value }'>リモート(非同期)オプション
Section titled “リモート(非同期)オプション”.hc-combobox に data-remote を足すと、サーバがフィルタします。
ビヘイビアはクライアントサイドのフィルタをオフにし、htmx リクエストの
ライフサイクルから読み込み / 空 / エラーの状態を表出させます:
<div class="hc-combobox" data-remote> <input class="hc-combobox__input hc-input" type="text" role="combobox" aria-controls="city-list" aria-label="City" data-hx-get="/cities" data-hx-trigger="input changed delay:200ms" data-hx-target="#city-list"> <ul class="hc-combobox__listbox" id="city-list" role="listbox"></ul></div>サーバは一致する <li role="option"> の行だけを返し、htmx がそれを
リストボックスへスワップします。リクエストの実行中、ビヘイビアは
スピナー行を表示して aria-busy="true" を設定します。空の結果は
「No matches」マーカーを、失敗レスポンスはエラー行を表示します。
3 つのメッセージは i18n カタログ
(combobox.loading / combobox.empty / combobox.error)で翻訳
でき、リストボックス単位では data-hc-loading / data-hc-empty /
data-hc-error で上書きできます。
デバウンスと実行中キャンセルは htmx に留まります(トリガーの
delay:200ms、hx-sync)— ビヘイビア自身がリクエストすることは
決してありません。
作成可能(creatable)
Section titled “作成可能(creatable)”data-allow-create を足すと、ユーザーはリストにない値を選べます。
入力したテキストに完全一致がないとき、合成の 「Create …」
オプションがリストボックスの末尾に現れます。それを選ぶ(クリックまたは
Enter)と生のテキストがコミットされ、created: true つきの
hc:comboboxselect が発火します。
<div class="hc-combobox" data-allow-create>…</div>input.addEventListener('hc:comboboxselect', (e) => { // e.detail = { value, label, option, input, created } if (e.detail.created) { // persist the new value (POST it, add it to the list, …) }});ラベルは i18n カタログで
翻訳できます(combobox.create、例: Create "{value}")。
リッチなオプション
Section titled “リッチなオプション”オプションには任意の HTML を入れられます — アイコン、2 行ラベル、 説明。2 つの任意属性が、フィルタリングと選択をきれいに保ちます:
data-label— 選択時に入力へ書き込まれる値(なければオプションの テキスト内容 — リッチマークアップのテキストを含んでしまいます)。data-search— フィルタが照合するテキスト(なければラベル)。 表示されないエイリアス / キーワードに一致させるのに使います。
- 🇯🇵 Japan Asia
- 🇫🇷 France Europe
- 🇧🇷 Brazil South America
<div class="hc-combobox"> <input class="hc-combobox__input hc-input" type="text" role="combobox" aria-controls="country-rich-list" aria-label="Country">
<ul class="hc-combobox__listbox" id="country-rich-list" role="listbox"> <li class="hc-combobox__option" role="option" data-value="jp" data-label="Japan" data-search="japan nippon 日本 jp"> 🇯🇵 <strong>Japan</strong> <small>Asia</small> </li> <li class="hc-combobox__option" role="option" data-value="fr" data-label="France" data-search="france fr"> 🇫🇷 <strong>France</strong> <small>Europe</small> </li> <li class="hc-combobox__option" role="option" data-value="br" data-label="Brazil" data-search="brazil brasil br"> 🇧🇷 <strong>Brazil</strong> <small>South America</small> </li> </ul></div>フィルタは一致しないオプションを hidden 属性で隠します。リッチ
レイアウトのためにオプションへ display(flex/grid)を与えるなら、
非表示を上書きしないよう :not([hidden]) にスコープしてください:
.hc-combobox__option:not([hidden]) { display: grid; … }アクセシビリティ
Section titled “アクセシビリティ”- 入力がユーザーのアンカーです — フォーカスは常にそこに留まるため、
タイプアヘッドとスクリーンリーダーの読み上げが正しく追跡されます。
aria-activedescendantは DOM フォーカスを動かさずにハイライトを 動かします。 - コンボボックスには常にアクセシブルな名前を与えてください。
<label>で包む、aria-labelを設定する、またはaria-labelledbyを使います。 - リストボックスは
role="listbox"を、各子はrole="option"を 持ちます。利用不可の行はdisabledではなくaria-disabled="true"で印を付けてください —<li>はフォーカス 可能なフォームコントロールではないため、disabledは効きません。 - アクティブなオプションの見えるフォーカスインジケーターは
--hc-color-focus-ringを使い、data-colorテーマをまたいで同期 します。
スコープ外(MVP)
Section titled “スコープ外(MVP)”MVP は表面を小さく保ちます。次のパターンは先送りです:
- 複数選択 — 複数の選択値を持つタグ入力。別コンポーネント
hc-multicomboboxとして出荷済みです。 - 組み込みのデバウンス / 実行中キャンセル — 非同期読み込み自体は
サポートされます(リモート(非同期)オプションを
参照)が、リクエストのタイミングはビヘイビアに同梱せず htmx に
留まります(トリガーの
delay:、hx-sync)。 - strict モード — 自由テキストを値として受け入れることは
data-allow-createでサポートされます。その 逆 — オプションに一致しないとき閉じる際に入力をクリアする — は まだ将来のオプトインフラグです。
テーマ用トークン
Section titled “テーマ用トークン”component トークン(component.tokens.json):
| トークンパス | 用途 |
|---|---|
combobox.listbox.bg / fg / border / radius | リストボックスの面。 |
combobox.listbox.max-height | リストボックスがスクロールし始める上限。 |
combobox.listbox.padding-block / min-width / offset | リストボックスの間隔とアンカーオフセット。 |
combobox.option.padding-x / padding-y / font-size | オプションのレイアウト。 |
combobox.option.fg / hover-bg / active-bg / selected-bg / selected-fg / disabled-fg | オプションの状態。 |
combobox.empty-fg | 「No matches」プレースホルダーの色。 |
CSS 変数
Section titled “CSS 変数”生成される CSS 変数を表示
--hc-combobox-listbox-bg | -listbox-fg | -listbox-border | -listbox-radius--hc-combobox-listbox-max-height | -listbox-padding-block--hc-combobox-listbox-min-width | -listbox-offset--hc-combobox-option-padding-x | -option-padding-y | -option-font-size--hc-combobox-option-fg | -option-hover-bg | -option-active-bg--hc-combobox-option-selected-bg | -option-selected-fg--hc-combobox-option-disabled-fg--hc-combobox-empty-fg--hc-color-focus-ring (inherited from data-color)