表示例
アイコン: 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>
/* 貼った先のリセットに頼らないよう、この部品の中だけ 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;
}
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 属性のボタンにはフォーカスが止まらない