表示例
アイコン: Font Awesome 7 Free(fa-solid / fa-regular)
別名・英語表記
- トランスファーリスト
- デュアルリストボックス
- リストシャトル
- 左右の一覧で移す
- Transfer list
実装例
<div class="x-transfer-list">
<fieldset class="x-transfer-list__box">
<legend class="x-transfer-list__h">表示しない列 <span class="x-transfer-list__n">3件</span></legend>
<!-- list-style: none の ul は Safari がリストとして読まないので role="list" を足す -->
<ul class="x-transfer-list__list" role="list">
<li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="hidden" value="tel">電話番号</label></li>
<li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="hidden" value="addr" checked>住所</label></li>
<li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="hidden" value="birth">生年月日</label></li>
</ul>
</fieldset>
<div class="x-transfer-list__ops">
<!-- 左で1件以上選ばれているときだけ押せる。選択が無いときは aria-disabled="true" にする -->
<button class="x-transfer-list__b" type="button" aria-label="選んだ列を「表示する列」へ移す">
<i class="fa-solid fa-chevron-right" aria-hidden="true"></i>
</button>
<button class="x-transfer-list__b" type="button" aria-label="選んだ列を「表示しない列」へ戻す" aria-disabled="true">
<i class="fa-solid fa-chevron-left" aria-hidden="true"></i>
</button>
</div>
<fieldset class="x-transfer-list__box">
<legend class="x-transfer-list__h">表示する列 <span class="x-transfer-list__n">3件</span></legend>
<ul class="x-transfer-list__list" role="list">
<li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="shown" value="name">氏名</label></li>
<li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="shown" value="mail">メール</label></li>
<li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="shown" value="dept">部署</label></li>
</ul>
</fieldset>
<!-- 移したら「住所を表示する列へ移しました」と書き換える -->
<p class="x-transfer-list__sr" role="status"></p>
</div>
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-transfer-list,
.x-transfer-list *:not([class*="fa-"]),
.x-transfer-list *:not([class*="fa-"])::before,
.x-transfer-list *: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-transfer-list {
display: flex;
align-items: center;
gap: 8px;
width: 340px;
max-width: 100%;
color: #1b2028;
font-size: 13px;
}
.x-transfer-list__box {
flex: 1 1 0;
min-width: 0;
margin: 0;
padding: 0;
border: 1px solid #7f8a99;
border-radius: 8px;
background: #fff;
overflow: hidden;
}
/* legend を枠の中の見出し帯として描く。float で枠線の切れ目を作らせない */
.x-transfer-list__h {
float: left;
width: 100%;
margin: 0;
padding: 7px 10px;
border-bottom: 1px solid #d5dae1;
background: #f4f6f9;
font-size: 12px;
font-weight: 700;
}
.x-transfer-list__n {
color: #5b6472;
font-weight: 400;
}
.x-transfer-list__list {
clear: both;
height: 118px;
margin: 0;
padding: 4px;
overflow-y: auto;
list-style: none;
}
.x-transfer-list__it {
display: flex;
align-items: center;
gap: 6px;
min-height: 34px;
padding: 0 6px;
border-radius: 6px;
cursor: pointer;
}
.x-transfer-list__it:hover {
background: #f4f6f9;
}
/* 選んだ行は塗りに加えてチェックの形でも分かる */
.x-transfer-list__it:has(:checked) {
background: #eef2ff;
color: #1d4ed8;
font-weight: 700;
}
.x-transfer-list__c {
width: 16px;
height: 16px;
margin: 0;
accent-color: #1d4ed8;
}
.x-transfer-list__ops {
display: flex;
flex: none;
flex-direction: column;
gap: 8px;
padding-top: 30px;
}
.x-transfer-list__b {
width: 36px;
height: 36px;
border: 1px solid #1d4ed8;
border-radius: 8px;
background: #1d4ed8;
color: #fff;
font-size: 13px;
cursor: pointer;
}
/* 押せないボタンも枠は残し、何のボタンかは読めるようにする */
.x-transfer-list__b[aria-disabled="true"] {
border-color: #7f8a99;
background: #fff;
color: #5b6472;
cursor: not-allowed;
}
.x-transfer-list__c:focus-visible,
.x-transfer-list__b:focus-visible {
outline: 2px solid #1d4ed8;
outline-offset: 2px;
}
.x-transfer-list__sr {
position: absolute;
width: 1px;
height: 1px;
margin: 0;
overflow: hidden;
white-space: nowrap;
clip-path: inset(50%);
}
AIへの指示文
「左右移動リスト(Transfer list)」を実装してください。
## これは何か
左右に2つの一覧が並び、間の「→」「←」ボタンで選んだ項目を行き来させる部品。表に出す列やメンバーの割り当てを選ぶ画面で見かける。
## 使いどころ
- 表に表示する列・チームに入れるメンバーのように、候補の中から「入れる側」と「入れない側」を分けて見せたいとき
- 選んだ側の一覧そのものを見ながら確かめたいとき(何件選んだか、何が残っているか)
## 実装の要件
- fieldset > legend(名前と件数)+ ul[role=list] > li > label > input[type=checkbox]、これを左右に2つ置き、間に移す button を2つ(全件を移す button を足すなら4つ)
- 移すボタンは、移せる項目が選ばれているときだけ有効にする。無効の間は aria-disabled="true" にして見た目も変える
- 送信するのは右側(選んだ側)の値だけ。並び順に意味があるなら、右側に上下の並べ替えボタンを足す
- 候補が少ないならチェックボックスを並べるだけで足りる。左右移動リストは、選んだものを別の一覧として見せる価値があるときに使う
- 複数選択プルダウンとの違いは、選んだ側がいつも見えていること。画面の幅を取るので、狭い画面では上下に積むか、別の部品にする
- 一覧の見出しに件数を出し、移すたびに更新する。移したことが件数でも分かるようにする
- 候補が多いときは各一覧の上に絞り込みの欄を置く。長い一覧をスクロールして探させない
- ドラッグで移す操作だけにしない。ボタンで移せる道を必ず残す
## アクセシビリティの要件
- 各一覧は fieldset と legend(「表示しない列」)で名前を付け、項目はチェックボックスにする。選んだ状態がそのまま読み上げに乗る
- 移すボタンには aria-label で「選んだ列を『表示する列』へ移す」のように行き先まで書く。矢印の絵だけでは向きしか伝わらない
- 選択が無いときのボタンは disabled ではなく aria-disabled="true" にする。押した直後に disabled になると、そのボタンにあったフォーカスが失われる
- 移したら role="status" の領域で「住所を表示する列へ移しました」と伝える(達成基準 4.1.3 ステータスメッセージ)
- 選んだ行は塗りに加えてチェックの形と太字でも示す(達成基準 1.4.1 色の用途)
## 参考にする実装
この形を出発点にして、プロジェクトの命名規則・色・余白に合わせて調整してください。
```html
<div class="x-transfer-list">
<fieldset class="x-transfer-list__box">
<legend class="x-transfer-list__h">表示しない列 <span class="x-transfer-list__n">3件</span></legend>
<!-- list-style: none の ul は Safari がリストとして読まないので role="list" を足す -->
<ul class="x-transfer-list__list" role="list">
<li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="hidden" value="tel">電話番号</label></li>
<li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="hidden" value="addr" checked>住所</label></li>
<li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="hidden" value="birth">生年月日</label></li>
</ul>
</fieldset>
<div class="x-transfer-list__ops">
<!-- 左で1件以上選ばれているときだけ押せる。選択が無いときは aria-disabled="true" にする -->
<button class="x-transfer-list__b" type="button" aria-label="選んだ列を「表示する列」へ移す">
<i class="fa-solid fa-chevron-right" aria-hidden="true"></i>
</button>
<button class="x-transfer-list__b" type="button" aria-label="選んだ列を「表示しない列」へ戻す" aria-disabled="true">
<i class="fa-solid fa-chevron-left" aria-hidden="true"></i>
</button>
</div>
<fieldset class="x-transfer-list__box">
<legend class="x-transfer-list__h">表示する列 <span class="x-transfer-list__n">3件</span></legend>
<ul class="x-transfer-list__list" role="list">
<li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="shown" value="name">氏名</label></li>
<li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="shown" value="mail">メール</label></li>
<li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="shown" value="dept">部署</label></li>
</ul>
</fieldset>
<!-- 移したら「住所を表示する列へ移しました」と書き換える -->
<p class="x-transfer-list__sr" role="status"></p>
</div>
```
```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-transfer-list,
.x-transfer-list *:not([class*="fa-"]),
.x-transfer-list *:not([class*="fa-"])::before,
.x-transfer-list *: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-transfer-list {
display: flex;
align-items: center;
gap: 8px;
width: 340px;
max-width: 100%;
color: #1b2028;
font-size: 13px;
}
.x-transfer-list__box {
flex: 1 1 0;
min-width: 0;
margin: 0;
padding: 0;
border: 1px solid #7f8a99;
border-radius: 8px;
background: #fff;
overflow: hidden;
}
/* legend を枠の中の見出し帯として描く。float で枠線の切れ目を作らせない */
.x-transfer-list__h {
float: left;
width: 100%;
margin: 0;
padding: 7px 10px;
border-bottom: 1px solid #d5dae1;
background: #f4f6f9;
font-size: 12px;
font-weight: 700;
}
.x-transfer-list__n {
color: #5b6472;
font-weight: 400;
}
.x-transfer-list__list {
clear: both;
height: 118px;
margin: 0;
padding: 4px;
overflow-y: auto;
list-style: none;
}
.x-transfer-list__it {
display: flex;
align-items: center;
gap: 6px;
min-height: 34px;
padding: 0 6px;
border-radius: 6px;
cursor: pointer;
}
.x-transfer-list__it:hover {
background: #f4f6f9;
}
/* 選んだ行は塗りに加えてチェックの形でも分かる */
.x-transfer-list__it:has(:checked) {
background: #eef2ff;
color: #1d4ed8;
font-weight: 700;
}
.x-transfer-list__c {
width: 16px;
height: 16px;
margin: 0;
accent-color: #1d4ed8;
}
.x-transfer-list__ops {
display: flex;
flex: none;
flex-direction: column;
gap: 8px;
padding-top: 30px;
}
.x-transfer-list__b {
width: 36px;
height: 36px;
border: 1px solid #1d4ed8;
border-radius: 8px;
background: #1d4ed8;
color: #fff;
font-size: 13px;
cursor: pointer;
}
/* 押せないボタンも枠は残し、何のボタンかは読めるようにする */
.x-transfer-list__b[aria-disabled="true"] {
border-color: #7f8a99;
background: #fff;
color: #5b6472;
cursor: not-allowed;
}
.x-transfer-list__c:focus-visible,
.x-transfer-list__b:focus-visible {
outline: 2px solid #1d4ed8;
outline-offset: 2px;
}
.x-transfer-list__sr {
position: absolute;
width: 1px;
height: 1px;
margin: 0;
overflow: hidden;
white-space: nowrap;
clip-path: inset(50%);
}
```
## 出力の約束
- HTML と CSS を分けて出す。JavaScript が要るときは素の JavaScript で書き、UIフレームワークは使わない。
- 参考実装がアイコンに Font Awesome のクラスを使っている場合は、プロジェクトで使っているアイコンかインライン SVG に置き換えてよい。
- 上のアクセシビリティ要件を満たす。満たせない箇所があるときは、理由を添えて先に知らせる。
- 色・角丸・余白は既存のデザイントークンがあればそれを使う。無ければ CSS 変数として切り出す。
使いどころ
- 表に表示する列・チームに入れるメンバーのように、候補の中から「入れる側」と「入れない側」を分けて見せたいとき
- 選んだ側の一覧そのものを見ながら確かめたいとき(何件選んだか、何が残っているか)
注意点
- 候補が少ないならチェックボックスを並べるだけで足りる。左右移動リストは、選んだものを別の一覧として見せる価値があるときに使う
- 複数選択プルダウンとの違いは、選んだ側がいつも見えていること。画面の幅を取るので、狭い画面では上下に積むか、別の部品にする
- 一覧の見出しに件数を出し、移すたびに更新する。移したことが件数でも分かるようにする
- 候補が多いときは各一覧の上に絞り込みの欄を置く。長い一覧をスクロールして探させない
- ドラッグで移す操作だけにしない。ボタンで移せる道を必ず残す
アクセシビリティの要点
- 各一覧は fieldset と legend(「表示しない列」)で名前を付け、項目はチェックボックスにする。選んだ状態がそのまま読み上げに乗る
- 移すボタンには aria-label で「選んだ列を『表示する列』へ移す」のように行き先まで書く。矢印の絵だけでは向きしか伝わらない
- 選択が無いときのボタンは disabled ではなく aria-disabled="true" にする。押した直後に disabled になると、そのボタンにあったフォーカスが失われる
- 移したら role="status" の領域で「住所を表示する列へ移しました」と伝える(達成基準 4.1.3 ステータスメッセージ)
- 選んだ行は塗りに加えてチェックの形と太字でも示す(達成基準 1.4.1 色の用途)