Webパーツ辞典

ツールバーToolbar

元に戻す・切り抜き・削除のようなアイコンのボタンを横一列に並べ、種類ごとに区切り線で分けた帯。押しっぱなしの道具は押された見た目で残る。

表示例

アイコン: Font Awesome 7 Free(fa-solid / fa-regular)

別名・英語表記

  • アイコンツールバー
  • 操作ボタン列
  • ツールボタンの帯
  • ボタンツールバー
  • 編集ツールバー
  • Toolbar

実装例

<div class="x-toolbar">
  <!-- Tab で入るのは1か所だけ(tabindex="0")。中は左右の矢印キーで移り、JS が tabindex を付け替える -->
  <div class="x-toolbar__bar" role="toolbar" aria-label="画像の編集">
    <div class="x-toolbar__g" role="group" aria-label="履歴">
      <button class="x-toolbar__b" type="button" tabindex="0" aria-label="元に戻す" aria-keyshortcuts="Control+Z">
        <i class="fa-solid fa-rotate-left" aria-hidden="true"></i>
      </button>
      <button class="x-toolbar__b" type="button" tabindex="-1" aria-label="やり直す" aria-disabled="true" aria-keyshortcuts="Control+Y">
        <i class="fa-solid fa-rotate-right" aria-hidden="true"></i>
      </button>
    </div>
    <span class="x-toolbar__sep" role="separator" aria-orientation="vertical"></span>
    <div class="x-toolbar__g" role="group" aria-label="変形">
      <button class="x-toolbar__b" type="button" tabindex="-1" aria-label="切り抜き">
        <i class="fa-solid fa-crop-simple" aria-hidden="true"></i>
      </button>
      <button class="x-toolbar__b" type="button" tabindex="-1" aria-label="左右反転">
        <i class="fa-solid fa-left-right" aria-hidden="true"></i>
      </button>
    </div>
    <span class="x-toolbar__sep" role="separator" aria-orientation="vertical"></span>
    <!-- 入り切りする道具は aria-pressed。押されている見た目は色だけにせず枠と地でも示す -->
    <button class="x-toolbar__b" type="button" tabindex="-1" aria-label="グリッド表示" aria-pressed="true">
      <i class="fa-solid fa-border-all" aria-hidden="true"></i>
    </button>
    <button class="x-toolbar__b" type="button" tabindex="-1" aria-label="削除">
      <i class="fa-regular fa-trash-can" aria-hidden="true"></i>
    </button>
  </div>
  <div class="x-toolbar__c" aria-hidden="true"><span></span></div>
</div>

AIへの指示文

「ツールバー(Toolbar)」を実装してください。

## これは何か
元に戻す・切り抜き・削除のようなアイコンのボタンを横一列に並べ、種類ごとに区切り線で分けた帯。押しっぱなしの道具は押された見た目で残る。

## 使いどころ
- 画像編集・図の作成・地図のように、同じ画面で道具を何度も持ち替えるアプリで、よく使う操作を1列にまとめたいとき
- ボタンが多く、Tab で1つずつ渡ると目的の場所までが遠くなるとき(帯全体を Tab 1回で通り過ぎられる)

## 実装の要件
- div[role="toolbar"][aria-label] > div[role="group"][aria-label] > button[type="button"][aria-label] の構成にし、グループの間に span[role="separator"][aria-orientation="vertical"] を置く
- キー操作は JavaScript で実装する。keydown で左右の矢印を受け、次のボタンの tabindex を 0・元のボタンを -1 に付け替えて focus() を呼ぶ。role を付けただけでは矢印キーは動かない
- 帯から Tab で出て戻ってきたときは、最後にフォーカスがあったボタンへ戻す
- 帯は display: flex; で並べ、ボタンは 24×24px 以上(指で押す画面は 44×44px を目安)の押せる領域を取る
- メニューバー(navigation/menubar)とは別物。見出しを押して下に操作の一覧を開くのがメニューバー、操作そのもののボタンを直接並べるのがツールバー
- 表の上に置く検索・並べ替え・一括操作の帯はテーブルの操作バー(data/table-toolbar)、書式ボタンと入力欄が一体のものはリッチテキストエディター(form/rich-text-editor)として扱う。ツールバーはその中に入る部品のほう
- WAI-ARIA APG は、role="toolbar" でまとめるのはコントロールが3つ以上あるときだけとしている。2つなら普通のボタンを並べる
- アイコンだけのボタンは意味が伝わりにくい。迷うものは文字を添えるか、ツールチップ(overlay/tooltip)で名前を見せる
- テキスト入力やスライダーのように左右の矢印キーを使う部品を混ぜると、帯の中の移動とキーがぶつかる。APG は避けるよう勧め、入れるなら1つだけにして最後に置くとしている
- 狭い画面で入り切らない分は、三点メニュー(overlay/kebab-menu)にまとめて逃がす

## アクセシビリティの要件
- 帯に role="toolbar" を付け、aria-label(見える見出しがあれば aria-labelledby)で「画像の編集」のような名前を付ける
- Tab で止まるのは帯の中の1つだけにする(ロービング tabindex: いまのボタンが 0、他は -1)。帯の中は左右の矢印キーで移り、Home・End で両端へ移る操作は APG で任意とされている
- 縦に並べるときは aria-orientation="vertical" を付け、上下の矢印キーで移るようにする
- アイコンだけのボタンには aria-label で名前を付け、アイコンは aria-hidden="true" にする
- 入り切りする道具には aria-pressed を付ける。押されている状態を色だけで示さず、枠や地の形でも区別する(WCAG 1.4.1 色の用途)
- 使えない操作は aria-disabled="true" にすると、フォーカスが止まるので場所を読み上げで辿れる。disabled 属性のボタンにはフォーカスが止まらない

## 参考にする実装
この形を出発点にして、プロジェクトの命名規則・色・余白に合わせて調整してください。

```html
<div class="x-toolbar">
  <!-- Tab で入るのは1か所だけ(tabindex="0")。中は左右の矢印キーで移り、JS が tabindex を付け替える -->
  <div class="x-toolbar__bar" role="toolbar" aria-label="画像の編集">
    <div class="x-toolbar__g" role="group" aria-label="履歴">
      <button class="x-toolbar__b" type="button" tabindex="0" aria-label="元に戻す" aria-keyshortcuts="Control+Z">
        <i class="fa-solid fa-rotate-left" aria-hidden="true"></i>
      </button>
      <button class="x-toolbar__b" type="button" tabindex="-1" aria-label="やり直す" aria-disabled="true" aria-keyshortcuts="Control+Y">
        <i class="fa-solid fa-rotate-right" aria-hidden="true"></i>
      </button>
    </div>
    <span class="x-toolbar__sep" role="separator" aria-orientation="vertical"></span>
    <div class="x-toolbar__g" role="group" aria-label="変形">
      <button class="x-toolbar__b" type="button" tabindex="-1" aria-label="切り抜き">
        <i class="fa-solid fa-crop-simple" aria-hidden="true"></i>
      </button>
      <button class="x-toolbar__b" type="button" tabindex="-1" aria-label="左右反転">
        <i class="fa-solid fa-left-right" aria-hidden="true"></i>
      </button>
    </div>
    <span class="x-toolbar__sep" role="separator" aria-orientation="vertical"></span>
    <!-- 入り切りする道具は aria-pressed。押されている見た目は色だけにせず枠と地でも示す -->
    <button class="x-toolbar__b" type="button" tabindex="-1" aria-label="グリッド表示" aria-pressed="true">
      <i class="fa-solid fa-border-all" aria-hidden="true"></i>
    </button>
    <button class="x-toolbar__b" type="button" tabindex="-1" aria-label="削除">
      <i class="fa-regular fa-trash-can" aria-hidden="true"></i>
    </button>
  </div>
  <div class="x-toolbar__c" aria-hidden="true"><span></span></div>
</div>
```

```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-toolbar,
.x-toolbar *:not([class*="fa-"]),
.x-toolbar *:not([class*="fa-"])::before,
.x-toolbar *:not([class*="fa-"])::after {
  box-sizing: border-box;
  /* 貼った先の既定の書体に引きずられないようにする。書体が変わると
     文字の幅と高さが変わり、枠のサイズまでずれる */
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}

.x-toolbar {
  width: 300px;
  max-width: 100%;
  border: 1px solid #7f8a99;
  border-radius: 8px;
  background: #fff;
  color: #1b2028;
  overflow: hidden;
}
.x-toolbar__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  padding: 6px;
  border-bottom: 1px solid #e3e6ea;
  background: #f3f4f6;
}
.x-toolbar__g {
  display: flex;
  gap: 2px;
}
.x-toolbar__b {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 6px;
  background: none;
  color: #1b2028;
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}
.x-toolbar__b:hover {
  background: #e3e6ea;
}
/* 使えないときも消さずに残す。押せないことは aria-disabled で伝える */
.x-toolbar__b[aria-disabled="true"] {
  color: #5b6472;
  cursor: default;
}
.x-toolbar__b[aria-disabled="true"]:hover {
  background: none;
}
/* 押されている状態は色だけでなく、枠と地の形でも見せる */
.x-toolbar__b[aria-pressed="true"] {
  border-color: #1d4ed8;
  background: #dbe3f7;
  color: #1d4ed8;
}
.x-toolbar__b:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: 1px;
}
.x-toolbar__sep {
  width: 1px;
  height: 20px;
  margin: 0 4px;
  background: #7f8a99;
}
/* 操作の対象(画像の編集面)。道具の帯がどこに効くかを見せるための飾り */
.x-toolbar__c {
  display: grid;
  place-items: center;
  height: 96px;
  padding: 12px;
}
.x-toolbar__c span {
  width: 120px;
  height: 72px;
  border: 1px dashed #7f8a99;
  border-radius: 4px;
  background: #eef1f5;
}
```

## 出力の約束
- HTML と CSS を分けて出す。JavaScript が要るときは素の JavaScript で書き、UIフレームワークは使わない。
- 参考実装がアイコンに Font Awesome のクラスを使っている場合は、プロジェクトで使っているアイコンかインライン SVG に置き換えてよい。
- 上のアクセシビリティ要件を満たす。満たせない箇所があるときは、理由を添えて先に知らせる。
- 色・角丸・余白は既存のデザイントークンがあればそれを使う。無ければ CSS 変数として切り出す。

使いどころ

  • 画像編集・図の作成・地図のように、同じ画面で道具を何度も持ち替えるアプリで、よく使う操作を1列にまとめたいとき
  • ボタンが多く、Tab で1つずつ渡ると目的の場所までが遠くなるとき(帯全体を Tab 1回で通り過ぎられる)

注意点

  • メニューバー(navigation/menubar)とは別物。見出しを押して下に操作の一覧を開くのがメニューバー、操作そのもののボタンを直接並べるのがツールバー
  • 表の上に置く検索・並べ替え・一括操作の帯はテーブルの操作バー(data/table-toolbar)、書式ボタンと入力欄が一体のものはリッチテキストエディター(form/rich-text-editor)として扱う。ツールバーはその中に入る部品のほう
  • WAI-ARIA APG は、role="toolbar" でまとめるのはコントロールが3つ以上あるときだけとしている。2つなら普通のボタンを並べる
  • アイコンだけのボタンは意味が伝わりにくい。迷うものは文字を添えるか、ツールチップ(overlay/tooltip)で名前を見せる
  • テキスト入力やスライダーのように左右の矢印キーを使う部品を混ぜると、帯の中の移動とキーがぶつかる。APG は避けるよう勧め、入れるなら1つだけにして最後に置くとしている
  • 狭い画面で入り切らない分は、三点メニュー(overlay/kebab-menu)にまとめて逃がす

アクセシビリティの要点

  • 帯に role="toolbar" を付け、aria-label(見える見出しがあれば aria-labelledby)で「画像の編集」のような名前を付ける
  • Tab で止まるのは帯の中の1つだけにする(ロービング tabindex: いまのボタンが 0、他は -1)。帯の中は左右の矢印キーで移り、Home・End で両端へ移る操作は APG で任意とされている
  • 縦に並べるときは aria-orientation="vertical" を付け、上下の矢印キーで移るようにする
  • アイコンだけのボタンには aria-label で名前を付け、アイコンは aria-hidden="true" にする
  • 入り切りする道具には aria-pressed を付ける。押されている状態を色だけで示さず、枠や地の形でも区別する(WCAG 1.4.1 色の用途)
  • 使えない操作は aria-disabled="true" にすると、フォーカスが止まるので場所を読み上げで辿れる。disabled 属性のボタンにはフォーカスが止まらない

作るときに使えるツール

見分け方

関連するパーツ