DEMO
CODE
<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>
.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
: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
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
学びを止めない · AMAZONサブスク
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
: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プロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。