GODRICH

ラジオボタン チェックボックス css カスタムアニメーション9選 — 一行でコピペOK

ラジオボタン チェックボックス css カスタムは、ブラウザ標準の丸いラジオボタンと四角いチェックボックスのinput要素は消さずに残し、隣に置いた図形だけを:checkedひとつで描き直す手法です。九つ集め、そのうち一つは四角いチェックボックスなのにスイッチの見た目だけを借りた例外です。

自動再生 · タイルを押すとその項目へ · すべて1つのzip

九つの並び順は人気順ではなく、選べる選択肢の数が増える順に置きました。01・02・06・09はチェックボックス一つが単独でオン・オフする場合、03・05・07・08はラジオでまとめて複数のうち一つだけが残る場合、04はチェックボックスなのにスイッチの姿だけを借りた例外です。

01チェックのストローク描画

チェックボックスを押すと、隠したinput要素の:checkedがSVGチェックアイコンのstroke-dashoffsetを20から0まで縮め、チェック印が左下から右上へ向けて描かれるように現れます。利用規約の同意のように「読んで押した」という手応えを伝えたい場所に向いています。

stroke-dashoffset:checked0.6초
.box {
  border: 2px solid rgba($subject-cream, .55);
  transition: background $duration $easing, border-color $duration $easing;
}
.sr-input:checked ~ .box { background: $color; border-color: $color; }

.box__path {
  stroke-dasharray: 20;
  stroke-dashoffset: 20;
  transition: stroke-dashoffset $duration $easing;
}
.sr-input:checked ~ .box .box__path { stroke-dashoffset: 0; }

02バウンドチェック

チェックが入った瞬間、丸い枠の中のチェックアイコンがscale(0)から1.3倍まで跳ね上がったあと1倍に収まる、@keyframesによるバウンドで現れます。カート追加のように、押した反応が弾んで見えてほしい場所に合います。

scale 바운스cubic-bezier0.6초
.sr-input:checked ~ .box .box__svg {
  animation: check-pop $duration $easing forwards;
}

@keyframes check-pop {
  0%   { transform: scale(0); opacity: 0; }
  55%  { transform: scale(1.3); opacity: 1; }
  75%  { transform: scale(.9); }
  100% { transform: scale(1); opacity: 1; }
}

03ラジオの塗りつぶし

三つのラジオのうち一つを選ぶと、枠の内側の点がscale(0)からscale(1)へ広がり、別の選択肢を選んだ瞬間に前の点は同じ:checked規則で即座に消えます。支払い方法や配送オプションのように一つだけが残る場面に使います。

scale:checked0.22초
.radio__dot::after {
  content: "";
  width: 12px; height: 12px;
  border-radius: 50%;
  background: $color;
  transform: scale(0);
  transition: transform $duration $easing;
}
.sr-input:checked ~ .radio__dot::after { transform: scale(1); }

04スイッチ型チェックボックス

見た目はスイッチでもノブは中央に固定したまま、トラックの背景だけがclip-path: circle()で中心から外側へじわじわ広がりオン状態を伝えます。ノブの中の二つのアイコンは同じ位置で重なったまま、opacityとscaleでクロスフェードします。

clip-path:circle():checked0.3초
.fill {
  position: absolute; inset: 0;
  background: $color;
  clip-path: circle(0% at 50% 50%);
  transition: clip-path $duration $easing;
}
.sr-input:checked ~ .track .fill {
  clip-path: circle(75% at 50% 50%);
}

05カード選択型

ラジオでまとめたカードの一つを選ぶと:has(.sr-input:checked)でそのカードの枠と背景が強調され、右上のバッジの中のチェックアイコンがscaleで現れます。:has()のおかげでラジオとバッジが兄弟でなくてもカード全体をスタイリングできます。

:has()scale0.3초
.card:has(.sr-input:checked) {
  border-color: $color;
  background: rgba($subject-cream, .14);
}
.card:has(.sr-input:checked) .card__badge {
  border-color: $color;
  background: $color;
}
.card:has(.sr-input:checked) .card__badge svg {
  opacity: 1; transform: scale(1);
}

06ToDoの取り消し線

チェックボックスをオンにすると、ラベル文字の上に::afterの取り消し線がscaleX(0)からscaleX(1)へ左から引かれ、文字のopacityが0.45まで下がります。ToDoリストやチェックリストの完了表示に使います。

scaleXopacity0.3초
.strike {
  position: absolute; inset-inline: 0; top: 50%;
  height: 2px; background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform $duration $easing;
}
.sr-input:checked ~ .todo__text .strike { transform: scaleX(1); }
.sr-input:checked ~ .todo__text .label { opacity: .45; }

07カラースウォッチ選択

円形のスウォッチの一つを選ぶと、:has()でそのlabelの::afterのリングがscale(0)からscale(1)へ広がります。スウォッチの背景色は新しいhexを足さず、デザイントークン四つをそのまま使い回しました。

box-shadow 링scale0.22초
.swatch::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: 50%;
  border: 2px solid $color;
  transform: scale(0);
  opacity: 0;
  transition: transform $duration $easing, opacity $duration $easing;
}
.swatch:has(.sr-input:checked)::after { transform: scale(1); opacity: 1; }

08星評価入力

ラジオ五つを5→1の逆順に並べ、星の並び自体をrow-reverseで反転させて画面には1→5に見せたうえで、:checked ~ .star .star__icon というきょうだいセレクタ二つの組み合わせで選んだ星とその手前の星をまとめて塗りつぶします。レビューや満足度アンケートの星評価入力に向いています。

~ 형제 선택자clip-path0.22초
.stars { display: flex; flex-direction: row-reverse; gap: 6px; }

.sr-input:checked ~ .star .star__icon,
.sr-input:checked ~ .star ~ .star .star__icon {
  background: $color;
}

09チップトグル

チップ形のチェックボックスを押すと背景が中央からscaleX(0)からscaleX(1)へ広がって塗りつぶされ、:activeの間はチップ全体がscale(.96)でわずかに押し込まれます。タグやフィルターを複数同時に選ぶときに使います。

scaleXpress scale0.22초
.fill {
  position: absolute; inset: 0;
  background: $color; z-index: 0;
  transform: scaleX(0);
  transform-origin: center;
  transition: transform $duration $easing;
}
.sr-input:checked ~ .fill { transform: scaleX(1); }
.chip:active { transform: scale(.96); }

どこで壊れるか — 見た目だけ隠したinput要素という落とし穴

九つとも本物のinput type="checkbox"またはinput type="radio"を、position: absolute; width: 1px; height: 1px; clip: rect(0,0,0,0); という書き方で見た目だけ隠していて、display: noneやvisibility: hiddenは使っていません。この二つは要素をフォーカス順序から完全に外してしまうため、キーボードだけで操作する人はTabキーでそのチェックボックス自体にたどり着けなくなります。05のカード選択型と07のカラースウォッチ選択は:has()で親要素をスタイリングしていて、この擬似クラスはChrome・Edge 105+、Safari 15.4+、Firefox 121+でしか動かず、それより古いバージョンではカードの枠が強調されないだけでクリック自体は普通に機能します。

デモの中で勝手にオン・オフが繰り返される動きは、各index.htmlに書かれた4〜6行だけの短いJavaScriptがchecked値を一定間隔で反転させているにすぎず、実際のページに組み込むときはこの一時的な再生コードを削除し、人のクリックがそのまま:checkedを変えるようにすれば十分です。九つのファイルをまとめたzipの圧縮パスワードは8zg24hjvで、そのzipの中のindex.htmlにはこの自動再生用のコードは入っていません。

アクセシビリティ

九つとも本物のinput要素を隠しはしても消してはいないため、スクリーンリーダーはチェックボックスやラジオボタンをそのまま読み上げ、TabキーとSpaceキー・矢印キーだけで状態を変えられます。prefers-reduced-motion: reduceの環境ではtransitionの長さを1msまで縮めて動きだけを消し、チェックや選択という状態自体は変わらず残します。入力にフォーカスが移ると隣の図形にリングを描くよう、九つとも:focus-visibleを加えました。

.sr-input:focus-visible ~ .box {
  box-shadow: 0 0 0 3px rgba($subject-cream, .5);
}

@media (prefers-reduced-motion: reduce) {
  .box, .box__path { transition-duration: 1ms !important; }
}

チェックボックス・ラジオボタン以外のCSSアニメーションはCSSカテゴリー一覧に、このサイトが何を届けているかは紹介ページにまとめています。

FAQ

チェックボックスとラジオボタン、どちらを使うべきですか?

一つだけを単独でオン・オフする独立した状態ならチェックボックスを、複数の選択肢のうち正確に一つだけが残るべきならラジオボタンを選びます。04のスイッチ型チェックボックスは見た目だけスイッチを借りていて実体はチェックボックスなので、複数を並べてそれぞれ個別にオン・オフできます。

labelを使わずaria-labelだけを付けても構いませんか?

スウォッチや星のように見た目だけで意味が伝わる短い項目ならaria-labelで足りますが、01のチェックのストローク描画のように文章が続く項目はlabelで囲んで文字までクリック領域に含めたほうがタッチでの押しやすさが上がります。

React版では:checkedセレクタはどう扱われますか?

CSS Moduleにコンパイルされたクラスの中でも:checkedセレクタ自体はそのまま働きますが、Reactのコンポーネント側ではchecked値をuseStateで持たせ、onChangeで書き換える制御コンポーネントにする必要があります。zipのreact/フォルダに入った九つのコンポーネントがこの形をそのまま示しています。

解凍パスワードを入力してください

パスワードはこの記事の本文の中にあります。読み進めると出てきます。