HTML/CSS ラジオボタンの見た目を変更したい

## 背景
- 初心者の身分でありながら, HTMLでラジオボタンを実装したときにデフォルトのデザインがダサいと思ってしまった.
- パパっと変えられるかと舐めてたけど, 疑似要素を使うみたいなので初心者には一見難しかった. ごめんなさい.
- これを機にCSSの疑似要素を使いこなせるようになろうと思うので（今後も利用の機会がありそうなので）, 備忘録的に記録および同じ思いを持つ方への共有の意味も込めて投稿することにします.


## 概要
左図ようなデフォルトのラジオボタンを右図のようにそこそこ見栄えが良いものにしたい. 
![20230620_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3484687/e635b118-8d07-a8f0-8dee-b3a4aeb4a6a5.png)

## 実装
### HTML
- ラジオボタンを等間隔で配置するために `div` 要素で囲う.
    - `<div class="radio-btn-area">` をflexbox化するために, `<div class="radio-btn">` で内部のブロック要素を構成しています.
    - `<div class="radio-btn">` の内部がすべて `label` タグで埋め尽くされることになるので, ここでラジオボタンの間隔や配置を調整します. 
- `<div class="radio-btn">` 内は至って普通のラジオボタンの実装だが,  `label` タグをブロック要素化して内部の文字の配置を柔軟に設定するために, `span` タグをつける.
```html
<div class="radio-btn-area">
  <div class="radio-btn">
    <input type="radio" name="radio" id="sukiyaki" value="すき焼き" />
    <label for="sukiyaki"><span>すき焼き</span></label>
  </div>
  <div class="radio-btn">
    <input type="radio" name="radio" id="shabushabu" value="しゃぶしゃぶ" />
    <label for="shabushabu"><span>しゃぶしゃぶ</span></label>
  </div>
  <div class="radio-btn">
    <input type="radio" name="radio" id="yudofu" value="湯豆腐" checked />
    <label for="yudofu"><span>湯豆腐</span></label>
  </div>
</div>
```
### CSS
- イメージとしては `<div class="radio-btn">` 内で `label` を `display: block` で枠いっぱいに広げて形や色をデザインしていく感じ.
- `input` で付与されるプリセットのラジオボタンは `display: none` で（無理やり）消して, `label` に `::before` や `::after` 疑似クラスを適用してデザインする.
    - `content: ""` によって, 中身のないただの図形のような扱いとして色や形, 位置を変更することができます.
    - 今回は `label` ブロック内で位置調整することになるので, `label` に `position: relative` を,  `label::before`, `label::after` に `position: absolute` を指定することで, ラジオボタン内での位置調整を行いやすくしています.
- 選択された状態限定のCSSは `input` に `:checked` 疑似クラスを活用することで適用できる.
    - 今回は `label` の背景色や文字のスタイルも変更したいので, それの隣接セレクタを指定することで選択状態の `label` にCSSをうまく適用することができます.
```css
/* 外枠となるflexbox, ラジオボタンの間隔や配置を調整 */
.radio-btn-area {
  width: 180px;
  height: 140px;
  margin: 100px 0 0 100px;
  padding: 25px;
  border: gray solid 2px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 10px;
}

/* ラジオボタン1つの大枠, ラジオボタンの大きさを調整 */
.radio-btn {
  width: 100%;
  height: 45px;
}

/* プリセットのラジオボタンを削除 */
.radio-btn input {
  display: none;
}

/* 未選択状態のラジオボタンの背景色やボーダーの指定 */
.radio-btn label {
  display: flex; /* 中の文字の上下中央揃えを行うための設定 */
  width: 100%; /* ラジオボタンを大枠いっぱいに広げるための設定 */
  height: 100%; /* ラジオボタンを大枠いっぱいに広げるための設定 */
  background-color: #d1ecf1;
  border: #1e6cc0 solid 1px;
  border-radius: 3px;
  align-items: center; /* 中の文字の上下中央揃えを行うための設定 */
  position: relative; /* 疑似要素の位置調整のための設定 */
}

/* 中の文字列の位置調整 */
.radio-btn span {
  display: block; /* ラジオボタン内の文字の上下中央揃えを行うための設定 */
  width: 100%;
  padding-left: 26px; /* 左側のボタン設置スペース確保のための設定 */
  padding-right: 4px; /* 文字列が右側にギリギリにならないようにするための設定 */
}

/* 選択/未選択時の両方に存在する白い丸を作成 */
.radio-btn label::before,
.radio-btn input:checked + label::before {
  content: "";
  width: 16px; /* 白い丸の横の長さ */
  height: 16px; /* 白い丸の縦の長さ */
  border-radius: 50%; /* 丸くするための設定 */
  background-color: #ffffff; /* 丸の色の設定 */
  position: absolute; /* 位置調整の起点をlabelの左上の角とするための設定 */
  top: 50%;
  left: 13px;
  transform: translate(-50%, -50%); /* topとleftが丸の中心を基準とするための設定 */
  z-index: 10;
}

/* 選択時のみに存在する青い丸を作成, 基本は白い丸とすべて同じ */
.radio-btn input:checked + label::after {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: #1e6cc0;
  position: absolute;
  top: 50%;
  left: 13px;
  transform: translate(-50%, -50%);
  z-index: 11; /* 白い丸より確実に前面に配置されるように調整 */
}

/* 選択時のみのlabelのデザイン指定 */
.radio-btn input:checked + label {
  color: #ffffff;
  font-weight: bold;
  background-color: #1e6cc0;
  z-index: 8;
}
```

## 補足
- `input` タグを `label` タグの子要素にする方法もあるが, 子要素を軸に親要素へCSSを適用する方法が難しそうなので隣接セレクタを使用できる今回の方法にした.
- CSS4の `:has()` 疑似クラスでも実装でき, 特定のcssが付与された要素を子要素として持つ親要素をセレクタとして使用できるらしい（なにそれ便利）.  
- ただ, 非対応ブラウザが存在しているようで対応済みになるまでは今回のような方法でもよさそう.

## 総括
- 疑似要素とブロック要素化を駆使して, そこそこスタイリッシュなラジオボタンが実装できた.
- 中の文字列が動的ならば, 文字列長によって高さが変わるような設定（ `height: auto` など）が必要そう.
- チェックボックスなどにも流用できるので, 活用の幅は広そうですね.


## 参考サイト様
- [HTML・CSSでつくるおしゃれなラジオボタンのデザイン3選](https://pote-chil.com/html-maker/radio)
    - `:has()` 疑似クラスを使用したラジオボタンのデザインCSS.