本文へスキップ

コマンドパレット

ボタンまたは ⌘K/Ctrl+K でコマンド検索オーバーレイを開き、入力で絞り込み、↑↓キーまたはタップで実行できる。

ENGLISH

Command Palette

  • JS
約283行領域別 / SaaS・業務 の一覧 →

プレビュー

クリック

操作クリック/タップで動きます。

仕様書

向く場面
  • 機能が多く名前で直接呼びたい SaaS の管理画面
  • ⌘K の慣習を知る熟練ユーザー向けツール
  • メニュー階層を飛ばして操作させたいエディタ
避ける場面
  • 操作項目が 10 に満たない小規模アプリ
  • 初回利用者が主対象で ⌘K を知らない画面
構成要素
  • 開くボタン
  • オーバーレイ
  • 検索入力
  • Esc 表示
  • 候補リスト
  • 空結果
持っている状態
  • 通常
  • ホバー
  • 押下中
  • 空
キーボード

⌘K/Ctrl+K で開閉(他の入力欄にフォーカス中は奪わない)、入力で絞り込み、↑↓ で候補移動(端で止まる)、Enter で実行、Esc で閉じる。Tab はパレット内に留まる

支援技術

role=dialog+aria-modal。入力は role=combobox+aria-expanded/controls/activedescendant、候補は role=listbox/option+aria-selected。開くと入力欄へ、閉じると開くボタンへ戻す。実行結果は aria-live

動き

候補の背景 100ms。オーバーレイの出現はアニメ無し

デザインの要点

  • 開くボタンと ⌘K の両方を用意する。ショートカットだけだと存在に気づけず、ボタンだけだと熟練者が遅い
  • 絞り込むたびに先頭の候補をアクティブにする。打ってすぐ Enter で最有力を実行でき、矢印キーが要らない
  • 入力欄は role=combobox+aria-activedescendant で「今どの候補か」を伝える。見た目の背景色だけでは読めない
  • 閉じたら開くボタンへフォーカスを戻す。dialog を消した瞬間にフォーカスが body へ落ちるのが最多の事故
実装メモ

プレビューは枠内に収めるため、オーバーレイを position:absolute(ステージ基準)で実装しています。実際のページで画面全体に重ねるときは position:fixed に変え、body 直下など高い階層へ置いてください。

使いどころ

操作対象が多く、機能を探すより名前を思い出して直接呼び出したい熟練ユーザー向けのSaaSダッシュボードやエディタ、ドキュメントツールに向く。⌘K/Ctrl+Kという慣習化されたショートカットで、メニューの階層をたどらずに目的の操作へ一直線に行ける速さが価値になる。逆に、操作項目が10個に満たないような小規模なアプリでは、コマンド一覧を保守するコストに見合わず、通常のメニューやツールバーで十分。また初回利用者や非熟練ユーザーが主対象の画面にも不向きで、⌘Kの存在に気づいてもらうこと自体が課題になる。サンプルのサブタイトルにある「⌘Kでコマンドを開く」のような明示的なヒントを画面に常設し、開くボタンも併設しておくことで、ショートカットを知らない利用者への導線を切らさないようにする必要がある。

仕組み

document全体にkeydownリスナーを1つ張り、metaKey(Mac)またはctrlKey(Windows/Linux)と'k'の組み合わせを検知してオーバーレイの開閉をトグルする。グローバル方式だが、他のinput/textarea/contenteditableにフォーカスがあるときは無視して、打っている最中のショートカットを奪わないようにしてある。絞り込みは入力のたびにCOMMANDS配列をlabelの部分一致(小文字化したincludes)でフィルタし、リストをまるごと作り直す単純な実装で、10件程度の規模なら問題にならない。フィルタ後は常に先頭の項目を自動でアクティブにするため、入力してすぐEnterを押すだけで矢印キーを使わずに最有力候補を選べる。↑↓キーでのアクティブ切り替えはMath.min/maxで端に達したら止まるクランプ方式で、端から反対側へ循環はしない。

実装の注意

このサンプルのEnterキーとリスト項目クリックのハンドラはrun()でコマンド名を画面下の状態表示(aria-live)へ書くだけで、実際の処理は持たない。実装するときは各コマンド定義に実行関数を持たせ、run()の中でそれを呼び出す形にする。次に、document全体で⌘K/Ctrl+Kを奪うグローバル方式は、他のテキスト入力中や別の機能が同じショートカットを使っている場面と衝突しうる。この見本は他のinput/textarea/contenteditableにフォーカスがあるときは無視するが、全域で奪うかどうかは設計として決めておく必要がある。また↑↓キーが端で止まり循環しない挙動は、循環する実装に慣れた利用者には違和感が出ることがあるため、意図的な選択として決めておくべきである。閉じるときは開くボタンへフォーカスを戻しているが、⌘Kで開いた場合も戻り先は開くボタンになるので、実ページでは開く直前のactiveElementを覚えて戻すほうが自然になる。

アクセシビリティ

オーバーレイにはrole=dialog aria-modal=trueが付き、Tabキーはパレット内(この見本ではinputのみ)に留めるフォーカストラップを入れてある。aria-modal=trueは背後を支援技術から隠す指示だが、視覚的なTab移動までは制御しないため、トラップのJSが無いと開いたままTabを押すと背後の要素へフォーカスが抜ける。リスト項目はrole=optionにaria-selectedと安定したID(cp-opt-N)を持ち、入力欄側にrole=combobox aria-expanded aria-controls aria-activedescendantを付けて、↑↓キーでどの項目がアクティブか(視覚的には背景色の変化で分かる)をスクリーンリーダーにも伝える。WAI-ARIAのコンボボックス+リストボックスパターンそのもので、絞り込みで候補を作り直すたびにIDとactivedescendantを同期させるのが要点。閉じたときは開くボタンへフォーカスを戻し、実行したコマンド名はaria-live=politeの状態表示で読み上げる。

調整して使う

空の入力状態では「最近使ったコマンド」や「よく使うコマンド」を先に並べておくと、検索語を思いつく前から使い始められる。各項目の右端にキーボードショートカットのkbdタグを添えると、パレット自体を使わずに直接そのキーを覚えて呼び出す動線にもなる。部分一致だけでなく、タイプミスや語順の違いを許容するファジーマッチに強化すると、正確な名称を覚えていなくても見つけやすくなる。項目数が増えたらカテゴリ見出しで区切ったり、まず大分類を選んでから絞り込む階層型(ドリルダウン式)に発展させる設計もある。実行頻度や最終使用日時でソート順を動的に変えると、使うほど目的の項目が上位に来るようになり、10項目を超える規模でも探す時間を短縮できる。

コード(コピーして使えます)

<div class="cp-wrap" id="cp-wrap">
  <div class="cp-app">
    <p class="cp-app-title">ダッシュボード</p>
    <p class="cp-app-sub">⌘K でコマンドを開く</p>
    <button class="cp-open-btn" id="cp-open-btn">コマンドを開く</button>
    <p class="cp-app-status" id="cp-status" aria-live="polite"></p>
  </div>
  <div class="cp-overlay hidden" id="cp-overlay" role="dialog" aria-modal="true" aria-label="コマンドパレット">
    <div class="cp-palette">
      <div class="cp-search-row">
        <span class="cp-search-icon" aria-hidden="true">⌕</span>
        <input class="cp-input" id="cp-input" type="text" placeholder="コマンドを検索…" autocomplete="off" aria-label="コマンドを検索" role="combobox" aria-expanded="false" aria-controls="cp-list" aria-autocomplete="list" aria-haspopup="listbox" />
        <kbd class="cp-esc" aria-hidden="true">Esc</kbd>
      </div>
      <ul class="cp-list" id="cp-list" role="listbox" aria-label="コマンド候補"></ul>
    </div>
  </div>
</div>

タグ

同じカテゴリ「SaaS・業務」のほかのパーツ

関連パーツ