ラジオボタンやセレクトボックスなど、フォームのパーツをデザイン変更することがよくありますが、そういった際にアクセシビリティ(キーボード操作)に配慮した形になるように考えてみます。
対応前
以前にアクセシビリティを考慮したフォームの記事でフォームのマークアップを作成したので、このフォームにデザイン変更を行ってみます。
以下のようなコードになります。
<form action="">
<div class="form-item">
<div class="form-item_key">
<label for="name">お名前<span class="required">(必須)</span></label>
</div>
<div class="form-item_val">
<input type="text" name="name" id="name" aria-required="true" />
</div>
</div>
<div class="form-item">
<div class="form-item_key">
<label for="tel">電話番号</label>
</div>
<div class="form-item_val">
<input type="tel" name="tel" id="tel" aria-describedby="notes-tel" placeholder="09000000000" />
<p id="notes-tel" class="form-item_notes">ハイフンを含めず、半角数字のみで入力してください。</p>
</div>
</div>
<fieldset class="form-item">
<legend class="form-item_key">
性別<span class="required">(必須)</span>
</legend>
<div class="form-item_val">
<input type="radio" name="gender" id="male" value="" />
<label for="male">男性</label>
<input type="radio" name="gender" id="female" value="" />
<label for="female">女性</label>
</div>
</fieldset>
<fieldset class="form-item">
<legend class="form-item_key">
好み
</legend>
<div class="form-item_val">
<input type="checkbox" name="favorite[]" id="toru" value="浅倉透" />
<label for="toru">浅倉透</label>
~略~
</div>
</fieldset>
<div class="form-item">
<div class="form-item_key">
<label for="area">地域</label>
</div>
<div class="form-item_val">
<select name="area" id="area">
<option value="">----------</option>
<option value="北海道">北海道</option>
~略~
</select>
</div>
</div>
<div class="form-item">
<div class="form-item_key">
<label for="img">添付画像</label>
</div>
<div class="form-item_val">
<input type="file" name="img" id="img" />
</div>
</div>
<div class="form-item">
<div class="form-item_key">
<label for="message">メッセージ<span class="required">(必須)</span></label>
</div>
<div class="form-item_val">
<textarea name="message" id="message" aria-describedby="notes-message" aria-required="true"></textarea>
<p id="notes-message" class="form-item_notes">500文字以内で入力してください。</p>
</div>
</div>
<button>送信</button>
</form>
inputのtype=fileのデザイン変更も試したかったので、フォームに追加しています。
対応前のデモページ
ラジオボタン・チェックボックス
まずはラジオボタンとチェックボックスの変更ですが、最初に個人的によくやっていた駄目な例を試してみます。
HTML構造はそのままで、inputとlabelにclassを追加します。
<form action="">
~略~
<fieldset class="form-item">
<legend class="form-item_key">
性別<span class="required">(必須)</span>
</legend>
<div class="form-item_val">
<input class="radio_input" type="radio" name="gender" id="male" value="" />
<label class="radio_label" for="male">男性</label>
<input class="radio_input" type="radio" name="gender" id="female" value="" />
<label class="radio_label" for="female">女性</label>
</div>
</fieldset>
<fieldset class="form-item">
<legend class="form-item_key">
好み
</legend>
<div class="form-item_val">
<input class="checkbox_input" type="checkbox" name="favorite[]" id="toru" value="浅倉透" />
<label class="checkbox_label" for="toru">浅倉透</label>
<input class="checkbox_input" type="checkbox" name="favorite[]" id="madoka" value="樋口円香" />
<label class="checkbox_label" for="madoka">樋口円香</label>
<input class="checkbox_input" type="checkbox" name="favorite[]" id="koito" value="福丸小糸" />
<label class="checkbox_label" for="koito">福丸小糸</label>
<input class="checkbox_input" type="checkbox" name="favorite[]" id="hinana" value="市川雛菜" />
<label class="checkbox_label" for="hinana">市川雛菜</label>
</div>
</fieldset>
~略~
<button>送信</button>
</form>
inputはdisplay: noneで非表示にして、labelの疑似要素に背景画像を設定してチェックボックスやラジオボタンの代わりにします。
.radio_input {
display: none;
}
.radio_label {
position: relative;
padding-left: 20px;
}
.radio_label::before {
content: '';
display: block;
position: absolute;
top: calc(50% - 7px);
left: 0;
width: 14px;
height: 14px;
background: url(icon_radio_off.png) left center no-repeat;
}
.radio_input:checked + .radio_label::before {
background-image: url(icon_radio_on.png);
}
.checkbox_input {
display: none;
}
.checkbox_label {
position: relative;
padding-left: 20px;
}
.checkbox_label::before {
content: '';
display: block;
position: absolute;
top: calc(50% - 7px);
left: 0;
width: 14px;
height: 14px;
background: url(icon_checkbox_off.png) left center no-repeat;
}
.checkbox_input:checked + .checkbox_label::before {
background-image: url(icon_checkbox_on.png);
}
これでマウス操作時は問題なく動作するのですが、Tabキーでの移動時にフォーカスされません。
ラジオボタン・チェックボックスの駄目な対応例のデモページ
次に正しい対応方法ですが、先ほどのHTMLでinputとlabelを囲う要素を追加します。
<form action="">
~略~
<fieldset class="form-item">
<legend class="form-item_key">
性別<span class="required">(必須)</span>
</legend>
<div class="form-item_val">
<div class="radio">
<input class="radio_input" type="radio" name="gender" id="male" value="" />
<label class="radio_label" for="male">男性</label>
</div>
<div class="radio">
<input class="radio_input" type="radio" name="gender" id="female" value="" />
<label class="radio_label" for="female">女性</label>
</div>
</div>
</fieldset>
<fieldset class="form-item">
<legend class="form-item_key">
好み
</legend>
<div class="form-item_val">
<div class="checkbox">
<input class="checkbox_input" type="checkbox" name="favorite[]" id="toru" value="浅倉透" />
<label class="checkbox_label" for="toru">浅倉透</label>
</div>
<div class="checkbox">
<input class="checkbox_input" type="checkbox" name="favorite[]" id="madoka" value="樋口円香" />
<label class="checkbox_label" for="madoka">樋口円香</label>
</div>
<div class="checkbox">
<input class="checkbox_input" type="checkbox" name="favorite[]" id="koito" value="福丸小糸" />
<label class="checkbox_label" for="koito">福丸小糸</label>
</div>
<div class="checkbox">
<input class="checkbox_input" type="checkbox" name="favorite[]" id="hinana" value="市川雛菜" />
<label class="checkbox_label" for="hinana">市川雛菜</label>
</div>
</div>
</fieldset>
~略~
<button>送信</button>
</form>
inputを隠す方法がdisplay: noneだとフォーカス対象にならないので、スキップリンク実装の記事でも使った.visually-hidden のスタイルに変更します。
.radio {
position: relative;
display: inline-block;
}
.radio_input {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.radio_label {
position: relative;
padding-left: 20px;
}
.radio_label::before {
content: '';
display: block;
position: absolute;
top: calc(50% - 7px);
left: 0;
width: 14px;
height: 14px;
background: url(icon_radio_off.png) left center no-repeat;
}
.radio_input:focus + .radio_label::before {
outline-offset: 2px;
outline: 2px solid black;
}
.radio_input:checked + .radio_label::before {
background-image: url(icon_radio_on.png);
}
.checkbox {
position: relative;
display: inline-block;
}
.checkbox_input {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.checkbox_label {
position: relative;
padding-left: 20px;
}
.checkbox_label::before {
content: '';
display: block;
position: absolute;
top: calc(50% - 7px);
left: 0;
width: 14px;
height: 14px;
background: url(icon_checkbox_off.png) left center no-repeat;
}
.checkbox_input:focus + .checkbox_label::before {
outline-offset: 2px;
outline: 2px solid black;
}
.checkbox_input:checked + .checkbox_label::before {
background-image: url(icon_checkbox_on.png);
}
フォーカスが当たっていることが分かるように、フォーカス時に疑似要素にアウトラインも設定しています。
これでTabキーでの移動時にフォーカスされるようになりました。
ラジオボタン・チェックボックスの対応後のデモページ
セレクトボックス
セレクトボックスは以前にデザインを変更する記事を投稿していますが、現在はIEの対応はほぼ不要になっているので、selectに対して装飾を行うので問題なさそうです。
appearanceの対応ブラウザはこちら
<form action="">
~略~
<div class="form-item">
<div class="form-item_key">
<label for="area">地域</label>
</div>
<div class="form-item_val">
<select name="area" id="area" class="select">
<option value="">----------</option>
<option value="北海道">北海道</option>
<option value="東北">東北</option>
<option value="関東">関東</option>
<option value="中部">中部</option>
<option value="近畿">近畿</option>
<option value="中国">中国</option>
<option value="四国">四国</option>
<option value="九州">九州</option>
</select>
</div>
</div>
~略~
<button>送信</button>
</form>
.select {
appearance: none;
margin: 0;
outline-offset: 2px;
border: 1px solid #1E90FF;
padding: 5px 30px 5px 5px;
background: url(icon_arrow_down.png) right 5px center no-repeat #ffffff;
}
input type=file
最後にinput type=fileですが、こちらも駄目な例を先に試してみます。
デザイン変更にlabelを使うので、項目全体をfieldsetとlegendを使用した形に変更します。
<form action="">
~略~
<fieldset class="form-item">
<legend class="form-item_key">
添付画像
</legend>
<div class="form-item_val">
<input class="file_input" type="file" name="img" id="img" />
<label class="file_label" for="img">画像選択</label>
</div>
</fieldset>
~略~
<button>送信</button>
</form>
inputをdisplay: noneで非表示にして、labelに対してボタンの装飾を行います。
.file_input {
display: none;
}
.file_label {
padding: 5px 10px;
color: #ffffff;
background: #1E90FF;
}
この方法は前述のラジオボタン・チェックボックスの駄目な例と同じで、Tabキーでの移動時にフォーカスされません。
input type=fileの駄目な対応例のデモページ
ラジオボタン・チェックボックスの時と同じように、inputの隠し方を変更します。
<form action="">
~略~
<fieldset class="form-item">
<legend class="form-item_key">
添付画像
</legend>
<div class="form-item_val">
<div class="file">
<input class="file_input" type="file" name="img" id="img" />
<label class="file_label" for="img">画像選択</label>
</div>
</div>
</fieldset>
~略~
<button>送信</button>
</form>
.file {
position: relative;
display: inline-block;
}
.file_input {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.file_label {
padding: 5px 10px;
color: #ffffff;
background: #1E90FF;
}
.file_input:focus + .file_label {
outline: 2px solid black;
}
これでTabキーでフォーカスされるようになりました。
input type=fileの対応後のデモページ
通常のinput type=fileではファイル選択中にファイル名が表示されますが、この方法はinputを隠しているため、選択中にファイル名は表示されません。
ファイル名を表示させたい場合、JavaScriptを使った対応になります。
JavaScriptで操作するため、inputにclassの追加とファイル名を表示するエリアの追加を行います。
<form action="">
~略~
<fieldset class="form-item">
<legend class="form-item_key">
添付画像
</legend>
<div class="form-item_val">
<div class="file">
<input class="file_input js-file-input" type="file" name="img" id="img" multiple />
<label class="file_label" for="img">画像選択</label>
<span class="file_name js-file-name">選択されていません</span>
</div>
</div>
</fieldset>
~略~
<button>送信</button>
</form>
JavaScriptでファイル選択時にファイル名を取得して、表示されるようにします。
const $fileInput = document.querySelector('.js-file-input');
const $fileName = document.querySelector('.js-file-name');
$fileInput.addEventListener('change', (e) => {
const files = e.target.files;
console.log(files[0])
if(files.length < 1) {
$fileName.textContent = '選択されていません';
} else if(files.length === 1) {
$fileName.textContent = files[0].name;
} else {
$fileName.textContent = `${files.length}ファイル選択`;
}
});
未選択時(選択した後で選択解除した場合)や複数選択できる場合もあるので、それぞれ出し分けできるようにしています。
input type=fileでファイル名も表示するデモページ
別の方法として、::file-selector-button 疑似要素を使ってinputに装飾を行う方法もあります。
::file-selector-button の対応ブラウザはこちらですが、モダンブラウザでは特に問題なさそうです。
<form action="">
~略~
<div class="form-item">
<div class="form-item_key">
<label for="img">添付画像</label>
</div>
<div class="form-item_val">
<input class="file" type="file" name="img" id="img" />
</div>
</div>
~略~
<button>送信</button>
</form>
.file::file-selector-button {
appearance: none;
border: none;
padding: 5px 10px;
color: #ffffff;
background: #1E90FF;
}
input type=fileのボタン部分に対して装飾ができました。
::file-selector-buttonのデモページ
コメントが承認されるまで時間がかかります。