表示例
アイコン: Font Awesome 7 Free(fa-solid / fa-regular)
別名・英語表記
- カード型ラジオボタン
- ラジオカード
- 選択タイル
- プラン選択カード
- カード型の選択肢
- Choice card
実装例
<fieldset class="x-choice-card">
<legend class="x-choice-card__lg">プランを選ぶ</legend>
<!-- カード全体が label なので、どこを押しても中の radio が選ばれる。
名前はタイトルだけにし、説明と料金は aria-describedby で後から読ませる -->
<div class="x-choice-card__list">
<label class="x-choice-card__c">
<input class="x-choice-card__in" type="radio" name="plan" value="lite"
aria-labelledby="x-choice-card-t1" aria-describedby="x-choice-card-d1">
<i class="x-choice-card__mk fa-solid fa-circle-check" aria-hidden="true"></i>
<span class="x-choice-card__t" id="x-choice-card-t1">ライト</span>
<span class="x-choice-card__d" id="x-choice-card-d1">1人で使う。保存容量 5GB。月 500円</span>
</label>
<label class="x-choice-card__c">
<input class="x-choice-card__in" type="radio" name="plan" value="team" checked
aria-labelledby="x-choice-card-t2" aria-describedby="x-choice-card-d2">
<i class="x-choice-card__mk fa-solid fa-circle-check" aria-hidden="true"></i>
<span class="x-choice-card__t" id="x-choice-card-t2">チーム</span>
<span class="x-choice-card__d" id="x-choice-card-d2">10人まで。保存容量 100GB。月 2,400円</span>
</label>
</div>
</fieldset>
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-choice-card,
.x-choice-card *:not([class*="fa-"]),
.x-choice-card *:not([class*="fa-"])::before,
.x-choice-card *:not([class*="fa-"])::after {
box-sizing: border-box;
/* 貼った先の既定の書体に引きずられないようにする。書体が変わると
文字の幅と高さが変わり、枠のサイズまでずれる */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}
.x-choice-card {
width: 320px;
max-width: 100%;
margin: 0;
padding: 0;
border: 0;
color: #1b2028;
}
.x-choice-card__lg {
margin-bottom: 8px;
padding: 0;
font-size: 12px;
font-weight: 700;
}
.x-choice-card__list {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
}
/* カード全体が押せる範囲。選ばれたときは枠の太さ・背景・チェックの印の3つで示し、色だけに頼らない */
.x-choice-card__c {
position: relative;
display: flex;
flex-direction: column;
gap: 4px;
min-height: 118px;
padding: 12px 12px 14px;
border: 1px solid #7f8a99;
border-radius: 10px;
background: #fff;
cursor: pointer;
}
.x-choice-card__c:hover {
border-color: #1b2028;
}
.x-choice-card__c:has(.x-choice-card__in:checked) {
border-color: #1d4ed8;
box-shadow: 0 0 0 2px #1d4ed8;
background: #eff4ff;
}
.x-choice-card__c:has(.x-choice-card__in:focus-visible) {
outline: 2px solid #1d4ed8;
outline-offset: 4px;
}
/* 本物の radio は残して見せる。形(塗りつぶした丸)でも選択が分かり、強制カラーモードでも消えない */
.x-choice-card__in {
width: 18px;
height: 18px;
margin: 0 0 4px;
accent-color: #1d4ed8;
}
.x-choice-card__mk {
position: absolute;
top: 10px;
right: 10px;
color: #1d4ed8;
font-size: 18px;
visibility: hidden;
}
.x-choice-card__c:has(.x-choice-card__in:checked) .x-choice-card__mk {
visibility: visible;
}
.x-choice-card__t {
font-size: 14px;
font-weight: 700;
}
.x-choice-card__d {
color: #4b5563;
font-size: 11.5px;
line-height: 1.6;
}
AIへの指示文
「選択カード(Choice card)」を実装してください。
## これは何か
枠で囲んだカードを並べ、どれか1つを押して選ぶ入力。見出し・説明・料金がカードに入り、選んだカードは枠が太くなりチェックの印が付く。
## 使いどころ
- 料金プラン・配送方法・支払い方法のように、選択肢ごとに説明や金額を見比べてから選んでほしいとき
- 選択肢が2〜4個で、1行の文言では違いが伝わらないとき
## 実装の要件
- fieldset > legend + label.カード > input[type=radio][name 共通] + タイトル + 説明。label で包むのでカードのどこを押しても選ばれる
- 選ばれたカードの見た目は label:has(input:checked) で切り替える。:has() は Chrome・Edge は 105 から、Safari は 15.4 から、Firefox は 121 から対応し、Baseline の「広く利用可能」に入っているので、input:checked + 兄弟要素の形の代替は書かない
- フォーカスの輪郭は label:has(input:focus-visible) でカードの外側に出す
- 既定で1つ選んでおかないなら required を付け、未選択のまま送られたときのメッセージを legend のそばに出す
- ラジオボタンとの違い: 中身は同じ「1つだけ選ぶ」入力。選択肢に説明や料金が付き、見比べさせたいときだけカードにする。名前だけで分かる選択肢をカードにすると、場所を取るだけで読む量が増える
- カード(container/card)との違い: あちらは情報のまとまりを見せる器で、押しても選択の状態を持たない。選択カードは中に本物の radio があり、フォームとして値を送る
- 選んだ状態を色の差だけで示さない。枠の太さ・チェックの印・radio の塗りのように形でも変える
- カードの中にリンクやボタンを入れない。label の中の操作部品は押し分けにくく、どちらが反応するか分からなくなる
- 複数選べる場合は radio を checkbox に替える。見た目が同じでも、1つだけか複数かを legend や補足文で言葉にする
## アクセシビリティの要件
- div と onclick で作らず、input type="radio" を label で包む。矢印キーでの移動・Space での選択・フォーム送信がそのまま動く
- radio を display:none や visibility:hidden で消さない。フォーカスも読み上げも無くなる。見せたくないときは見た目だけ隠し、カードの枠に :focus-visible の輪郭を出す
- グループは fieldset と legend で囲み、何を選ぶのかを言葉にする(1.3.1 情報及び関係性)
- カード全体が label だと、中の文章がすべて名前として読まれて長くなる。aria-labelledby でタイトルだけを名前にし、説明と料金は aria-describedby で結ぶ
- 選択の表示は色だけに頼らない(1.4.1 色の用途)。カードの枠は背景に対して 3:1 以上にする(1.4.11 非テキストのコントラスト)
## 参考にする実装
この形を出発点にして、プロジェクトの命名規則・色・余白に合わせて調整してください。
```html
<fieldset class="x-choice-card">
<legend class="x-choice-card__lg">プランを選ぶ</legend>
<!-- カード全体が label なので、どこを押しても中の radio が選ばれる。
名前はタイトルだけにし、説明と料金は aria-describedby で後から読ませる -->
<div class="x-choice-card__list">
<label class="x-choice-card__c">
<input class="x-choice-card__in" type="radio" name="plan" value="lite"
aria-labelledby="x-choice-card-t1" aria-describedby="x-choice-card-d1">
<i class="x-choice-card__mk fa-solid fa-circle-check" aria-hidden="true"></i>
<span class="x-choice-card__t" id="x-choice-card-t1">ライト</span>
<span class="x-choice-card__d" id="x-choice-card-d1">1人で使う。保存容量 5GB。月 500円</span>
</label>
<label class="x-choice-card__c">
<input class="x-choice-card__in" type="radio" name="plan" value="team" checked
aria-labelledby="x-choice-card-t2" aria-describedby="x-choice-card-d2">
<i class="x-choice-card__mk fa-solid fa-circle-check" aria-hidden="true"></i>
<span class="x-choice-card__t" id="x-choice-card-t2">チーム</span>
<span class="x-choice-card__d" id="x-choice-card-d2">10人まで。保存容量 100GB。月 2,400円</span>
</label>
</div>
</fieldset>
```
```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-choice-card,
.x-choice-card *:not([class*="fa-"]),
.x-choice-card *:not([class*="fa-"])::before,
.x-choice-card *:not([class*="fa-"])::after {
box-sizing: border-box;
/* 貼った先の既定の書体に引きずられないようにする。書体が変わると
文字の幅と高さが変わり、枠のサイズまでずれる */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}
.x-choice-card {
width: 320px;
max-width: 100%;
margin: 0;
padding: 0;
border: 0;
color: #1b2028;
}
.x-choice-card__lg {
margin-bottom: 8px;
padding: 0;
font-size: 12px;
font-weight: 700;
}
.x-choice-card__list {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
}
/* カード全体が押せる範囲。選ばれたときは枠の太さ・背景・チェックの印の3つで示し、色だけに頼らない */
.x-choice-card__c {
position: relative;
display: flex;
flex-direction: column;
gap: 4px;
min-height: 118px;
padding: 12px 12px 14px;
border: 1px solid #7f8a99;
border-radius: 10px;
background: #fff;
cursor: pointer;
}
.x-choice-card__c:hover {
border-color: #1b2028;
}
.x-choice-card__c:has(.x-choice-card__in:checked) {
border-color: #1d4ed8;
box-shadow: 0 0 0 2px #1d4ed8;
background: #eff4ff;
}
.x-choice-card__c:has(.x-choice-card__in:focus-visible) {
outline: 2px solid #1d4ed8;
outline-offset: 4px;
}
/* 本物の radio は残して見せる。形(塗りつぶした丸)でも選択が分かり、強制カラーモードでも消えない */
.x-choice-card__in {
width: 18px;
height: 18px;
margin: 0 0 4px;
accent-color: #1d4ed8;
}
.x-choice-card__mk {
position: absolute;
top: 10px;
right: 10px;
color: #1d4ed8;
font-size: 18px;
visibility: hidden;
}
.x-choice-card__c:has(.x-choice-card__in:checked) .x-choice-card__mk {
visibility: visible;
}
.x-choice-card__t {
font-size: 14px;
font-weight: 700;
}
.x-choice-card__d {
color: #4b5563;
font-size: 11.5px;
line-height: 1.6;
}
```
## 出力の約束
- HTML と CSS を分けて出す。JavaScript が要るときは素の JavaScript で書き、UIフレームワークは使わない。
- 参考実装がアイコンに Font Awesome のクラスを使っている場合は、プロジェクトで使っているアイコンかインライン SVG に置き換えてよい。
- 上のアクセシビリティ要件を満たす。満たせない箇所があるときは、理由を添えて先に知らせる。
- 色・角丸・余白は既存のデザイントークンがあればそれを使う。無ければ CSS 変数として切り出す。
使いどころ
- 料金プラン・配送方法・支払い方法のように、選択肢ごとに説明や金額を見比べてから選んでほしいとき
- 選択肢が2〜4個で、1行の文言では違いが伝わらないとき
注意点
- ラジオボタンとの違い: 中身は同じ「1つだけ選ぶ」入力。選択肢に説明や料金が付き、見比べさせたいときだけカードにする。名前だけで分かる選択肢をカードにすると、場所を取るだけで読む量が増える
- カード(container/card)との違い: あちらは情報のまとまりを見せる器で、押しても選択の状態を持たない。選択カードは中に本物の radio があり、フォームとして値を送る
- 選んだ状態を色の差だけで示さない。枠の太さ・チェックの印・radio の塗りのように形でも変える
- カードの中にリンクやボタンを入れない。label の中の操作部品は押し分けにくく、どちらが反応するか分からなくなる
- 複数選べる場合は radio を checkbox に替える。見た目が同じでも、1つだけか複数かを legend や補足文で言葉にする
アクセシビリティの要点
- div と onclick で作らず、input type="radio" を label で包む。矢印キーでの移動・Space での選択・フォーム送信がそのまま動く
- radio を display:none や visibility:hidden で消さない。フォーカスも読み上げも無くなる。見せたくないときは見た目だけ隠し、カードの枠に :focus-visible の輪郭を出す
- グループは fieldset と legend で囲み、何を選ぶのかを言葉にする(1.3.1 情報及び関係性)
- カード全体が label だと、中の文章がすべて名前として読まれて長くなる。aria-labelledby でタイトルだけを名前にし、説明と料金は aria-describedby で結ぶ
- 選択の表示は色だけに頼らない(1.4.1 色の用途)。カードの枠は背景に対して 3:1 以上にする(1.4.11 非テキストのコントラスト)