:has()で純CSS連動
JS無しで:has()を使い、ラジオ選択でカードがハイライト&詳細パネルが開き、同意チェックでボタンが活性風に変化。状態連動UIをCSSだけで実現します。
ライブデモ
使用例(お題: アイドルグループ Sakura)
この技法を「アイドルグループ Sakura」というテーマのダミーサイトで実際に使った例です。
HTML
<!-- Sakura:推しグッズ選択を :has() で連動(JS無し) -->
<div class="hs">
<div class="hs__bar"><span class="hs__logo">🌸 Sakura</span><span class="hs__sub">GOODS STORE</span></div>
<h2 class="hs__title">アクリルスタンドを選ぶ</h2>
<!-- ラジオ選択で :has(:checked) によりカードがハイライト&詳細展開 -->
<div class="hs__grid">
<label class="plan">
<input type="radio" name="goods" class="plan__input">
<span class="plan__head"><span class="plan__name">桜井 ひな ver.</span><span class="plan__price">¥1,500</span></span>
<span class="plan__detail">ライブ衣装デザイン。高さ12cm・台座付き。受注生産。</span>
<span class="plan__check" aria-hidden="true">✓</span>
</label>
<label class="plan">
<input type="radio" name="goods" class="plan__input" checked>
<span class="plan__badge">人気</span>
<span class="plan__head"><span class="plan__name">月城 あおい ver.</span><span class="plan__price">¥1,500</span></span>
<span class="plan__detail">私服デザインの描き下ろし。スマホスタンドにもなる仕様。</span>
<span class="plan__check" aria-hidden="true">✓</span>
</label>
<label class="plan">
<input type="radio" name="goods" class="plan__input">
<span class="plan__head"><span class="plan__name">5人 コンプセット</span><span class="plan__price">¥6,800</span></span>
<span class="plan__detail">全メンバー+特典ステージ台座付き。お得なセット。</span>
<span class="plan__check" aria-hidden="true">✓</span>
</label>
</div>
<!-- 同意チェックでボタンが活性風に変化 -->
<form class="hs__form">
<label class="agree">
<input type="checkbox" class="agree__input">
<span class="agree__box" aria-hidden="true"></span>
<span>受注生産・お届け時期に同意する</span>
</label>
<button type="button" class="submit">カートに入れる</button>
</form>
</div>
CSS
/* Sakura アイドル テーマ */
:root{--pink:#ffd1e0;--deep:#e86a96;--ink:#4a3540;--muted:#9b8690;--card:#fff;--line:#f0dde4;--ok:#e86a96}
*{box-sizing:border-box}
body{
margin:0;min-height:100vh;display:grid;place-items:center;padding:16px;
font-family:"Hiragino Kaku Gothic ProN","Segoe UI",sans-serif;color:var(--ink);
background:radial-gradient(600px 320px at 50% -10%,#ffe3ee,transparent),#fff5f9;
}
.hs{width:min(420px,100%)}
.hs__bar{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}
.hs__logo{font-weight:800;color:var(--deep)}
.hs__sub{font-size:.72rem;letter-spacing:.12em;color:var(--muted)}
.hs__title{margin:0 0 12px;font-size:1.05rem}
.hs__grid{display:grid;gap:10px}
/* グッズカード(label) */
.plan{position:relative;display:grid;gap:5px;cursor:pointer;padding:13px 15px;border-radius:14px;background:var(--card);border:1px solid var(--line);box-shadow:0 4px 12px rgba(232,106,150,.06);transition:border-color .2s,box-shadow .2s,background .2s}
.plan__input{position:absolute;opacity:0;pointer-events:none}
.plan__head{display:flex;align-items:baseline;justify-content:space-between}
.plan__name{font-weight:700;font-size:.98rem}
.plan__price{color:var(--deep);font-size:.9rem;font-weight:700}
.plan__badge{position:absolute;top:-9px;right:14px;font-size:.64rem;font-weight:800;letter-spacing:.06em;color:#fff;background:var(--deep);padding:3px 9px;border-radius:999px}
/* 詳細パネル:選択時に開く */
.plan__detail{color:var(--muted);font-size:.82rem;line-height:1.55;max-height:0;opacity:0;overflow:hidden;transition:max-height .3s ease,opacity .25s ease,margin .3s ease}
.plan__check{position:absolute;top:13px;right:15px;width:22px;height:22px;border-radius:50%;display:grid;place-items:center;font-size:.72rem;font-weight:700;color:#fff;background:var(--deep);opacity:0;transform:scale(.5);transition:opacity .2s,transform .25s cubic-bezier(.2,.9,.3,1.2)}
.plan:hover{border-color:var(--pink)}
/* ★ :has() — 選択カードをハイライト&詳細展開 */
.plan:has(.plan__input:checked){border-color:var(--deep);background:linear-gradient(160deg,#fff,#ffeef4);box-shadow:0 14px 30px -16px rgba(232,106,150,.6)}
.plan:has(.plan__input:checked) .plan__detail{max-height:60px;opacity:1;margin-top:2px}
.plan:has(.plan__input:checked) .plan__check{opacity:1;transform:scale(1)}
/* ★ 選択中なら案内文を表示(兄弟連動) */
.hs__grid:has(.plan__input:checked) + .hs__form::before{content:"グッズ選択済み — 同意するとカートに進めます";display:block;font-size:.76rem;color:var(--muted);margin-bottom:10px;text-align:center}
/* 同意チェック行 */
.hs__form{margin-top:16px}
.agree{display:flex;align-items:center;gap:9px;cursor:pointer;font-size:.86rem;margin-bottom:12px;user-select:none}
.agree__input{position:absolute;opacity:0;pointer-events:none}
.agree__box{flex:none;width:20px;height:20px;border-radius:6px;border:2px solid var(--muted);position:relative;transition:all .2s}
.agree__box::after{content:"✓";position:absolute;inset:0;display:grid;place-items:center;font-size:.7rem;font-weight:700;color:#fff;opacity:0;transition:opacity .15s}
.agree:has(.agree__input:checked) .agree__box{background:var(--ok);border-color:var(--ok)}
.agree:has(.agree__input:checked) .agree__box::after{opacity:1}
/* ボタン:既定は無効風 */
.submit{width:100%;font:inherit;font-weight:700;border:none;border-radius:12px;padding:13px;color:var(--muted);background:#f3e6ec;cursor:not-allowed;transition:all .25s}
/* ★ 同意済みのときだけ活性風に */
.hs__form:has(.agree__input:checked) .submit{color:#fff;cursor:pointer;background:linear-gradient(135deg,#ff9ec0,var(--deep));box-shadow:0 14px 28px -12px rgba(232,106,150,.7)}
.hs__form:has(.agree__input:checked) .submit:active{transform:translateY(1px)}
@media (prefers-reduced-motion:reduce){.plan,.plan__detail,.plan__check,.agree__box,.submit{transition:none}}
JavaScript
// 純CSS(:has())のみで連動。JSは不要。
コード
HTML
<!-- :has() で純CSS連動:選択状態に応じて他要素がハイライト&詳細パネルが開く(JS無し) -->
<div class="hs">
<h2 class="hs__title">プランを選択</h2>
<!-- ラジオで選んだカードが :has(:checked) でハイライト&詳細展開 -->
<div class="hs__grid">
<label class="plan">
<input type="radio" name="plan" class="plan__input">
<span class="plan__head">
<span class="plan__name">Free</span>
<span class="plan__price">¥0</span>
</span>
<span class="plan__detail">個人利用に。3プロジェクトまで・コミュニティ支援。</span>
<span class="plan__check" aria-hidden="true">✓</span>
</label>
<label class="plan">
<input type="radio" name="plan" class="plan__input" checked>
<span class="plan__badge">人気</span>
<span class="plan__head">
<span class="plan__name">Pro</span>
<span class="plan__price">¥980</span>
</span>
<span class="plan__detail">無制限プロジェクト・優先サポート・チーム共有。</span>
<span class="plan__check" aria-hidden="true">✓</span>
</label>
<label class="plan">
<input type="radio" name="plan" class="plan__input">
<span class="plan__head">
<span class="plan__name">Team</span>
<span class="plan__price">¥2,980</span>
</span>
<span class="plan__detail">SSO・権限管理・監査ログ。組織での運用に。</span>
<span class="plan__check" aria-hidden="true">✓</span>
</label>
</div>
<!-- 同意チェックの有無で「申し込む」ボタンが活性風に変化 -->
<form class="hs__form">
<label class="agree">
<input type="checkbox" class="agree__input">
<span class="agree__box" aria-hidden="true"></span>
<span>利用規約に同意する</span>
</label>
<button type="button" class="submit">申し込む</button>
</form>
</div>
CSS
:root{
--bg:#0b1020;
--card:#161c30;
--card2:#1e253c;
--text:#e7ebf5;
--muted:#9aa3bd;
--accent:#818cf8;
--accent2:#a5b4fc;
--ok:#22c55e;
--line:rgba(129,140,248,.16);
}
*{box-sizing:border-box}
body{
margin:0;min-height:100vh;
display:grid;place-items:center;padding:20px;
font-family:"Segoe UI",system-ui,sans-serif;color:var(--text);
background:radial-gradient(700px 360px at 50% -10%,#222c5c,transparent),var(--bg);
}
.hs{width:min(440px,100%)}
.hs__title{margin:0 0 14px;font-size:1.1rem;text-align:center}
.hs__grid{display:grid;gap:10px}
/* プランカード(label) */
.plan{
position:relative;display:grid;gap:6px;cursor:pointer;
padding:13px 15px;border-radius:13px;
background:var(--card);border:1px solid var(--line);
transition:border-color .2s,background .2s,box-shadow .2s;
}
.plan__input{position:absolute;opacity:0;pointer-events:none}
.plan__head{display:flex;align-items:baseline;justify-content:space-between}
.plan__name{font-weight:700;font-size:1rem}
.plan__price{color:var(--muted);font-size:.9rem}
.plan__badge{
position:absolute;top:-9px;right:14px;
font-size:.66rem;font-weight:700;letter-spacing:.08em;color:#0b1020;
background:linear-gradient(135deg,var(--accent2),var(--accent));
padding:3px 9px;border-radius:999px;
}
/* 詳細パネル:通常は閉じ、選択時に開く */
.plan__detail{
color:var(--muted);font-size:.83rem;line-height:1.55;
max-height:0;opacity:0;overflow:hidden;
transition:max-height .3s ease,opacity .25s ease,margin .3s ease;
}
.plan__check{
position:absolute;top:13px;right:15px;
width:22px;height:22px;border-radius:50%;
display:grid;place-items:center;font-size:.74rem;font-weight:700;color:#0b1020;
background:var(--accent);opacity:0;transform:scale(.5);
transition:opacity .2s,transform .25s cubic-bezier(.2,.9,.3,1.2);
}
.plan:hover{border-color:rgba(129,140,248,.4)}
/* ★ :has() — 選択されたカードをハイライト&詳細を展開 */
.plan:has(.plan__input:checked){
border-color:var(--accent);
background:linear-gradient(160deg,var(--card2),rgba(129,140,248,.1));
box-shadow:0 16px 32px -18px rgba(129,140,248,.7);
}
.plan:has(.plan__input:checked) .plan__detail{
max-height:60px;opacity:1;margin-top:2px;
}
.plan:has(.plan__input:checked) .plan__check{opacity:1;transform:scale(1)}
.plan:has(.plan__input:checked) .plan__price{color:var(--accent2)}
/* ★ いずれか選択中なら見出しに「選択中」を出す(兄弟連動) */
.hs__grid:has(.plan__input:checked) + .hs__form::before{
content:"プラン選択済み — 規約に同意すると進めます";
display:block;font-size:.78rem;color:var(--muted);margin-bottom:10px;text-align:center;
}
/* 同意チェック行 */
.hs__form{margin-top:16px}
.agree{
display:flex;align-items:center;gap:9px;cursor:pointer;
font-size:.88rem;margin-bottom:12px;user-select:none;
}
.agree__input{position:absolute;opacity:0;pointer-events:none}
.agree__box{
flex:none;width:20px;height:20px;border-radius:6px;
border:2px solid var(--muted);position:relative;transition:all .2s;
}
.agree__box::after{
content:"✓";position:absolute;inset:0;
display:grid;place-items:center;font-size:.7rem;font-weight:700;color:#0b1020;
opacity:0;transition:opacity .15s;
}
/* ★ チェック状態をボックスに反映 */
.agree:has(.agree__input:checked) .agree__box{
background:var(--ok);border-color:var(--ok);
}
.agree:has(.agree__input:checked) .agree__box::after{opacity:1}
/* 申し込むボタン:既定は無効風 */
.submit{
width:100%;font:inherit;font-weight:700;border:none;border-radius:12px;
padding:13px;color:var(--muted);background:var(--card2);
cursor:not-allowed;transition:all .25s;
}
/* ★ フォームが checkbox:checked を含むときだけ活性風に */
.hs__form:has(.agree__input:checked) .submit{
color:#0b1020;cursor:pointer;
background:linear-gradient(135deg,var(--accent2),var(--accent));
box-shadow:0 14px 30px -12px rgba(129,140,248,.7);
}
.hs__form:has(.agree__input:checked) .submit:active{transform:translateY(1px)}
@media (prefers-reduced-motion:reduce){
.plan,.plan__detail,.plan__check,.agree__box,.submit{transition:none}
}
JavaScript
// 純CSS(:has())のみで連動。JSは不要。
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「:has()で純CSS連動」の効果を追加してください。
# 追加してほしい効果
:has()で純CSS連動(UIコンポーネント)
JS無しで:has()を使い、ラジオ選択でカードがハイライト&詳細パネルが開き、同意チェックでボタンが活性風に変化。状態連動UIをCSSだけで実現します。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- :has() で純CSS連動:選択状態に応じて他要素がハイライト&詳細パネルが開く(JS無し) -->
<div class="hs">
<h2 class="hs__title">プランを選択</h2>
<!-- ラジオで選んだカードが :has(:checked) でハイライト&詳細展開 -->
<div class="hs__grid">
<label class="plan">
<input type="radio" name="plan" class="plan__input">
<span class="plan__head">
<span class="plan__name">Free</span>
<span class="plan__price">¥0</span>
</span>
<span class="plan__detail">個人利用に。3プロジェクトまで・コミュニティ支援。</span>
<span class="plan__check" aria-hidden="true">✓</span>
</label>
<label class="plan">
<input type="radio" name="plan" class="plan__input" checked>
<span class="plan__badge">人気</span>
<span class="plan__head">
<span class="plan__name">Pro</span>
<span class="plan__price">¥980</span>
</span>
<span class="plan__detail">無制限プロジェクト・優先サポート・チーム共有。</span>
<span class="plan__check" aria-hidden="true">✓</span>
</label>
<label class="plan">
<input type="radio" name="plan" class="plan__input">
<span class="plan__head">
<span class="plan__name">Team</span>
<span class="plan__price">¥2,980</span>
</span>
<span class="plan__detail">SSO・権限管理・監査ログ。組織での運用に。</span>
<span class="plan__check" aria-hidden="true">✓</span>
</label>
</div>
<!-- 同意チェックの有無で「申し込む」ボタンが活性風に変化 -->
<form class="hs__form">
<label class="agree">
<input type="checkbox" class="agree__input">
<span class="agree__box" aria-hidden="true"></span>
<span>利用規約に同意する</span>
</label>
<button type="button" class="submit">申し込む</button>
</form>
</div>
【CSS】
:root{
--bg:#0b1020;
--card:#161c30;
--card2:#1e253c;
--text:#e7ebf5;
--muted:#9aa3bd;
--accent:#818cf8;
--accent2:#a5b4fc;
--ok:#22c55e;
--line:rgba(129,140,248,.16);
}
*{box-sizing:border-box}
body{
margin:0;min-height:100vh;
display:grid;place-items:center;padding:20px;
font-family:"Segoe UI",system-ui,sans-serif;color:var(--text);
background:radial-gradient(700px 360px at 50% -10%,#222c5c,transparent),var(--bg);
}
.hs{width:min(440px,100%)}
.hs__title{margin:0 0 14px;font-size:1.1rem;text-align:center}
.hs__grid{display:grid;gap:10px}
/* プランカード(label) */
.plan{
position:relative;display:grid;gap:6px;cursor:pointer;
padding:13px 15px;border-radius:13px;
background:var(--card);border:1px solid var(--line);
transition:border-color .2s,background .2s,box-shadow .2s;
}
.plan__input{position:absolute;opacity:0;pointer-events:none}
.plan__head{display:flex;align-items:baseline;justify-content:space-between}
.plan__name{font-weight:700;font-size:1rem}
.plan__price{color:var(--muted);font-size:.9rem}
.plan__badge{
position:absolute;top:-9px;right:14px;
font-size:.66rem;font-weight:700;letter-spacing:.08em;color:#0b1020;
background:linear-gradient(135deg,var(--accent2),var(--accent));
padding:3px 9px;border-radius:999px;
}
/* 詳細パネル:通常は閉じ、選択時に開く */
.plan__detail{
color:var(--muted);font-size:.83rem;line-height:1.55;
max-height:0;opacity:0;overflow:hidden;
transition:max-height .3s ease,opacity .25s ease,margin .3s ease;
}
.plan__check{
position:absolute;top:13px;right:15px;
width:22px;height:22px;border-radius:50%;
display:grid;place-items:center;font-size:.74rem;font-weight:700;color:#0b1020;
background:var(--accent);opacity:0;transform:scale(.5);
transition:opacity .2s,transform .25s cubic-bezier(.2,.9,.3,1.2);
}
.plan:hover{border-color:rgba(129,140,248,.4)}
/* ★ :has() — 選択されたカードをハイライト&詳細を展開 */
.plan:has(.plan__input:checked){
border-color:var(--accent);
background:linear-gradient(160deg,var(--card2),rgba(129,140,248,.1));
box-shadow:0 16px 32px -18px rgba(129,140,248,.7);
}
.plan:has(.plan__input:checked) .plan__detail{
max-height:60px;opacity:1;margin-top:2px;
}
.plan:has(.plan__input:checked) .plan__check{opacity:1;transform:scale(1)}
.plan:has(.plan__input:checked) .plan__price{color:var(--accent2)}
/* ★ いずれか選択中なら見出しに「選択中」を出す(兄弟連動) */
.hs__grid:has(.plan__input:checked) + .hs__form::before{
content:"プラン選択済み — 規約に同意すると進めます";
display:block;font-size:.78rem;color:var(--muted);margin-bottom:10px;text-align:center;
}
/* 同意チェック行 */
.hs__form{margin-top:16px}
.agree{
display:flex;align-items:center;gap:9px;cursor:pointer;
font-size:.88rem;margin-bottom:12px;user-select:none;
}
.agree__input{position:absolute;opacity:0;pointer-events:none}
.agree__box{
flex:none;width:20px;height:20px;border-radius:6px;
border:2px solid var(--muted);position:relative;transition:all .2s;
}
.agree__box::after{
content:"✓";position:absolute;inset:0;
display:grid;place-items:center;font-size:.7rem;font-weight:700;color:#0b1020;
opacity:0;transition:opacity .15s;
}
/* ★ チェック状態をボックスに反映 */
.agree:has(.agree__input:checked) .agree__box{
background:var(--ok);border-color:var(--ok);
}
.agree:has(.agree__input:checked) .agree__box::after{opacity:1}
/* 申し込むボタン:既定は無効風 */
.submit{
width:100%;font:inherit;font-weight:700;border:none;border-radius:12px;
padding:13px;color:var(--muted);background:var(--card2);
cursor:not-allowed;transition:all .25s;
}
/* ★ フォームが checkbox:checked を含むときだけ活性風に */
.hs__form:has(.agree__input:checked) .submit{
color:#0b1020;cursor:pointer;
background:linear-gradient(135deg,var(--accent2),var(--accent));
box-shadow:0 14px 30px -12px rgba(129,140,248,.7);
}
.hs__form:has(.agree__input:checked) .submit:active{transform:translateY(1px)}
@media (prefers-reduced-motion:reduce){
.plan,.plan__detail,.plan__check,.agree__box,.submit{transition:none}
}
【JavaScript】
// 純CSS(:has())のみで連動。JSは不要。
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。