リンク・ボタン・フォーム最適化

ボタンはdivタグで実装しない

<!-- NG -->
<div>Click</div>

<!-- Good -->
<button>Click</button>

<div>要素で実装したボタンはフォーカスできず、アクセシビリティ上もインタラクティブな要素として認識されない。ボタンとしての機能を持つなら<button>要素を用いるのが多くの場面でも有効である。

もし、諸事情で<div>要素を用いないといけない場合は、キーボード操作でのフォーカスや、WAI-ARIA等でスクリーンリーダーのケアをする必要がある。

小さいアイコンはクリッカブルエリアを広く取る

/* NG 32px四方しかないアイコン */
.icon {
  position: relative;
  width: 32px;
  height: 32px;
}

/* Good 疑似要素を使って50px四方にクリッカブルエリアを拡大 */
.icon::before {
  position: absolute;
  top: -9px;
  left: -9px;
  display: block;
  width: 50px;
  height: 50px;
  content: "";
}

小さいアイコンをボタンにする場合、アイコンの見た目どおりの境界ではなく、周りに余白を設けてアイコン周辺でも反応するようクリッカブルエリア(クリックできる範囲)を設定するのがよい。paddingで余白を付けて領域を広げたり、背景色やレイアウトの関係でpaddingが難しい場合は疑似要素を使ってクリッカブルエリアを拡大できる。
(※Web Content Accessibility Guidelinesによれば、インタラクションの大きさは44×44 ピクセル以上であることを推奨しているため、小さいアイコンを用いるのは避けた方がよい)

テキストリンクとアイコンの間もリンクエリアに設定する

/* NG */
.externalLink {
  position: relative;
  color: #000;
}

.externalLink::after {
  position: absolute;
  top: 6px;
  right: -20px; /* アイコンが領域外に飛び出ている */
  display: block;
  width: 12px;
  height: 12px;
  content: "";
  background-image: url("/assets/images/icon_tab.svg");
  background-repeat: no-repeat;
  background-size: 12px;
}

/* Good */
.externalLink {
  position: relative;
  color: #000;
}

.externalLink::after {
  position: absolute;
  top: 6px;
  right: 0; /* 余白内右端に配置 */
  display: block;
  width: 12px;
  height: 12px;
  padding-right: 20px; /* アイコンの分余白を確保 */
  content: "";
  background-image: url("/assets/images/icon_tab.svg");
  background-repeat: no-repeat;
  background-size: 12px;
}

外部リンクなどを表す際にテキストリンクの後ろに別タブで開くことを示すアイコンを設置する場合、前述のクリッカブルエリアと同様に余白設定をしないと途切れることがある。

ハンバーガーメニューは項目の領域全体をリンクエリアに設定する

<ul class="menu">
  <li>
    <a href="#">About</a>
  </li>
</ul>
/* NG */
li {
  padding: 16px; /* リストタグが余白を確保している */
}

/* Good */
a {
  display: block;
  padding: 16px; /* aタグが余白を確保している */
}

ハンバーガーメニューは罫線の枠内全域がクリッカブルエリアのほうがよい。文字部分のみにしかリンクエリアがない場合は、右側の余白部分をタップ・クリックしても反応しない。ボタンのデザインと同じく項目の領域全体が反応するほうが使いやすい。

<input>要素をlabel要素で紐付ける

<!-- NG -->
<input type="checkbox">個人情報保護方針に同意する

<!-- Good label要素で囲う -->
<label><input type="checkbox">個人情報保護方針に同意する</label>

<!-- Good id属性とfor属性で紐付ける -->
<input type="checkbox" id="agreePrivacy">
<label for="agreePrivacy">個人情報保護方針に同意する</label>

要素に適切なラベルを紐付けるとアクセシビリティ・ユーザビリティ両面からメリットがある。アクセシビリティ観点としては要素にフォーカスした時に支援技術がラベルを読み上げるので、ユーザーの理解を助ける。ユーザビリティ観点としてはラベルをクリックした時にも要素にチェックが入ったり、フォーカスできたりするなど入力しやすくなる。

placeholderをラベル代わりにしない

<!-- NG -->
<input type="text" placeholder="お名前">

<!-- Good -->
<label>お名前 <input type="text" placeholder="山田太郎"></label>

テキスト入力の<input>要素にplaceholder属性を用いてラベルの代わりとするのは避けたほうがよい。<label>要素を用いないと要素とラベルの紐付けは不十分であり、HTML Living Standardによればplaceholder属性はユーザの入力を助ける短いヒントなので用法として不適切である。

インタラクションを重ねない、近づかせすぎない

<!-- NG -->
<label>
  <input type="checkbox">
  <a href="#" target="_blank">個人情報保護方針</a>に同意する
</label>

<!-- Good -->
<a href="#" target="_blank">個人情報保護方針について</a><br>
<label> <input type="checkbox">個人情報保護方針に同意する </label>

インタラクティブな要素の中に別のインタラクティブな要素を重ねるとユーザーに精密な操作を要求することになる。例えば、チェックのクリッカブルエリアとリンクのクリッカブルエリアが重なっている場合、誤操作(チェックを入れようとしたらリンクをクリックしてしまう)の原因になる。

またインタラクティブな要素同士が近すぎるのも誤操作を誘発するので、十分距離を空けておいたほうがよい。

アイコンのみのボタンには適切なラベルを付与する

<!-- NG -->
<button></button>

<!-- Good aria-labelで実装 -->
<button aria-label="検索する"></button>

<!-- Good 不可視テキストで実装 -->
<button>
  <span class="sr-only">検索する</span>
</button>
/* 要素を不可視にするスタイル */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  clip-path: polygon(0 0, 0 0, 0 0, 0 0);
  border: 0;
}

アイコンのみのボタンに適切なラベルを与えないと、スクリーンリーダーなどに読み上げらないという問題がある。アイコンとともにテキストも添えてあげるデザインが親切だが、アイコンのみとする場合はaria-label属性や不可視テキストでラベル情報を補ってあげるのがよい。

特に不可視テキストはボタンのラベルだけでなく、スクリーンリーダーなどの支援技術へテキストを伝える手段としてさまざまな場面で汎用性のある方法である。

ボタンにはcursor:pointerをつける

button {
  cursor: pointer;
}

デフォルトでは<button>要素にホバーしてもカーソルの見た目は変わらず矢印のままである。インタラクティブな要素であることを示すためにもCSSのcursorプロパティの値をpointerで指差しアイコンにしてあげると親切である。

タッチデバイスでのホバーアニメーションの回避

/* ホバー可能なデバイスでのみ適用される */
@media (any-hover: hover) {
  .hoverLinkGood:hover {
    color: orangered;
  }
}

ホバーアニメーションはマウス操作のデスクトップデバイスでは有用だが、タッチデバイスにはホバーというのが存在しない。しかしながら:hover疑似クラスで実装されたアニメーションはタッチ時に発火してしまい、微妙な表現になることがある。そこでany-hoverメディア特性をメディアクエリで用いてタッチデバイスにおけるホバーアニメーションを回避する。

インタラクション要素へのフォーカス時にoutline:noneをつけるのは避ける

/* NG */
button {
  outline: none;
}

キーボード操作などでインタラクション要素にフォーカスがあたった時は枠線が表示される。このフォーカス時の枠線はoutlineプロパティによってブラウザーのデフォルトスタイルとして提供されています。outlineプロパティを上書きすると枠線を消せるが、アクセシビリティ観点から避けたほうがよい。

特別な事情がなければ上書きしないほうがベター。色が濃いボタンの場合はoutline-offsetで少し離してあげるのもよい。

アコーディオンを作るときはsummary・detailsタグが便利

<!-- NG -->
<div>
  <div>概要</div>
  <div>折りたたまれている部分です。</div>
</div>

<!-- Good -->
<details>
  <summary>概要</summary>
  <div>折りたたまれている部分です。</div>
</details>

アコーディオンを<summary>要素と<details>要素を使って実装すると、開閉状態なども特別な追加実装なく支援技術に伝えられる。

大きくしたり、移動したりする場合はtransformを使う

/* NG */
button {
  width: 148px;
  height: 42px;
  transition: width 0.4s, height 0.4s;
}

button:hover {
  width: 178px;
  height: 50px;
}

/* Good */
button {
  width: 148px;
  height: 42px;
  transition: transform 0.4s;
}

button:hover {
  transform: scale(1.2);
}

大きくしたり、移動したりするアニメーション表現のときはwidthプロパティやleftプロパティを動かすより、transformプロパティのほうが便利。widthプロパティを変更すると要素の大きさそのものを変更するのでレイアウトへ影響がある。transformプロパティのscale()値を使えば見た目は大きくしつつも、レイアウトへの影響がない。

また、leftプロパティやwidthプロパティをアニメーションするとややカクついたような動きになる(とくに低速時に顕著)。これは、leftプロパティやwidthが1px単位でしかアニメーションできないためである。一方でtransformプロパティは小数点単位でアニメーションできるため、transformプロパティの方が滑らかになる。アニメーションの観点からも大きさを変えたり移動させたりする場合はtransformプロパティを使うとよい。

境界でのホバーに注意

<!-- NG -->
<button>Hover</button>

<!-- Good -->
<button>
  <span class="inner">Hover</span>
</button>
/* NG */
button {
  transition: transform 0.2s;
}

button:hover {
  transform: scale(0.5);
}

/* Good */
button .inner {
  transition: transform 0.2s;
}

button:hover .inner {
  transform: scale(0.5);
}

ホバーで要素の大きさを変えたり、移動させたりするアニメーションはその境界付近での挙動に注意が必要である。アニメーション開始と終了が高速に繰り返されて荒ぶることがある。

ホバーの前後でインタラクティブな領域が変わるため、ホバー → 小さくなる → ホバー外れる → 戻る → ホバー → …と繰り返されてしまうのが原因である。アニメーションの挙動だけでなくインタラクティブな領域が変わるのはユーザビリティとしてもあまりよくない。

上記の例では、ボタンの見た目は内部の要素にて行っている。インタラクティブな要素である要素ではなく、内部の要素をアニメーションさせることで領域の変化を防いでいる。

input要素をカスタムするときにdisplay:noneにしない

<!-- NG -->
<label class="customCheckbox">
  <input type="checkbox">個人情報保護方針に同意する
</label>

<!-- Good -->
<label class="customCheckbox">
  <input type="checkbox" class="sr-only">個人情報保護方針に同意する
</label>
.customCheckbox {
  /* カスタマイズしたスタイル */
}

/* NG */
.customCheckbox input {
  display: none
}

/* Good */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  clip-path: polygon(0 0, 0 0, 0 0, 0 0);
  border: 0;
}

<input>要素にdisplay: noneのスタイルをあててしまうとフォーカスできなくなる。<label>タグと不可視テキストのテクニックを組みあわせると、フォーカス制御を活かしつつ入力フォームを独自デザインにできる。

input要素にautocomplete属性を設置してあげると便利

<!-- NG -->
<div>
  <label>お名前<input type="text" name="name"> </label>
  <label>郵便番号<input type="text" name="postal-code"></label>
  <label>メールアドレス<input type="email" name="email"></label>
</div>

<!-- Good -->
<form>
  <label>お名前<input type="text" name="name" autocomplete="name"></label>
  <label>郵便番号<input type="text" name="postal-code" autocomplete="postal-code"></label>
  <label>メールアドレス<input type="email" name="email" autocomplete="email"></label>
</form>

<input>要素にはautocomplete属性というユーザー入力を補助する機能があります。ブラウザーによって細かい挙動は違うがautocomplete属性と適切な値を設定することで有効になる。Safariは<form>要素で囲わないと有効にならないので、<form>要素も合わせて設置するのがよい。

input要素の文字サイズを16px未満に指定しない

/* NG */
input {
  font-size: 14px;
}

/* Good */
input {
  font-size: 16px;
}

iOS Safariでの挙動だが、要素のfont-sizeを16px未満に指定するとフォーム部分へズームしてしまう。入力後もズームしたままなので、拡大率を戻すにはユーザーがピンチアウトしなくてはならない。あまり小さい文字のフォームをデザインしないほうがよい。

装飾系Videoタグのコントロール非表示

<!-- NG -->
<video
  muted
  playsinline
  autoplay
  loop
  src="ikura.mp4"
  width="256"
  height="256"
></video>

<!-- Good -->
<video
  muted
  playsinline
  autoplay
  loop
  controlslist="nodownload nofullscreen noremoteplayback"
  x-webkit-airplay="deny"
  disablepictureinpicture
  src="ikura.mp4"
  width="256"
  height="256"
></video>

モダンブラウザーなら透過動画も実装可能となった。見せるための動画ではなく、装飾的な動画の使い方も今後増えてくるであろう。装飾的な動画も<video>タグにて実装されるが、あくまで装飾なので動画コントロールUIは不要である。各種コントロールを非表示にする設定をつけておくとよい。

ただ、あくまで不用意にコントールUIが出てしまうのを防ぐためのものなので、完全にダウンロード不可などにはできない。

inserted by FC2 system