CSSのみでラジオボタンとチェックボックスをトグルボタンにする

Vue.jsなどでデータバインディングするとき<input>要素がきちんと(セマンティックに)使われて欲しくなる。jQueryなどで見かけ上の動作をされても困るのだ。かといってブラウザデフォルトの<input>の見た目はダサいので格好よくはしたい。

CSSのみでラジオボタンやチェックボックスをボタン化する方法は色々と紹介されているがフォーカス、キーボード操作、flexboxによるサイズの自動調整など細かいところまで考慮されたものが少なかったので、その方法を整理してみる。

基本形

この方法のポイントは以下。

  • JavaScript不使用
  • シンプルなHTMLマークアップ
  • ボタンサイズ自動調整
  • hover checked disabledなどのinput要素自身の属性のみに応じて見た目が変化
  • フォーカスした時にブラウザが自動で付加する枠がボタンとズレない
  • HTML5バリデーション結果のフィードバックでボタンがきちんとフォーカスされる

htmlの基本形は以下。

<!-- Radio buttons -->
<div class="toggle-buttons">
  <label>
    <input type="radio" name="radio-1">
    <span class="button">Radio</span>
  </label>
</div>

<!-- Checkbox -->
<div class="toggle-buttons">
  <label>
    <input type="checkbox">
    <span class="button">Checkbox</span>
  </label>
</div>

ラジオボタンとチェックボックスのカスタマイズ方法を説明しているサイトでは以下の形式もよく紹介されるがidforの命名が必須となるので採用しない。

<div class="toggle-buttons">
  <input type="checkbox" id="checkbox-1">
  <label for="checkbox-1" class="button">
    Checkbox
  </label>
</div>

CSSの基本形は以下の通り。色とborder関係はベースとなるボタンのデザインに合わせる必要がある。

.toggle-buttons {
  display: flex;
}

.toggle-buttons.vertical {
  flex-direction: column;
}

.toggle-buttons label {
  display: flex;
  position: relative;
}

.toggle-buttons [type=radio],
.toggle-buttons [type=checkbox] {
  -webkit-appearance: none;
  -moz-appearance: none;
  position: absolute;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

/* appearance: none; for IE11 */
_:-ms-lang(x)::-ms-backdrop, .toggle-buttons [type=radio],
_:-ms-lang(x)::-ms-backdrop, .toggle-buttons [type=checkbox] {
  visibility: hidden;
}

.toggle-buttons .button {
  z-index: 1;
}

.toggle-buttons.vertical .button {
  width: 100%;
}

.toggle-buttons:not(.vertical) :not(:first-child) .button {
  border-left: 1px solid #567;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

.toggle-buttons:not(.vertical) :not(:last-child) .button {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.toggle-buttons.vertical :not(:first-child) .button {
  border-top: 1px solid #567;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

.toggle-buttons.vertical :not(:last-child) .button {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

.toggle-buttons :checked + .button {
  background-color: #345;
}

.toggle-buttons :disabled + .button {
  cursor: not-allowed;
  opacity: .6;
  color: #def;
}

デフォルトのチェックボックスやラジオボタンを消す方法、その上でスタイリングする方法は以下で詳しく研究されている。 HTML フォームへの高度なスタイル設定 - ウェブ開発を学ぶ | MDN

.buttonに対するスタイルは別途指定されているものとする。これから示すサンプルでは以下を用いる。

.button {
  display: inline-block;
  padding: .5em 1em;
  border-radius: 4px;
  text-align: center;
  color: #eff;
  background-color: #678;
  cursor: pointer;
}

.button:hover {
  background-color: #567;
}

.button:disabled {
  cursor: not-allowed;
  opacity: .6;
  color: #def;
}

基本形のサンプル

サンプルだけのページはこちら





<form>
  <div class="toggle-buttons">
    <label>
      <input type="radio">
      <span class="button">Radio</span>
    </label>
  </div>
  <hr>
  <div class="toggle-buttons">
    <label>
      <input type="radio" name="radio-1" required>
      <span class="button">Radio (required)</span>
    </label>
    <label>
      <input type="radio" name="radio-1" disabled>
      <span class="button">Disabled</span>
    </label>
    <label>
      <input type="radio" name="radio-1">
      <span class="button">Radio</span>
    </label>
  </div>
  <hr>
  <button type="submit" class="button">Submit</button>
</form>
<form>
  <div class="toggle-buttons">
    <label>
      <input type="checkbox">
      <span class="button">Checkbox</span>
    </label>
  </div>
  <hr>
  <div class="toggle-buttons">
    <label>
      <input type="checkbox" disabled>
      <span class="button">Disabled</span>
    </label>
    <label>
      <input type="checkbox" checked>
      <span class="button">Checkbox</span>
    </label>
    <label>
      <input type="checkbox">
      <span class="button">Checkbox</span>
    </label>
  </div>
  <hr>
  <div class="toggle-buttons">
    <label>
      <input type="checkbox">
      <span class="button">Checkbox</span>
    </label>
    <label>
      <input type="checkbox" checked>
      <div class="button">
        Multiple<br>
        lines
      </div>
    </label>
    <label>
      <input type="checkbox">
      <span class="button">Checkbox</span>
    </label>
  </div>
</form>

縦型配置のサンプル

<form>
  <div class="toggle-buttons vertical">
    <label>
      <input type="radio" name="radio-1">
      <span class="button">Radio</span>
    </label>
    <label>
      <input type="radio" name="radio-1">
      <span class="button">Radio</span>
    </label>
    <label>
      <input type="radio" name="radio-1">
      <span class="button">Radio</span>
    </label>
    <label>
      <input type="radio" name="radio-1" disabled>
      <span class="button">Disabled</span>
    </label>
    <label>
      <input type="radio" name="radio-1">
      <span class="button">Radio</span>
    </label>
  </div>
</form>