No.123
HOVER CSS

Has Selector Hover

:has() で「子がホバーされている親」を選択し、ホバーしていない兄弟だけを沈ませるフォーカス演出。JavaScriptなしで実装できる親セレクタの代表的な使い方。

DEMO

CODE

HTML
<div class="demo-stage hsh-stage">
  <div class="hsh-list">
    <a class="hsh-item" href="#" onclick="return false;"><span class="hsh-idx">01</span><span class="hsh-name">DISCOVER</span></a>
    <a class="hsh-item" href="#" onclick="return false;"><span class="hsh-idx">02</span><span class="hsh-name">DESIGN</span></a>
    <a class="hsh-item" href="#" onclick="return false;"><span class="hsh-idx">03</span><span class="hsh-name">DEVELOP</span></a>
    <a class="hsh-item" href="#" onclick="return false;"><span class="hsh-idx">04</span><span class="hsh-name">DEPLOY</span></a>
  </div>
</div>
CSS
.hsh-stage { display:flex; align-items:center; justify-content:center; padding:2.5rem; background:#0F0F12; min-height:280px; }
.hsh-list { display:flex; flex-direction:column; width:100%; max-width:420px; }
.hsh-item {
  display:flex; align-items:baseline; gap:1.2rem;
  padding:1.1rem .6rem; border-bottom:1px solid rgba(255,255,255,.08);
  text-decoration:none; color:#EDEDED;
  transition: opacity .35s ease, filter .35s ease, transform .35s cubic-bezier(.23,1,.32,1), color .35s ease;
}
.hsh-idx { font-family:'JetBrains Mono',monospace; font-size:.7rem; color:#E91B89; }
.hsh-name { font-family:'Oswald',sans-serif; font-size:1.25rem; letter-spacing:.14em; }
.hsh-list:has(.hsh-item:hover) .hsh-item:not(:hover),
.hsh-list:has(.hsh-item:focus-visible) .hsh-item:not(:focus-visible) {
  opacity:.28; filter:blur(1.5px); transform:scale(.97);
}
.hsh-item:hover, .hsh-item:focus-visible { transform:translateX(10px); color:#fff; }
.hsh-item:focus-visible { outline:2px solid #E91B89; outline-offset:3px; }
@media (prefers-reduced-motion: reduce) {
  .hsh-item { transition-duration:.01ms; }
  .hsh-list:has(.hsh-item:hover) .hsh-item:not(:hover) { filter:none; transform:none; }
}

AI PROMPT

Claude Code Cursor v0
PROMPT
:has() を使って、リストの1項目にホバーしたとき「ホバーしていない他の項目だけ」を沈ませるフォーカス演出を作って。書き方は .list:has(.item:hover) .item:not(:hover) { opacity: .28; filter: blur(1.5px); transform: scale(.97) } の1ルール。:has() は「その条件を満たす子孫を持つ親」を選ぶセレクタなので、これで初めて「子の状態で親(と他の子)のスタイルを変える」ことがCSSだけで書ける。ホバー中の項目自体には transform: translateX(10px) など前に出る変化を与えるとコントラストが立つ。キーボード操作でも同じ体験にするため、:hover と :focus-visible の両方の組を書くこと。トランジションは opacity・filter・transform に掛け、duration は 300〜400ms 程度が自然。注意: filter: blur() は下のレイヤーを合成し直すため、項目数が多い(数十以上の)リストでは blur を外して opacity と transform だけにする。

Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift

AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する

BOOKS · PR · AMAZON

📖 なかしまぁ先生のCSSアニメーション入門 📖 マイクロインタラクション ―UI/UXデザインの神が宿る細部 📖 Fundamentals of Web Animation with GSAP

※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

学びを止めない · AMAZONサブスク

📚 Kindle Unlimited — 技術書・デザイン書が読み放題。30日無料体験 → 🎧 Audible — 移動中に耳で学ぶオーディオブック。30日無料体験 → 📦 Amazon Prime — Prime Reading・配送特典つき。30日無料体験 → 🎵 Music Unlimited — 作業用BGMに。1億曲が聴き放題・30日無料体験 →

※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

RELATED EFFECTS

↗ MORE
→ UNDERLINE SLIDE HOVER → SPOTLIGHT HOVER → MAGNETIC HOVER

:has() ホバー(兄弟要素のディミング)とは

:has() は条件を満たす子孫を持つ要素を選択できる、いわゆる親セレクタです。これにより「どれか1つがホバーされている状態のリスト」を選び、その中のホバーされていない項目だけを沈ませる、という表現がCSSだけで書けるようになりました。ナビゲーション・作品一覧・料金プランの比較など、選択肢が並ぶUIで注目を集中させる定番の手法です。

当サイトの Spotlight Hover はホバーした要素自身に光を当てる表現、Magnetic Hover は要素がカーソルに引き寄せられる表現です。本ページは「他を暗くすることで相対的に浮かび上がらせる」別テーマで、対象が単体ではなくリスト全体である点が異なります。

filter: blur() は合成コストが高いため、項目数の多いリストでは opacity と transform だけに絞るのが実務的です。

よくある質問

:has() は何ができるセレクタですか?

「指定した条件に当てはまる子孫を持つ要素」を選べるセレクタで、いわゆる親セレクタです。従来は子の状態(ホバー・チェック済み・エラー)に応じて親のスタイルを変えるにはJavaScriptが必要でしたが、:has() によってCSSだけで書けるようになりました。フォームのバリデーション表示や、画像を含むカードだけレイアウトを変える、といった用途にも使えます。

スマートフォンではどう動きますか?

タッチデバイスには継続的なホバー状態がないため、この演出は基本的に発火しません。実害はありませんが、タッチでも同じ強調を出したい場合は :focus-within や :active を条件に加えるか、@media (hover: hover) でホバー環境に限定した装飾として扱うのが安全です。

コードは無料で使えますか?

はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。