自分だけのブログを始めませんか?
WordPressブログなら、あなたのコーディング知識でデザインのカスタマイズが自由自在。
ブログデビューに最適な初心者向けサーバー環境を徹底比較しました。

WordPressブログなら、あなたのコーディング知識でデザインのカスタマイズが自由自在。
ブログデビューに最適な初心者向けサーバー環境を徹底比較しました。
CSSの:hover疑似クラスは、ユーザーのアクションに対して視覚的なフィードバックを返すWebデザインの基本機能です。
本記事では、滑らかなアニメーション(transition)の演出や子要素・隣の要素への連動、スマホでのホバー残り現象を防ぐモダンな無効化対策まで解説します。
Webサイトを閲覧している時、ボタンやリンクの上にマウスを乗せると色が変わったり、少し浮き上がったりする動きを見たことがあるでしょう。
これを実現するのがhover(ホバーエフェクト)です。
hoverとは、ユーザーが特定の要素にマウスカーソルを乗せている状態を指す疑似クラスのことです。
実装において最も使われる機能の一つであり、意味や使い方をマスターすることは、直感的なUI(ユーザーインターフェース)を作る上で欠かせません。
ここでは、hoverの書き方や基本、hoverの実例、実務で直面する「効かない」「崩れる」といったトラブルの解決法まで解説します。
:activeや:focusとの違いと優先順位hoverの基本となるのが、.class名:hoverという書き方です。
これにより、要素にマウスが乗った時だけ適用される専用のスタイルを定義できます。
その際、見た目の変化とともに重要なのが「マウスカーソルの形」です。
ブラウザ標準のリンク(<a>タグ)やボタン(<button>タグ)は、ホバーすると自動的にカーソルが「指のマーク」に変わりますが、ただの<div>や<span>にホバーエフェクトを付けただけではカーソルは矢印のままです。
ホバーエフェクトを実装するには、「リンクやボタンなど、ユーザーに『ここはクリックできる』と伝えたい要素に対しては、ホバー時のスタイル変化とともに、cursor: pointer;を指定して、カーソルを指のマークに変更すること」です。
⭕️ ホバーしてみよう!クリックできる要素には必ず cursor: pointer; を付けろ!
❌ 罠(矢印のまま)
※色は変わりますが、カーソルが矢印のままなので
押せると直感的に分かりません。
⭕️ 成功(指マークになる)
※色が変わると同時にカーソルが指になり、
クリック可能であることが明確に伝わります。
<div class="hover-cursor-wrapper">
<p class="hover-caption">⭕️ ホバーしてみよう!クリックできる要素には必ず cursor: pointer; を付けろ!</p>
<div class="hover-demo-area">
<div class="hover-box is-trap-cursor">
<p class="demo-title">❌ 罠(矢印のまま)</p>
<div class="btn-fail">
ホバーで色は変わるが…
</div>
<p class="demo-desc">※色は変わりますが、カーソルが矢印のままなので<br>押せると直感的に分かりません。</p>
</div>
<div class="hover-box is-success-cursor">
<p class="demo-title">⭕️ 成功(指マークになる)</p>
<div class="btn-success">
ホバーで指マークになる
</div>
<p class="demo-desc">※色が変わると同時にカーソルが指になり、<br>クリック可能であることが明確に伝わります。</p>
</div>
</div>
<div class="hover-code-area">
<span class="hl-comment">/* ❌ 罠:ホバーで色だけを変えている */</span><br>
<span class="hl-blue">.btn-fail:hover</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#0b5ed7;</span><br>
<span class="hl-comment">/* 🚨 cursorの指定が漏れているため矢印のまま */</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ クリック可能な要素には cursor: pointer; を付ける! */</span><br>
<span class="hl-blue">.btn-success:hover</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#146c43;</span><br>
<span class="hl-green">cursor:</span> <span class="hl-red">pointer;</span> <span class="hl-comment">/* 💡 これが指マークにする魔法のプロパティ */</span><br>
}
</div>
</div>.hover-cursor-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.hover-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
.hover-demo-area {
display: flex;
flex-wrap: wrap;
gap: 30px;
background-color: #e9ecef;
padding: 30px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 20px;
justify-content: center;
}
.hover-box {
width: 100%;
max-width: 280px;
background-color: #ffffff;
padding: 20px;
border: 1px solid #ced4da;
border-radius: 4px;
text-align: center;
}
.demo-title {
margin-top: 0;
margin-bottom: 15px;
font-size: 13px;
font-weight: bold;
}
.is-trap-cursor .demo-title { color: #dc3545; }
.is-success-cursor .demo-title { color: #198754; }
.demo-desc {
margin-top: 15px;
margin-bottom: 0;
font-size: 11px;
color: #6c757d;
line-height: 1.5;
}
/* === ❌ 罠:cursor指定なし === */
.btn-fail {
background-color: #0d6efd;
color: #ffffff;
padding: 15px;
border-radius: 4px;
font-weight: bold;
transition-property: background-color;
transition-duration: 0.3s;
}
.btn-fail:hover {
background-color: #0b5ed7;
/* 🚨 cursorの指定がない */
}
/* === ⭕️ 成功:cursor指定あり === */
.btn-success {
background-color: #198754;
color: #ffffff;
padding: 15px;
border-radius: 4px;
font-weight: bold;
transition-property: background-color;
transition-duration: 0.3s;
}
.btn-success:hover {
background-color: #146c43;
/* 💡 指マークにする */
cursor: pointer;
}
/* =コード解説エリア(エディタ風)= */
.hover-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }カーソルアイコンの種類一覧あるいはcursorの使い方を詳しく知りたい人は「【CSS】cursorの種類一覧!cursor: pointer;の使い方」を一読ください。
要素の状態を表す疑似クラスには、:hover以外にもいくつか種類があります。
代表的なものが、マウスのボタンを押し込んでいる瞬間の:active、要素がフォーカス(選択)されている状態の :focus、そして既に訪問済みのリンクを示す:visitedです。
違いとして、:hoverはあくまで「乗っているだけ」、:activeは「クリックしている最中」、:focusは「キーボードのTabキーなどで選択された状態」という役割分担があります。
リンクやボタンの疑似クラスを書く際は、『LVHAの法則』と呼ばれる順番で記述することです。
つまり、:link(未訪問)→:visited(訪問済み)→:hover(ホバー時)→:active(クリック時)の順に上から下に書くことで、CSSの詳細度の衝突を防ぎ、意図した通りのエフェクトを確実に発動させることができます。
⭕️ ホバーして、さらに「長押し(クリック)」してみよう!
<div class="state-wrapper">
<p class="hover-caption">⭕️ ホバーして、さらに「長押し(クリック)」してみよう!</p>
<div class="hover-demo-area">
<div class="state-demo-box">
<p class="demo-title">💡 hover と active の連続コンボ</p>
<a href="#" class="lvha-btn">クリックして長押し!</a>
<p class="demo-desc">※ホバーすると少し浮き上がり、<br>クリックして押し込んでいる間(:active)は<br>色が沈んで凹んだように見えます。</p>
</div>
</div>
<div class="hover-code-area">
<span class="hl-comment">/* ⭕️ 記述順序は必ず「L → V → H → A」を守る! */</span><br><br>
<span class="hl-comment">/* 1. L: :link(未訪問)& V: :visited(訪問済み)の基本スタイル */</span><br>
<span class="hl-blue">.lvha-btn:link, .lvha-btn:visited</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#6f42c1;</span><br>
<span class="hl-green">color:</span> <span class="hl-red">#ffffff;</span><br>
<span class="hl-green">text-decoration:</span> <span class="hl-red">none;</span><br>
}<br><br>
<span class="hl-comment">/* 2. H: :hover(マウスが乗った時) */</span><br>
<span class="hl-blue">.lvha-btn:hover</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#5936a2;</span> <span class="hl-comment">/* 💡 少し濃くする */</span><br>
<span class="hl-green">transform:</span> <span class="hl-red">translateY(-2px);</span> <span class="hl-comment">/* 💡 少し浮き上がらせる */</span><br>
}<br><br>
<span class="hl-comment">/* 3. A: :active(クリックして押し込んでいる瞬間) */</span><br>
<span class="hl-blue">.lvha-btn:active</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#43287a;</span> <span class="hl-comment">/* 💡 さらに濃くする */</span><br>
<span class="hl-green">transform:</span> <span class="hl-red">translateY(1px);</span> <span class="hl-comment">/* 💡 押し込んだように凹ませる */</span><br>
}
</div>
</div>.state-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.hover-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
/* === 💡 デモエリアを中央寄せにし、下部に余白を作る === */
.hover-demo-area {
display: flex;
justify-content: center; /* 左右の中央に寄せる */
background-color: #e9ecef;
padding: 40px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 40px; /* コードエリアとの距離をしっかり取る */
}
.state-demo-box {
background-color: #ffffff;
padding: 30px 20px;
border: 1px solid #ced4da;
border-radius: 4px;
text-align: center;
width: 100%;
max-width: 350px;
}
.demo-title {
margin-top: 0;
margin-bottom: 20px;
font-size: 14px;
font-weight: bold;
color: #0d6efd;
}
.demo-desc {
margin-top: 20px;
margin-bottom: 0;
font-size: 12px;
color: #6c757d;
line-height: 1.6;
}
/* === 💡 複数の状態を持つボタン(LVHA) === */
.lvha-btn {
display: inline-block;
padding: 15px 30px;
border-radius: 6px;
font-weight: bold;
font-size: 16px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition-property: background-color, transform, box-shadow;
transition-duration: 0.2s;
transition-timing-function: ease;
}
/* L & V */
.lvha-btn:link,
.lvha-btn:visited {
background-color: #6f42c1;
color: #ffffff;
text-decoration: none;
}
/* H(ホバー) */
.lvha-btn:hover {
background-color: #5936a2;
transform: translateY(-2px);
box-shadow: 0 6px 10px rgba(0,0,0,0.15);
cursor: pointer;
}
/* A(アクティブ:クリックした瞬間) */
.lvha-btn:active {
background-color: #43287a;
transform: translateY(1px);
box-shadow: 0 2px 3px rgba(0,0,0,0.1);
}
/* =コード解説エリア(エディタ風)= */
.hover-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue {
color: #61afef;
font-weight: bold;
}
.hl-red {
color: #e06c75;
font-weight: bold;
}
.hl-green {
color: #98c379;
font-weight: bold;
}
.hl-comment {
color: #6c757d;
font-style: italic;
}:hoverを使ったデザイン変更で一般的なのが、文字色や背景色の変更です。
ホバー時のカラーを操作・変更を起こすことで、ユーザーにリアクションを返すことができます。
また、要素の枠線を追加したり色を変えたりするテクニックも使われます。
また、ホバー時に文字色が変わらないというトラブルの多くは、親要素(例:liタグ)に:hoverを指定して色を変えようとしているのに、子要素(例:aタグ)に直接文字色が指定されていて、CSSの詳細度で負けていることが原因です。
例えば、ホバー時のレイアウト崩れを防ぐには、ホバー時にborderを追加したい場合、通常状態の時からborder: 2px solid transparent;(透明な枠線)を予め仕込んでおき、場所取りをしておくことです。
これにより、ホバー時に色だけを不透明に変更する形になり、ガタつきを100%防ぐことができます。
⭕️ ホバーして比較!「透明なborder」を仕込んでガタつきを無くせ!
❌ 罠(ガタつく)
※枠線の「太さ」の分だけ要素が大きくなり、
レイアウトがガタッと崩れます。
⭕️ 成功(崩れない)
※最初から透明な枠線が存在しているため、
色が変わるだけでガタつきません。
<div class="border-hover-wrapper">
<p class="hover-caption">⭕️ ホバーして比較!「透明なborder」を仕込んでガタつきを無くせ!</p>
<div class="hover-demo-area">
<div class="hover-box is-trap-border">
<p class="demo-title">❌ 罠(ガタつく)</p>
<div class="border-fail">ホバーで枠線が出ます</div>
<p class="demo-desc">※枠線の「太さ」の分だけ要素が大きくなり、<br>レイアウトがガタッと崩れます。</p>
</div>
<div class="hover-box is-success-border">
<p class="demo-title">⭕️ 成功(崩れない)</p>
<div class="border-success">ホバーで枠線が出ます</div>
<p class="demo-desc">※最初から透明な枠線が存在しているため、<br>色が変わるだけでガタつきません。</p>
</div>
</div>
<div class="hover-code-area">
<span class="hl-comment">/* ❌ 罠:ホバーした瞬間に物理的な「太さ」が発生してレイアウトを押し出す */</span><br>
<span class="hl-blue">.border-fail:hover</span> {<br>
<span class="hl-green">border:</span> <span class="hl-red">2px solid #dc3545;</span> <span class="hl-comment">/* 🚨 突然2px大きくなる! */</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ 通常時に「透明な枠線」で見えない場所取りをしておく! */</span><br>
<span class="hl-blue">.border-success</span> {<br>
<span class="hl-green">border:</span> <span class="hl-red">2px solid transparent;</span> <span class="hl-comment">/* 💡 ここが最大のポイント(透明で場所を確保) */</span><br>
}<br>
<span class="hl-blue">.border-success:hover</span> {<br>
<span class="hl-green">border-color:</span> <span class="hl-red">#198754;</span> <span class="hl-comment">/* 💡 ホバー時は「色」を変えるだけ! */</span><br>
}
</div>
</div>.border-hover-wrapper { background-color: #f8f9fa; padding: 30px; border-radius: 8px; border: 1px solid #dee2e6; margin-bottom: 30px; }
/* === ❌ 罠:いきなりborder追加 === */
.border-fail {
background-color: #f1f3f5;
color: #333;
padding: 15px;
font-weight: bold;
cursor: pointer;
text-align: center;
/* 🚨 通常時はborderがない */
}
.border-fail:hover {
/* 🚨 ホバー時に突然太さが発生し、文字位置などがガタつく */
border-style: solid;
border-width: 2px;
border-color: #dc3545;
background-color: #f8d7da;
}
/* === ⭕️ 成功:透明borderの場所取り === */
.border-success {
background-color: #f1f3f5;
color: #333;
padding: 15px;
font-weight: bold;
cursor: pointer;
text-align: center;
/* 💡 あらかじめ透明な2pxの枠線を作っておく */
border-style: solid;
border-width: 2px;
border-color: transparent;
}
.border-success:hover {
/* 💡 ホバー時は「色」を変えるだけで済むため、ピクセル単位のズレが起きない */
border-color: #198754;
background-color: #d1e7dd;
}
.hover-caption,
.demo-title,
.demo-desc {
text-align: center;
}
/* =コード解説エリア(エディタ風)= */
.hover-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue {
color: #61afef;
font-weight: bold;
}
.hl-red {
color: #e06c75;
font-weight: bold;
}
.hl-green {
color: #98c379;
font-weight: bold;
}
.hl-comment {
color: #6c757d;
font-style: italic;
}枠線を設定するborderについて詳しく知りたい人は「【CSS】borderの使い方まとめ!枠線の種類・角丸・太さの指定方法」を一読ください。
WebサイトのUIにおいて、マウスを乗せた瞬間にパチッと切り替わる動きは、時としてユーザーに機械的で冷たい印象を与えてしまいます。
そこで活躍するのが、状態の変化を滑らかに繋ぐホバーアニメーションです。
現代のWebデザインでは、単に色を変えるだけでなく、ホバーエフェクトとして要素を拡大させたり、浮き上がらせたりする演出が標準となっています。
ここでは、高度で滑らかな表現を可能にするtransitionの使い方、アニメーションの基本、実務で頻発するレイアウト崩れを防ぐアニメーションまで解説します。
transitionで色や変化をゆっくりさせる基本scale・zoomを使った画像やボタンの拡大(ズーム)エフェクトtranslate・rotateを使った浮き上がる・揺れる・回転する動きボタンやリンクの背景色・文字色を切り替える際、ゆっくり変化させるために不可欠なのがtransitionプロパティです。
transitionを指定することで、変化にかかる時間や変化の進行速度のカーブを自由にコントロールできます。
滑らかなホバーアニメーションは、transitionプロパティは:hover側ではなく『通常時(ベースとなる要素自体)』に記述することです。
これにより、行き(マウスを乗せた時)も帰り(マウスを外した時)も同じように滑らかなアニメーションが適用されます。
⭕️ ホバーして、そして「マウスを外して」比較しろ!transitionは通常時に書くのが鉄則!
❌ 罠(帰りが一瞬)
※乗せる時はふわっとしますが、
外した瞬間にパッと戻ってしまいます。
⭕️ 成功(帰りも滑らか)
※ベース要素に指定しているため、
外した時も設定された時間をかけて戻ります。
<div class="trans-basic-wrapper">
<p class="trans-caption">⭕️ ホバーして、そして「マウスを外して」比較しろ!transitionは通常時に書くのが鉄則!</p>
<div class="trans-demo-area">
<div class="trans-box">
<p class="demo-title is-error">❌ 罠(帰りが一瞬)</p>
<div class="btn-fail">
マウスを外すと…?
</div>
<p class="demo-desc">※乗せる時はふわっとしますが、<br>外した瞬間にパッと戻ってしまいます。</p>
</div>
<div class="trans-box">
<p class="demo-title is-success">⭕️ 成功(帰りも滑らか)</p>
<div class="btn-success">
行きも帰りも滑らか
</div>
<p class="demo-desc">※ベース要素に指定しているため、<br>外した時も設定された時間をかけて戻ります。</p>
</div>
</div>
<div class="trans-code-area">
<span class="hl-comment">/* ❌ 罠::hoverの中に書いてしまう(行きしか効かない) */</span><br>
<span class="hl-blue">.btn-fail</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#6c757d;</span><br>
}<br>
<span class="hl-blue">.btn-fail:hover</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#dc3545;</span><br>
<span class="hl-green">transition:</span> <span class="hl-red">background-color 0.5s ease;</span> <span class="hl-comment">/* 🚨 マウスを外すとこの設定が消滅するため一瞬で戻る */</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ 通常時(ベース)に書く!(行きも帰りも効く) */</span><br>
<span class="hl-blue">.btn-success</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#6c757d;</span><br>
<span class="hl-green">transition-property:</span> <span class="hl-red">background-color;</span> <span class="hl-comment">/* 💡 アニメーションさせるプロパティ */</span><br>
<span class="hl-green">transition-duration:</span> <span class="hl-red">0.5s;</span> <span class="hl-comment">/* 💡 かける時間 */</span><br>
<span class="hl-green">transition-timing-function:</span> <span class="hl-red">ease-in-out;</span> <span class="hl-comment">/* 💡 変化のカーブ */</span><br>
}<br>
<span class="hl-blue">.btn-success:hover</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#198754;</span> <span class="hl-comment">/* 💡 ホバー時は「変化後の値」だけを書く */</span><br>
}
</div>
</div>.trans-basic-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.trans-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
.trans-demo-area {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 30px;
background-color: #e9ecef;
padding: 40px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 40px;
}
.trans-box {
background-color: #ffffff;
padding: 20px;
border: 1px solid #ced4da;
border-radius: 4px;
text-align: center;
width: 100%;
max-width: 250px;
}
.demo-title {
margin-top: 0;
margin-bottom: 20px;
font-size: 14px;
font-weight: bold;
}
.is-error {
color: #dc3545;
}
.is-success {
color: #198754;
}
.demo-desc {
margin-top: 20px;
margin-bottom: 0;
font-size: 11px;
color: #6c757d;
line-height: 1.6;
}
/* === ❌ 罠::hover側にtransition === */
.btn-fail {
background-color: #6c757d;
color: #ffffff;
padding: 15px;
border-radius: 6px;
font-weight: bold;
cursor: pointer;
}
.btn-fail:hover {
background-color: #dc3545;
transition-property: background-color;
transition-duration: 0.5s;
transition-timing-function: ease;
}
/* === ⭕️ 成功:ベース側にtransition === */
.btn-success {
background-color: #6c757d;
color: #ffffff;
padding: 15px;
border-radius: 6px;
font-weight: bold;
cursor: pointer;
/* 💡 鉄則:ここに書く */
transition-property: background-color;
transition-duration: 0.5s;
transition-timing-function: ease-in-out;
}
.btn-success:hover {
background-color: #198754;
}
/* =コード解説エリア(エディタ風)= */
.trans-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }変化時間を指定するtransitionの使い方を詳しく知りたい人は「【CSS】transitionの書き方!transformとの違いやコピペ用コード集」を一読ください。
カード型の記事リンクなどで、画像にホバーした際に少しだけ拡大させるエフェクトは、クリックできることを直感的に伝える定番の手法です。
例えば、ホバーで画像拡大を実装する際、実務で使うべきプロパティはtransform: scale()一択です。
※zoomプロパティは非標準でありブラウザ間で挙動が異なるため、実務では使用しません。
背景画像の場合はbackground-sizeをアニメーションさせる手法もありますが、パフォーマンス面で不利になります。
画像のズームエフェクトは、拡大はwidthではなくtransform: scale()で行うことです。
そして、拡大しても枠からはみ出さないよう、画像を囲む『親要素(ラッパー)』に対してoverflow: hidden;を指定し、額縁の中で絵だけが大きくなるような構造を作ることです。
⭕️ 画像を拡大させるなら、親要素に「overflow: hidden」をかけて枠からはみ出させるな!
❌ 罠(枠を突き破る)
※画像が拡大された分だけ、
親の枠線を超えて外にはみ出します。
⭕️ 成功(枠に収まる)
※親に overflow: hidden があるため、
枠の中で美しくズームされます。
<div class="scale-basic-wrapper">
<p class="trans-caption">⭕️ 画像を拡大させるなら、親要素に「overflow: hidden」をかけて枠からはみ出させるな!</p>
<div class="trans-demo-area">
<div class="scale-box">
<p class="demo-title is-error">❌ 罠(枠を突き破る)</p>
<div class="img-wrapper-fail">
<div class="dummy-img">画像</div>
</div>
<p class="demo-desc">※画像が拡大された分だけ、<br>親の枠線を超えて外にはみ出します。</p>
</div>
<div class="scale-box">
<p class="demo-title is-success">⭕️ 成功(枠に収まる)</p>
<div class="img-wrapper-success">
<div class="dummy-img">画像</div>
</div>
<p class="demo-desc">※親に overflow: hidden があるため、<br>枠の中で美しくズームされます。</p>
</div>
</div>
<div class="trans-code-area">
<span class="hl-comment">/* ❌ 罠:親要素に制御がない */</span><br>
<span class="hl-blue">.wrapper-fail</span> {<br>
<span class="hl-green">border:</span> <span class="hl-red">2px solid #ccc;</span><br>
<span class="hl-comment">/* 🚨 はみ出しを隠す設定がない! */</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ 親要素で「額縁」を作る */</span><br>
<span class="hl-blue">.wrapper-success</span> {<br>
<span class="hl-green">border:</span> <span class="hl-red">2px solid #ccc;</span><br>
<span class="hl-green">overflow:</span> <span class="hl-red">hidden;</span> <span class="hl-comment">/* 💡 必須:枠からはみ出た部分を切り取る */</span><br>
<span class="hl-green">border-radius:</span> <span class="hl-red">8px;</span> <span class="hl-comment">/* 角丸も一緒に設定すると綺麗です */</span><br>
}<br><br>
<span class="hl-comment">/* 💡 子要素(画像)の拡大設定 */</span><br>
<span class="hl-blue">.dummy-img</span> {<br>
<span class="hl-green">transition-property:</span> <span class="hl-red">transform;</span><br>
<span class="hl-green">transition-duration:</span> <span class="hl-red">0.4s;</span><br>
<span class="hl-green">transition-timing-function:</span> <span class="hl-red">ease;</span><br>
}<br>
<span class="hl-blue">.wrapper-success:hover .dummy-img</span> {<br>
<span class="hl-green">transform:</span> <span class="hl-red">scale(1.15);</span> <span class="hl-comment">/* 💡 2倍(200%)に拡大 */</span><br>
}
</div>
</div>.scale-basic-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.trans-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
/* === 💡 横並びにし、下のコードエリアとの余白を作る === */
.trans-demo-area {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 30px;
background-color: #e9ecef;
padding: 40px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 40px; /* 💡 コードエリアとの間にしっかり余白を取る */
}
.scale-box {
background-color: #ffffff;
padding: 20px;
border: 1px solid #ced4da;
border-radius: 4px;
text-align: center;
width: 100%;
max-width: 250px;
}
.demo-title {
margin-top: 0;
margin-bottom: 20px;
font-size: 14px;
font-weight: bold;
}
.is-error {
color: #dc3545;
}
.is-success {
color: #198754;
}
.demo-desc {
margin-top: 20px;
margin-bottom: 0;
font-size: 11px;
color: #6c757d;
line-height: 1.6;
}
/* === ❌ 罠:はみ出すラッパー === */
.img-wrapper-fail {
border-style: solid;
border-width: 2px;
border-color: #dc3545;
width: 100%;
height: 120px;
cursor: pointer;
/* 🚨 overflow: hidden; がない */
}
/* === ⭕️ 成功:収まるラッパー === */
.img-wrapper-success {
border-style: solid;
border-width: 2px;
border-color: #198754;
width: 100%;
height: 120px;
cursor: pointer;
/* 💡 枠からはみ出た部分を隠す */
overflow: hidden;
border-radius: 6px;
}
/* === 画像本体の共通スタイル === */
.dummy-img {
background-color: #0d6efd;
color: #ffffff;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
transition-property: transform;
transition-duration: 0.4s;
transition-timing-function: ease;
}
/* ホバー時の拡大 */
.img-wrapper-fail:hover .dummy-img,
.img-wrapper-success:hover .dummy-img {
transform: scale(2); /* 15%拡大 */
}
/* === 💡 エディタ風コードエリアのCSSを追加 === */
.trans-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }transform・overflowの使い方を詳しく知りたい人は以下から一読ください。
ボタンがふわりと少しだけ上に動く表現や注目させたい要素が小さく揺れる、またはアイコンがくるっと回る表現も、transformプロパティを使えば滑らかに実装できます。
Y軸(縦方向)の移動にはtranslateY()、回転にはrotate()を使用します。
複数の変化を組み合わせるには、transformプロパティは宣言するたびに以前の設定を上書きしてしまうため、ホバー時にも『通常時の状態』を含めて数珠繋ぎで記述することです。(例:transform: rotate(10deg) translateY(-10px);)
もしくは、モダンブラウザであれば独立したプロパティであるtranslate, rotate, scaleを個別に指定して上書きを防ぐことです。
⭕️ ホバーして浮き上がらせろ!複数の動きを混ぜる時は「上書き」に注意!
💡 浮き上がる(translate)
※Y軸(縦方向)にマイナスの値を
指定すると上へ移動します。
💡 回転+浮上(複数指定)
※ホバー時にも rotate を記述しないと
傾きがリセットされてしまいます。
<div class="transform-basic-wrapper">
<p class="trans-caption">⭕️ ホバーして浮き上がらせろ!複数の動きを混ぜる時は「上書き」に注意!</p>
<div class="trans-demo-area">
<div class="scale-box">
<p class="demo-title is-success">💡 浮き上がる(translate)</p>
<div class="float-btn">
ホバーで浮くボタン
</div>
<p class="demo-desc">※Y軸(縦方向)にマイナスの値を<br>指定すると上へ移動します。</p>
</div>
<div class="scale-box">
<p class="demo-title is-success">💡 回転+浮上(複数指定)</p>
<div class="combo-btn">
傾いたボタン
</div>
<p class="demo-desc">※ホバー時にも rotate を記述しないと<br>傾きがリセットされてしまいます。</p>
</div>
</div>
<div class="trans-code-area">
<span class="hl-comment">/* 💡 浮き上がるボタン(Y軸の移動) */</span><br>
<span class="hl-blue">.btn-float</span> {<br>
<span class="hl-green">transition-property:</span> <span class="hl-red">transform, box-shadow;</span><br>
<span class="hl-green">transition-duration:</span> <span class="hl-red">0.3s;</span><br>
}<br>
<span class="hl-blue">.btn-float:hover</span> {<br>
<span class="hl-green">transform:</span> <span class="hl-red">translateY(-5px);</span> <span class="hl-comment">/* 💡 上に5px移動させる */</span><br>
<span class="hl-green">box-shadow:</span> <span class="hl-red">0 8px 15px rgba(0,0,0,0.2);</span> <span class="hl-comment">/* 影を濃くするとより浮いたように見えます */</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ 複数のtransformを組み合わせる場合 */</span><br>
<span class="hl-blue">.btn-combo</span> {<br>
<span class="hl-green">transform:</span> <span class="hl-red">rotate(-5deg);</span> <span class="hl-comment">/* 通常時から少し傾けておく */</span><br>
<span class="hl-green">transition-property:</span> <span class="hl-red">transform;</span><br>
<span class="hl-green">transition-duration:</span> <span class="hl-red">0.3s;</span><br>
}<br>
<span class="hl-blue">.btn-combo:hover</span> {<br>
<span class="hl-comment">/* 🚨 ここで translateY(-5px) だけ書くと傾きが戻ってしまう! */</span><br>
<span class="hl-green">transform:</span> <span class="hl-red">rotate(-5deg) translateY(-5px);</span> <span class="hl-comment">/* 💡 元の回転を維持したまま移動させる */</span><br>
}
</div>
</div>.transform-basic-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
/* === 💡 浮き上がるボタン === */
.float-btn {
background-color: #6f42c1;
color: #fff;
padding: 15px;
border-radius: 6px;
font-weight: bold;
cursor: pointer;
margin-top: 20px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition-property: transform, box-shadow;
transition-duration: 0.3s;
transition-timing-function: ease;
}
.float-btn:hover {
/* 💡 Y軸(縦)にマイナス移動で上へ */
transform: translateY(-6px);
/* 影を濃く・広げて浮遊感を強調 */
box-shadow: 0 10px 20px rgba(0,0,0,0.15);
}
/* === 💡 回転+浮上ボタン === */
.combo-btn {
background-color: #fd7e14;
color: #fff;
padding: 15px;
border-radius: 6px;
font-weight: bold;
cursor: pointer;
margin-top: 20px;
/* 通常時から傾けておく */
transform: rotate(-5deg);
transition-property: transform;
transition-duration: 0.3s;
transition-timing-function: ease;
}
.combo-btn:hover {
/* 💡 回転情報を残したまま移動させる */
transform: rotate(-5deg) translateY(-6px);
}
/* === 💡 エディタ風コードエリアのCSSを追加 === */
.trans-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }移動に利用するtransform(translate)、回転に利用するrotateの使い方を詳しく知りたい人は以下から一読ください。
グローバルナビゲーション(メニュー)のリンクなどで、ホバーした際に文字の下にスッと下線が伸びてくるエフェクトは、スタイリッシュなサイトで人気があります。
下線がスッと伸びるアニメーションは、標準の下線を消し、疑似要素(::after)を使って『絶対配置された1本の線』を自作することです。
そして、その線の幅をtransform: scaleX(0);(見えない状態)から scaleX(1);(100%の長さ)へアニメーションさせることで、レイアウトに一切影響を与えずに伸び縮みを表現できます。
⭕️ ホバーして確認!下線や枠線が伸びるアニメは「疑似要素」と「scale」で作れ!
💡 下線(scaleX)
スッと下線が伸びるリンク💡 枠線(scaleX & scaleY)
スッと枠線が囲むボタン<div class="underline-basic-wrapper">
<p class="trans-caption">⭕️ ホバーして確認!下線や枠線が伸びるアニメは「疑似要素」と「scale」で作れ!</p>
<div class="trans-demo-area">
<div class="scale-box">
<p class="demo-title">💡 下線(scaleX)</p>
<a href="#" class="cool-underline-link">
スッと下線が伸びるリンク
</a>
</div>
<div class="scale-box">
<p class="demo-title">💡 枠線(scaleX & scaleY)</p>
<a href="#" class="cool-border-btn">
スッと枠線が囲むボタン
</a>
</div>
</div>
<div class="trans-code-area">
<span class="hl-comment">/* === 💡 1. 下線が伸びるアニメーション === */</span><br>
<span class="hl-blue">.cool-underline-link</span> {<br>
<span class="hl-green">position:</span> <span class="hl-red">relative;</span><br>
<span class="hl-green">text-decoration-line:</span> <span class="hl-red">none;</span><br>
<span class="hl-green">color:</span> <span class="hl-red">#333;</span><br>
}<br>
<span class="hl-blue">.cool-underline-link::after</span> {<br>
<span class="hl-green">content:</span> <span class="hl-red">"";</span><br>
<span class="hl-green">position:</span> <span class="hl-red">absolute;</span><br>
<span class="hl-green">bottom:</span> <span class="hl-red">-5px;</span><br>
<span class="hl-green">left:</span> <span class="hl-red">0;</span><br>
<span class="hl-green">width:</span> <span class="hl-red">100%;</span><br>
<span class="hl-green">height:</span> <span class="hl-red">2px;</span><br>
<span class="hl-green">background-color:</span> <span class="hl-red">#0d6efd;</span><br>
<span class="hl-green">transform:</span> <span class="hl-red">scaleX(0);</span><br>
<span class="hl-green">transform-origin:</span> <span class="hl-red">left top;</span><br>
<span class="hl-green">transition-property:</span> <span class="hl-red">transform;</span><br>
<span class="hl-green">transition-duration:</span> <span class="hl-red">0.3s;</span><br>
<span class="hl-green">transition-timing-function:</span> <span class="hl-red">ease;</span><br>
}<br>
<span class="hl-blue">.cool-underline-link:hover::after</span> {<br>
<span class="hl-green">transform:</span> <span class="hl-red">scaleX(1);</span><br>
}<br><br>
<span class="hl-comment">/* === 💡 2. 枠線が伸びるアニメーション === */</span><br>
<span class="hl-blue">.cool-border-btn</span> {<br>
<span class="hl-green">position:</span> <span class="hl-red">relative;</span><br>
<span class="hl-green">padding:</span> <span class="hl-red">15px 30px;</span><br>
<span class="hl-green">text-decoration-line:</span> <span class="hl-red">none;</span><br>
<span class="hl-green">color:</span> <span class="hl-red">#333;</span><br>
}<br>
<span class="hl-blue">.cool-border-btn::before, .cool-border-btn::after</span> {<br>
<span class="hl-green">content:</span> <span class="hl-red">"";</span><br>
<span class="hl-green">position:</span> <span class="hl-red">absolute;</span><br>
<span class="hl-green">top:</span> <span class="hl-red">0;</span><br>
<span class="hl-green">left:</span> <span class="hl-red">0;</span><br>
<span class="hl-green">width:</span> <span class="hl-red">100%;</span><br>
<span class="hl-green">height:</span> <span class="hl-red">100%;</span><br>
<span class="hl-green">box-sizing:</span> <span class="hl-red">border-box;</span><br>
<span class="hl-green">transition-property:</span> <span class="hl-red">transform;</span><br>
<span class="hl-green">transition-duration:</span> <span class="hl-red">0.4s;</span><br>
<span class="hl-green">transition-timing-function:</span> <span class="hl-red">ease-in-out;</span><br>
}\n <span class="hl-blue">.cool-border-btn::before</span> {<br>
<span class="hl-green">border-top:</span> <span class="hl-red">2px solid #0d6efd;</span><br>
<span class="hl-green">border-bottom:</span> <span class="hl-red">2px solid #0d6efd;</span><br>
<span class="hl-green">transform:</span> <span class="hl-red">scaleX(0);</span><br>
}\n <span class="hl-blue">.cool-border-btn::after</span> {<br>
<span class="hl-green">border-left:</span> <span class="hl-red">2px solid #0d6efd;</span><br>
<span class="hl-green">border-right:</span> <span class="hl-red">2px solid #0d6efd;</span><br>
<span class="hl-green">transform:</span> <span class="hl-red">scaleY(0);</span><br>
}<br>
<span class="hl-blue">.cool-border-btn:hover::before</span> {<br>
<span class="hl-green">transform:</span> <span class="hl-red">scaleX(1);</span><br>
}<br>
<span class="hl-blue">.cool-border-btn:hover::after</span> {<br>
<span class="hl-green">transform:</span> <span class="hl-red">scaleY(1);</span><br>
}
</div>
</div>.underline-basic-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
margin-bottom: 30px;
}
.trans-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
.trans-demo-area {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 30px;
background-color: #e9ecef;
padding: 40px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 40px;
}
.scale-box {
background-color: #ffffff;
padding: 40px 20px;
border: 1px solid #ced4da;
border-radius: 4px;
text-align: center;
width: 100%;
max-width: 300px;
}
.demo-title {
margin-top: 0;
margin-bottom: 30px;
font-size: 14px;
font-weight: bold;
color: #0d6efd;
}
/* === 💡 1. 下線が伸びるアニメーション === */
.cool-underline-link {
position: relative;
text-decoration-line: none;
color: #333;
font-weight: bold;
font-size: 16px;
padding-bottom: 5px;
}
.cool-underline-link::after {
content: "";
position: absolute;
left: 0;
bottom: -5px;
width: 100%;
height: 2px;
background-color: #0d6efd;
transform: scaleX(0);
transform-origin: left top;
transition-property: transform;
transition-duration: 0.3s;
transition-timing-function: ease-out;
}
.cool-underline-link:hover::after {
transform: scaleX(1);
}
/* === 💡 2. 枠線が伸びるアニメーション === */
.cool-border-btn {
position: relative;
display: inline-block;
padding-top: 15px;
padding-right: 30px;
padding-bottom: 15px;
padding-left: 30px;
text-decoration-line: none;
color: #333;
font-weight: bold;
font-size: 16px;
}
/* 上下・左右の線を疑似要素で用意 */
.cool-border-btn::before,
.cool-border-btn::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
transition-property: transform;
transition-duration: 0.4s;
transition-timing-function: ease-in-out;
}
/* 💡 上と下の線 */
.cool-border-btn::before {
border-top-style: solid;
border-top-width: 2px;
border-top-color: #0d6efd;
border-bottom-style: solid;
border-bottom-width: 2px;
border-bottom-color: #0d6efd;
transform: scaleX(0);
}
/* 💡 左と右の線 */
.cool-border-btn::after {
border-left-style: solid;
border-left-width: 2px;
border-left-color: #0d6efd;
border-right-style: solid;
border-right-width: 2px;
border-right-color: #0d6efd;
transform: scaleY(0);
}
/* 💡 実際のCSS側に枠線用のホバー処理 */
.cool-border-btn:hover::before {
transform: scaleX(1);
}
.cool-border-btn:hover::after {
transform: scaleY(1);
}
/* === 💡 エディタ風コードエリアのCSS === */
.trans-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left-style: solid;
border-left-width: 4px;
border-left-color: #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }枠線・下線・疑似クラスの使い方を詳しく知りたい人は以下から一読ください。
CSSのホバーエフェクトは、マウスを乗せた本人(要素自体)の色や形を変えるだけにとどまりません。
実務のWeb制作では、「メニューにホバーしたとき、下に隠れている子要素(ドロップダウン)を展開する」「特定のボタンにホバーしたとき、隣にある解説テキストを表示する」といった連動した動きが多いです。
このように他の要素のスタイルをコントロールするには、CSSの「結合子(セレクタ)」の関係性を正しく理解する必要があります。
ここでは、子要素・隣の要素(兄弟要素)・親要素の操作まで解説します。
:has()が必要display: none;からblockにしてツールチップやテキストを表示するホバーした要素の内部にある要素やHTML上で直後にある要素のスタイルを書き換えるのは、CSSセレクタの基本であり強力なテクニックです。
子要素を指定する場合は「子孫結合子(半角スペース)」、隣の要素を指定する場合は「次兄弟結合子(+)」または「一般兄弟結合子(~)」を使用します。
他の要素を連動させるには、結合子を使う際はHTMLの構造(DOMツリー)の並び順を100%意識することです。
また、子要素を変更したいなら親:hover 子、直後の要素なら兄:hover + 弟、それ以降の後ろの要素すべてなら兄:hover ~ 弟 のパターンを使い分けます。
動かしたいターゲット要素は、ホバーする要素の『中』か『後ろ』に配置するようHTMLを設計します。
⭕️ ホバーしてみよう!結合子を使えば、中にある要素も後ろにある要素も自由自在!
💡 子要素の制御(スペース)
※親カード全体にホバーすると、
中にある子要素のバッジの色が変わります。
💡 隣の要素の制御( + )
※HTML上で直後(隣)に並んでいる
弟要素のスタイルを切り替えます。
<div class="sibling-hover-wrapper">
<p class="sibling-caption">⭕️ ホバーしてみよう!結合子を使えば、中にある要素も後ろにある要素も自由自在!</p>
<div class="sibling-demo-area">
<div class="sibling-box hover-trigger-parent">
<p class="demo-title">💡 子要素の制御(スペース)</p>
<div class="parent-card">
ここが親カード
<div class="child-badge">子要素のバッジ</div>
</div>
<p class="demo-desc">※親カード全体にホバーすると、<br>中にある子要素のバッジの色が変わります。</p>
</div>
<div class="sibling-box">
<p class="demo-title">💡 隣の要素の制御( + )</p>
<button class="trigger-btn-sibling">ホバーする要素(兄)</button>
<div class="target-box-sibling">連動する要素(弟)</div>
<p class="demo-desc">※HTML上で直後(隣)に並んでいる<br>弟要素のスタイルを切り替えます。</p>
</div>
</div>
<div class="sibling-code-area">
<span class="hl-comment">/* === 💡 1. 子要素(ネスト)の変更 === */</span><br>
<span class="hl-blue">.parent-card:hover .child-badge</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#0d6efd;</span> <span class="hl-comment">/* 💡 親ホバー時に、中の子要素だけ色を変える */</span><br>
<span class="hl-green">color:</span> <span class="hl-red">#ffffff;</span><br>
}<br><br>
<span class="hl-comment">/* === 💡 2. 隣の要素(兄弟)の変更 === */</span><br>
<span class="hl-blue">.trigger-btn-sibling:hover + .target-box-sibling</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#198754;</span> <span class="hl-comment">/* 💡 兄ホバー時に、直後の弟要素(+)を変える */</span><br>
<span class="hl-green">color:</span> <span class="hl-red">#ffffff;</span><br>
<span class="hl-green">transform:</span> <span class="hl-red">translateX(10px);</span> <span class="hl-comment">/* 少し右に動かす */</span><br>
}<br>
<span class="hl-comment">/* 🚨 注意:HTML上で「trigger-btn」より前に書かれた要素は + では操作できません */</span>
</div>
</div>.sibling-hover-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.sibling-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
.sibling-demo-area {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 30px;
background-color: #e9ecef;
padding: 40px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 40px;
}
.sibling-box {
background-color: #ffffff;
padding: 20px;
border: 1px solid #ced4da;
border-radius: 4px;
text-align: center;
width: 100%;
max-width: 280px;
}
.demo-title {
margin-top: 0;
margin-bottom: 20px;
font-size: 14px;
font-weight: bold;
color: #333;
}
.demo-desc {
margin-top: 20px;
margin-bottom: 0;
font-size: 11px;
color: #6c757d;
line-height: 1.6;
}
/* === 💡 1. 子要素のスタイル === */
.parent-card {
background-color: #f1f3f5;
border: 1px solid #ced4da;
padding: 20px;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
}
.child-badge {
background-color: #fff;
border: 1px solid #ccc;
padding: 5px 10px;
border-radius: 20px;
margin-top: 15px;
font-size: 12px;
display: inline-block;
color: #666;
transition-property: background-color, color;
transition-duration: 0.3s;
}
/* 親ホバー時の子要素変化 */
.parent-card:hover .child-badge {
background-color: #0d6efd;
color: #ffffff;
}
/* === 💡 2. 隣の要素(兄弟)のスタイル === */
.trigger-btn-sibling {
background-color: #6c757d;
color: #fff;
border: none;
padding: 12px 15px;
border-radius: 6px;
font-weight: bold;
cursor: pointer;
width: 100%;
}
.target-box-sibling {
background-color: #fff;
border: 1px solid #ccc;
padding: 12px 15px;
border-radius: 6px;
margin-top: 15px;
font-size: 13px;
font-weight: bold;
color: #666;
transition-property: background-color, color, transform;
transition-duration: 0.3s;
}
/* 兄ホバー時の弟要素変化(+ を使用) */
.trigger-btn-sibling:hover + .target-box-sibling {
background-color: #198754;
color: #ffffff;
transform: translateX(10px);
}
/* =コード解説エリア(エディタ風)= */
.sibling-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left-style: solid;
border-left-width: 4px;
border-left-color: #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }CSSの世界では、長年にわたり「子要素の状態をトリガーにして、親要素のスタイルを変えることは不可能」とされてきました。
CSSは上から下、親から子へとスタイルが流れるのが大原則だったからです。
しかし、現代のモダンブラウザは、疑似クラスと呼ばれる:has()セレクタに対応しました。
これを使うことで、JavaScriptを使わずCSSだけで「子要素にホバーした時に、親要素のスタイルを変える」という実装が可能です。
親要素を操作するには、親:has(子:hover)という『関数型』の書き方をマスターすることです。
これは『内部にある子がホバー状態になっている、そんな親要素自身』という意味になり、親の背景色をダイナミックに変えるようなカード型UIで威力を発揮します。
望ましいのは、レガシー環境で動かないリスクを考慮し、効かなくても最低限デザインが壊れないフォールバックを想定しておくことです。
⭕️ 枠内の「リンク文字」だけにホバーしてみて!親である白い箱の色が変わるぞ!
<div class="parent-hover-wrapper">
<p class="sibling-caption">⭕️ 枠内の「リンク文字」だけにホバーしてみて!親である白い箱の色が変わるぞ!</p>
<div class="sibling-demo-area">
<div class="has-parent-card">
<p class="has-card-title">親要素(カードの枠)</p>
<a href="#" class="has-child-link">ここが子要素のリンク</a>
<p class="demo-desc">※通常は文字にホバーしても親は変わりませんが、<br>:has() を使うことで親の背景を赤に変えられます。</p>
</div>
</div>
<div class="sibling-code-area">
<span class="hl-comment">/* ⭕️ :has() を使って「子要素がホバーされた時の親」を指定する */</span><br>
<span class="hl-blue">.has-parent-card:has(.has-child-link:hover)</span> {<br>
<span class="hl-green">background-color: #f8d7da;</span> <span class="hl-comment">/* 💡 子のホバーを感知して、親の背景を赤にする */</span><br>
<span class="hl-green">border-color: #dc3545;</span> <span class="hl-comment">/* 親の枠線も赤にする */</span><br>
}<br><br>
<span class="hl-comment">/* ※この指定により、子要素であるリンクにマウスが乗った瞬間、親全体のスタイルが切り替わります */</span>
</div>
</div>.parent-hover-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
/* === 💡 :has() 親要素カードのスタイル === */
.has-parent-card {
background-color: #ffffff;
border-style: solid;
border-width: 2px;
border-color: #ced4da;
padding: 30px 20px;
border-radius: 6px;
text-align: center;
width: 100%;
max-width: 350px;
/* 変化を滑らかにする */
transition-property: background-color, border-color;
transition-duration: 0.3s;
transition-timing-function: ease;
}
.has-card-title {
margin-top: 0;
margin-bottom: 20px;
font-size: 16px;
font-weight: bold;
color: #333;
}
.has-child-link {
color: #0d6efd;
font-weight: bold;
text-decoration-line: underline;
font-size: 15px;
display: inline-block;
}
/* ⭕️ 子のホバーを条件にして、親のスタイルを変更(:has) */
.has-parent-card:has(.has-child-link:hover) {
background-color: #f8d7da;
border-color: #dc3545;
}
/* =コード解説エリア(エディタ風)= */
.sibling-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left-style: solid;
border-left-width: 4px;
border-left-color: #0d6efd;
overflow-x: auto;
text-align: left;
}
/* =コード解説エリア(エディタ風)= */
.sibling-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left-style: solid;
border-left-width: 4px;
border-left-color: #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }親要素を指定できる疑似クラス:has()の使い方を詳しく知りたい人は「【CSS】:has()疑似クラスの使い方:親・子・孫要素の指定や複数条件との組み合わせ」を一読ください。
実務のデザインにおいてよく使われるのが、「隠されているテキストや補足メッセージ」や「アイコンの上にふわっと浮き出る説明用の小さな吹き出し」、または画像の上に被さる半透明のレイヤーの実装です。
これらは、通常時をdisplay: none;で非表示にしておき、ホバーされたタイミングでblockに切り替えることで表示させます。
ツールチップやポップアップを表示させるには、①表示されるポップアップ(子要素)は、ホバーされる親要素の領域の中に隙間なく配置することです。
②ポップアップを表示する際は、display: noneからblockにするとアニメーションが効かないため、場所取りを維持して隠すvisibility: hidden;とopacity: 0;のコンボを使い、ホバー時にvisibleと1に引き上げる手法を採用します。
これによりチカチカ現象を100%防ぎ、滑らかなフェード表示が可能です。
⭕️ 「?」マークにマウスを乗せてみよう!滑らかな吹き出しが安全に出現するぞ!
<div class="tooltip-hover-wrapper">
<p class="sibling-caption">⭕️ 「?」マークにマウスを乗せてみよう!滑らかな吹き出しが安全に出現するぞ!</p>
<div class="sibling-demo-area">
<div class="tooltip-demo-box">
<span>お支払い方法について</span>
<span class="tooltip-trigger">
?
<span class="tooltip-body">
クレジットカード、コンビニ払い、<br>各種キャリア決済に対応しています。
</span>
</span>
</div>
</div>
<div class="sibling-code-area">
<span class="hl-comment">/* 💡 親要素(トリガー):ホバーの判定エリアになる */</span><br>
<span class="hl-blue">.tooltip-trigger</span> {<br>
<span class="hl-green">position:</span> <span class="hl-red">relative;</span> <span class="hl-comment">/* 💡 吹き出しの絶対配置の基準にする */</span><br>
<span class="hl-green">cursor:</span> <span class="hl-red">pointer;</span><br>
}<br><br>
<span class="hl-comment">/* ❌ 通常時は visibility と opacity で隠す(アニメーション用) */</span><br>
<span class="hl-blue">.tooltip-body</span> {<br>
<span class="hl-green">position:</span> <span class="hl-red">absolute;</span><br>
<span class="hl-green">bottom:</span> <span class="hl-red">130%;</span> <span class="hl-comment">/* 親要素の少し上に配置 */</span><br>
<span class="hl-green">left:</span> <span class="hl-red">50%;</span><br>
<span class="hl-green">transform:</span> <span class="hl-red">translateX(-50%);</span> <span class="hl-comment">/* 吹き出しの横方向中央揃え */</span><br>
<span class="hl-green">opacity:</span> <span class="hl-red">0;</span> <span class="hl-comment">/* 💡 透明にしておく */</span><br>
<span class="hl-green">visibility:</span> <span class="hl-red">hidden;</span> <span class="hl-comment">/* 💡 判定も消しておく */</span><br>
<span class="hl-green">transition:</span> <span class="hl-red">opacity 0.3s, visibility 0.3s;</span> <span class="hl-comment">/* ふわっと表示させる */</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ 親がホバーされたら、中のツールチップを表示状態にする */</span><br>
<span class="hl-blue">.tooltip-trigger:hover .tooltip-body</span> {<br>
<span class="hl-green">opacity:</span> <span class="hl-red">1;</span><br>
<span class="hl-green">visibility:</span> <span class="hl-red">visible;</span> <span class="hl-comment">/* 💡 チカチカ点滅せずに美しくフェードインする */</span><br>
}
</div>
</div>.tooltip-hover-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.tooltip-demo-area {
display: flex;
justify-content: center;
background-color: #e9ecef;
padding: 60px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 40px;
}
.tooltip-demo-box {
background-color: #ffffff;
padding: 15px 25px;
border: 1px solid #ced4da;
border-radius: 30px;
font-size: 14px;
font-weight: bold;
color: #333;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
}
/* === 💡 ツールチップトリガー(親) === */
.tooltip-trigger {
position: relative;
display: inline-flex;
justify-content: center;
align-items: center;
background-color: #0d6efd;
color: #ffffff;
width: 20px;
height: 20px;
border-radius: 50%;
font-size: 11px;
margin-left: 8px;
cursor: pointer;
}
/* === 💡 ツールチップ本体(子) === */
.tooltip-body {
position: absolute;
bottom: 140%; /* 少し上に浮かせる */
left: 50%;
transform: translateX(-50%);
background-color: #212529;
color: #ffffff;
padding: 10px 15px;
border-radius: 6px;
font-size: 12px;
line-height: 1.5;
width: 220px;
text-align: left;
font-weight: normal;
box-shadow: 0 8px 20px rgba(0,0,0,0.15);
/* 💡 点滅バグを防ぐため、親要素との間に透明な隙間埋め(padding)を敷く */
padding-bottom: 15px;
/* 💡 通常時はアニメーションのために非表示(隠す) */
opacity: 0;
visibility: hidden;
transition-property: opacity, visibility;
transition-duration: 0.3s;
transition-timing-function: ease;
z-index: 10;
}
/* 💡 吹き出しのチョボ(矢印)を疑似要素で作る */
.tooltip-body::after {
content: "";
position: absolute;
bottom: 5px; /* 下部paddingの内側に配置 */
left: 50%;
transform: translateX(-50%);
border-style: solid;
border-width: 6px 6px 0 6px;
border-color: #212529 transparent transparent transparent;
}
/* ⭕️ ホバー時に連動して表示状態を1に戻す */
.tooltip-trigger:hover .tooltip-body {
opacity: 1;
visibility: visible;
}
/* =コード解説エリア(エディタ風)= */
.sibling-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left-style: solid;
border-left-width: 4px;
border-left-color: #0d6efd;
overflow-x: auto;
text-align: left;
}
/* =コード解説エリア(エディタ風)= */
.sibling-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left-style: solid;
border-left-width: 4px;
border-left-color: #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }要素の性質を決めるdisplayの使い方を詳しく知りたい人は「【CSS】display: blockとは?inlineやflexとの違いと種類」を一読ください。
ここからは、実務のWeb制作でそのまま使える具体的なコンポーネント別のホバーデザイン集をお届けします。
ネット上には便利なライブラリや自動でコードを生成するホバーエフェクトジェネレーターもありますが、仕組みを理解せずにコピペすると、カスタマイズやバグ修正ができなくなります。
ボタン、画像、メニュー、疑似要素を駆使したスタイルまで、構造からマスターしていきましょう。
お問い合わせフォームの送信ボタンやCVボタンにおいて、ホバー時にボタンがグッとへこむような動きやネオンのようにふわっと光るエフェクトは、クリック率に直結する重要な演出です。
これらはtransform: translateY()とbox-shadowを連動させることで実現します。
影や光をデザインするには、box-shadowの色で不透明度をコントロールできるrgba()を使用することです。(例:黒い影ならrgba(0, 0, 0, 0.15))
光らせるエフェクトを作りたい場合は、ボタンのメインカラー(例:青)の同系色をrgba(13, 110, 253, 0.5)のように薄く指定し、ぼかし幅を大きめにとって外側にレイヤーを重ねることです。
これにより、透明感のあるきれいな輝きを表現できます。
⭕️ ホバーして確認!「へこむ動き」と「美しい光の影」でクリック率を高めろ!
💡 立体的にへこむボタン
💡 ネオン風に光るボタン
<div class="element-btn-wrapper">
<p class="element-caption">⭕️ ホバーして確認!「へこむ動き」と「美しい光の影」でクリック率を高めろ!</p>
<div class="element-demo-area">
<div class="element-box">
<p class="demo-title">💡 立体的にへこむボタン</p>
<button class="btn-demo-press">
送信する (Press)
</button>
</div>
<div class="element-box">
<p class="demo-title">💡 ネオン風に光るボタン</p>
<button class="btn-demo-glow">
詳しく見る (Glow)
</button>
</div>
</div>
<div class="element-code-area">
<span class="hl-comment">/* === 💡 1. 立体的にへこむボタン === */</span><br>
<span class="hl-blue">.btn-demo-press</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#0d6efd;</span><br>
<span class="hl-green">box-shadow:</span> <span class="hl-red">0 5px 0 #0a58ca;</span> <span class="hl-comment">/* 💡 通常時はあらかじめ下に「立体的な厚み」の影を敷く */</span><br>
<span class="hl-green">transition:</span> <span class="hl-red">transform 0.1s, box-shadow 0.1s;</span><br>
}<br>
<span class="hl-blue">.btn-demo-press:hover</span> {<br>
<span class="hl-green">transform:</span> <span class="hl-red">translateY(3px);</span> <span class="hl-comment">/* 💡 ホバー時に下に3px下げる */</span><br>
<span class="hl-green">box-shadow:</span> <span class="hl-red">0 2px 0 #0a58ca;</span> <span class="hl-comment">/* 💡 影の厚みも3px分減らすことで、へこんだように見える */</span><br>
}<br><br>
<span class="hl-comment">/* === 💡 2. ネオンのように光るボタン === */</span><br>
<span class="hl-blue">.btn-demo-glow</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#198754;</span><br>
<span class="hl-green">box-shadow:</span> <span class="hl-red">0 0 0 rgba(0,0,0,0);</span> <span class="hl-comment">/* 通常時は影をゼロにしておく */</span><br>
<span class="hl-green">transition:</span> <span class="hl-red">box-shadow 0.3s ease;</span><br>
}<br>
<span class="hl-blue">.btn-demo-glow:hover</span> {<br>
<span class="hl-green">cursor:</span> <span class="hl-red">pointer;</span><br>
<span class="hl-comment">/* ⭕️ 同系色の rgba を使って、広めのぼかし幅(20px)で発光を演出 */</span><br>
<span class="hl-green">box-shadow:</span> <span class="hl-red">0 0 20px rgba(25, 135, 84, 0.7);</span><br>
}
</div>
</div>.element-btn-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.element-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
.element-demo-area {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 30px;
background-color: #e9ecef;
padding: 40px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 40px;
}
.element-box {
background-color: #ffffff;
padding: 30px 20px;
border: 1px solid #ced4da;
border-radius: 4px;
text-align: center;
width: 100%;
max-width: 250px;
}
.demo-title {
margin-top: 0;
margin-bottom: 30px;
font-size: 14px;
font-weight: bold;
color: #333;
}
/* === 💡 1. 立体的にへこむボタンのスタイル === */
.btn-demo-press {
background-color: #0d6efd;
color: #ffffff;
border-top-style: none;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
padding-top: 15px;
padding-right: 30px;
padding-bottom: 15px;
padding-left: 30px;
border-radius: 6px;
font-weight: bold;
font-size: 15px;
/* 立体的な厚み(影)を作る */
box-shadow: 0 5px 0 #0a58ca;
/* 押し込みは一瞬で動かすのがリアル(0.1s) */
transition-property: transform, box-shadow;
transition-duration: 0.1s;
transition-timing-function: linear;
}
.btn-demo-press:hover {
/* 💡 下に3px移動 */
transform: translateY(3px);
/* 💡 影の厚みも3px削って2pxにする */
box-shadow: 0 2px 0 #0a58ca;
cursor: pointer;
}
/* === 💡 2. ネオン風に光るボタンのスタイル === */
.btn-demo-glow {
background-color: #198754;
color: #ffffff;
border-top-style: none;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
padding-top: 15px;
padding-right: 30px;
padding-bottom: 15px;
padding-left: 30px;
border-radius: 6px;
font-weight: bold;
font-size: 15px;
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
/* 発光はふわっと滑らかに(0.3s) */
transition-property: box-shadow;
transition-duration: 0.3s;
transition-timing-function: ease;
}
.btn-demo-glow:hover {
/* 同系色の緑色をrgbaでコントロール */
box-shadow: 0 0 20px rgba(25, 135, 84, 0.7);
cursor: pointer;
}
/* =コード解説エリア(エディタ風)= */
.element-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left-style: solid;
border-left-width: 4px;
border-left-color: #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }要素を傾けるtransformと影を操作するbox-shadowの使い方を詳しく知りたい人は以下から一読ください。
バナー画像やギャラリーのアイキャッチに対して、マウスを乗せた時に「フワッと白く透過させる」「少し暗くして文字を浮かび上がらせる」といった効果は人気です。
また、完全に別の写真に差し替える画像 切り替えのテクニックも解説します。
画像のエフェクトには、主にCSSのfilterプロパティを使用します。
画像制御は、色味を変えるなら、ブラウザ負荷が低く滑らかなfilter: brightness()(明度)やfilter: opacity()を使用することです。
もし完全に画像を切り替えるデザインにするなら、HTML上にあらかじめ2枚の画像(<img>)を重ねて配置しておき、通常時は上の画像をopacity: 1;、ホバー時にopacity: 0; にして『下の画像を透けて見させる』手法を徹底します。
これにより、事前に画像が読み込まれるため、チラつきが100%発生しなくなります。
⭕️ ホバーして比較!filterによる明度変化と、2枚重ねによるチラつかない画像切り替え!
💡 明度を下げる (filter)
※ホバーすると画像が少し暗くなり、
リンクであることが分かりやすくなります。
💡 安全な画像切り替え
※事前に2枚ともロードされているため、
チラつかずにフワッと切り替わります。
<div class="element-img-wrapper">
<p class="element-caption">⭕️ ホバーして比較!filterによる明度変化と、2枚重ねによるチラつかない画像切り替え!</p>
<div class="element-demo-area">
<div class="element-box">
<p class="demo-title">💡 明度を下げる (filter)</p>
<div class="img-effect-box">
<img src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22200%22%20height%3D%22120%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%230d6efd%22%2F%3E%3Ctext%20x%3D%2250%25%22%20y%3D%2250%25%22%20font-weight%3D%22bold%22%20fill%3D%22%23fff%22%20dominant-baseline%3D%22middle%22%20text-anchor%3D%22middle%22%3EIMAGE-A%3C%2Ftext%3E%3C%2Fsvg%3E" alt="デモ画像" class="brightness-img">
</div>
<p class="demo-desc">※ホバーすると画像が少し暗くなり、<br>リンクであることが分かりやすくなります。</p>
</div>
<div class="element-box">
<p class="demo-title">💡 安全な画像切り替え</p>
<div class="img-switch-container">
<img src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22200%22%20height%3D%22120%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23198754%22%2F%3E%3Ctext%20x%3D%2250%25%22%20y%3D%2250%25%22%20font-weight%3D%22bold%22%20fill%3D%22%23fff%22%20dominant-baseline%3D%22middle%22%20text-anchor%3D%22middle%22%3EIMAGE-B%3C%2Ftext%3E%3C%2Fsvg%3E" alt="切り替え後" class="img-back">
<img src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22200%22%20height%3D%22120%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23fd7e14%22%2F%3E%3Ctext%20x%3D%2250%25%22%20y%3D%2250%25%22%20font-weight%3D%22bold%22%20fill%3D%22%23fff%22%20dominant-baseline%3D%22middle%22%20text-anchor%3D%22middle%22%3EIMAGE-A%3C%2Ftext%3E%3C%2Fsvg%3E" alt="切り替え前" class="img-front">
</div>
<p class="demo-desc">※事前に2枚ともロードされているため、<br>チラつかずにフワッと切り替わります。</p>
</div>
</div>
<div class="element-code-area">
<span class="hl-comment">/* === 💡 1. brightnessによる明度制御 === */</span><br>
<span class="hl-blue">.brightness-img</span> {<br>
<span class="hl-green">transition:</span> <span class="hl-red">filter 0.3s ease;</span><br>
}<br>
<span class="hl-blue">.brightness-img:hover</span> {<br>
<span class="hl-green">filter:</span> <span class="hl-red">brightness(0.7);</span> <span class="hl-comment">/* 💡 70%の明るさに落とす(暗くする) */</span><br>
<span class="hl-green">cursor:</span> <span class="hl-red">pointer;</span><br>
}<br><br>
<span class="hl-comment">/* === 💡 2. チラつかない画像切り替え(プロの鉄則) === */</span><br>
<span class="hl-blue">.img-switch-container</span> {<br>
<span class="hl-green">position:</span> <span class="hl-red">relative;</span> <span class="hl-comment">/* 💡 2枚を重ねるための基準 */</span><br>
}<br>
<span class="hl-blue">.img-front</span> {<br>
<span class="hl-green">position:</span> <span class="hl-red">absolute;</span> <span class="hl-comment">/* 💡 1枚目を絶対配置で完全に上に被せる */</span><br>
<span class="hl-green">top:</span> <span class="hl-red">0;</span><br>
<span class="hl-green">left:</span> <span class="hl-red">0;</span><br>
<span class="hl-green">opacity:</span> <span class="hl-red">1;</span> <span class="hl-comment">/* 通常時は見える状態 */</span><br>
<span class="hl-green">transition:</span> <span class="hl-red">opacity 0.4s ease;</span><br>
}<br>
<span class="hl-blue">.img-switch-container:hover .img-front</span> {<br>
<span class="hl-green">opacity:</span> <span class="hl-red">0;</span> <span class="hl-comment"> /* 💡 ホバー時に上の画像だけを透明にすることで、下の画像が出現する */</span><br>
<span class="hl-green">cursor:</span> <span class="hl-red">pointer;</span><br>
}
</div>
</div>.element-img-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
/* === 💡 1. 明度制御スタイルの補足 === */
.img-effect-box {
width: 100%;
height: 120px;
border-radius: 4px;
overflow: hidden;
}
.img-effect-box img {
width: 100%;
height: 100%;
object-fit: cover;
transition-property: filter;
transition-duration: 0.3s;
transition-timing-function: ease;
}
.img-effect-box img:hover {
filter: brightness(0.7);
}
/* === 💡 2. 画像切り替えスタイルの補足 === */
.img-switch-container {
position: relative;
width: 100%;
height: 120px;
border-radius: 4px;
overflow: hidden;
}
.img-switch-container img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.img-front {
position: absolute;
top: 0;
left: 0;
opacity: 1;
transition-property: opacity;
transition-duration: 0.4s;
transition-timing-function: ease;
}
.img-switch-container:hover .img-front {
opacity: 0;
}
.element-caption,
.demo-title,
.demo-desc {
text-align: center;
}
/* =コード解説エリア(エディタ風)= */
.element-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left-style: solid;
border-left-width: 4px;
border-left-color: #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }画像エフェクトで多用するfilterの使い方を詳しく知りたい人は「【CSS】filterの使い方:画像や背景の色変更・ぼかしと効かない時の対策」を一読ください。
ヘッダーのグローバルナビやデータのまとまりを示すテーブル、箇条書きリストなどでは、ユーザーが現在どこの項目を見ているかを明確にするため、行全体をハイライトする実装が必須です。
特にテーブルの行をまとめて光らせる処理は、大量のデータ表を読みやすくする実務の定番パーツです。
ハイライト表現は、テーブルにおいて横の繋がり(行)を強調したい場合、マス目(td)ではなく、行の枠組みであるtrに対してホバーをかけることです。(tr:hover td)
これにより、その行にあるすべてのセルが連動して一斉にハイライトされ、読みやすい表組みになります。
⭕️ テーブルにマウスを乗せてみて!「td」ではなく「tr」にかける!
| クラス名 | 目的 | 難易度 |
|---|---|---|
| :hover | マウスホバー | ★☆☆ |
| :active | クリック押し込み | ★★☆ |
| :has() | 親要素の操作 | ★★★ |
<div class="element-nav-wrapper">
<p class="element-caption">⭕️ テーブルにマウスを乗せてみて!「td」ではなく「tr」にかける!</p>
<div class="element-demo-area">
<table class="highlight-table">
<thead>
<tr>
<th>クラス名</th>
<th>目的</th>
<th>難易度</th>
</tr>
</thead>
<tbody>
<tr>
<td>:hover</td>
<td>マウスホバー</td>
<td>★☆☆</td>
</tr>
<tr>
<td>:active</td>
<td>クリック押し込み</td>
<td>★★☆</td>
</tr>
<tr>
<td>:has()</td>
<td>親要素の操作</td>
<td>★★★</td>
</tr>
</tbody>
</table>
</div>
<div class="element-code-area">
<span class="hl-comment">/* ❌ 良くない例:td:hover にすると、マウスが乗った「1マス」しか色が変わらない */</span><br><br>
<span class="hl-comment">/* ⭕️ 行全体(tr)のホバーをトリガーにして、その中にある td すべてを染める! */</span><br>
<span class="hl-blue">.highlight-table tbody tr:hover td</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#cfe2ff;</span> <span class="hl-comment">/* 💡 行丸ごとブルーにハイライト */</span><br>
<span class="hl-green">color:</span> <span class="hl-red">#084298;</span><br>
<span class="hl-green">cursor:</span> <span class="hl-red">pointer;</span> <span class="hl-comment">/* 表全体がクリック可能な場合はカーソルも変更 */</span><br>
}<br>
<span class="hl-blue">.highlight-table td</span> {<br>
<span class="hl-green">transition:</span> <span class="hl-red">background-color 0.2s, color 0.2s;</span> <span class="hl-comment">/* ハイライトも少し滑らかにすると上品です */</span><br>
}
</div>
</div>.element-nav-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
/* === 💡 ハイライトテーブルのスタイル === */
.highlight-table {
width: 100%;
border-collapse: collapse;
background-color: #ffffff;
}
.highlight-table th,
.highlight-table td {
border: 1px solid #dee2e6;
padding: 12px 15px;
font-size: 14px;
text-align: center;
}
.highlight-table th {
background-color: #f1f3f5;
color: #495057;
font-weight: bold;
}
.highlight-table td {
color: #212529;
/* 変化をふわっとさせる */
transition-property: background-color, color;
transition-duration: 0.2s;
transition-timing-function: ease;
}
/* ⭕️ trホバー時に中のtdの色を一斉に変える */
.highlight-table tbody tr:hover td {
background-color: #cfe2ff;
color: #084298;
cursor: pointer;
}
/* =コード解説エリア(エディタ風)= */
.element-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left-style: solid;
border-left-width: 4px;
border-left-color: #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }リストやテーブルの作り方を詳しく知りたい人は以下から一読ください。
ホバーエフェクトの表現力を引き上げてくれるのが、疑似要素の組み合わせテクニックです。
HTMLの構造を汚すことなく、CSS側だけで装飾用の背景レイヤーを作り出し、ホバーした瞬間にそれをダイナミックに動かすことでリッチな表現を実装できます。
疑似要素を使った背景アニメーションは、①背景として動かす疑似要素(::before等)にはz-index: 1;を指定することです。
②それに対して、ボタンの文字(テキスト)はそのままでは疑似要素に負けてしまうため、<span>などのタグで囲んだ上でposition: relative;とz-index: 2;を明示的に指定し、スライドしてくる背景レイヤーよりも『上の階層』へ引っ張り上げるとよいです。
⭕️ ホバーしてみて!背景レイヤーが左からスライドして満たされる高級エフェクト!
💡 背景スライドボタン
お申し込みはこちら<div class="element-pseudo-wrapper">
<p class="element-caption">⭕️ ホバーしてみて!背景レイヤーが左からスライドして満たされる高級エフェクト!</p>
<div class="element-demo-area">
<div class="element-box" style="max-width: 320px;">
<p class="demo-title">💡 背景スライドボタン</p>
<a href="#" class="pseudo-slide-btn">
<span class="btn-text">お申し込みはこちら</span>
</a>
</div>
</div>
<div class="element-code-area">
<span class="hl-comment">/* 1. ボタンの土台(親)の設定 */</span><br>
<span class="hl-blue">.pseudo-slide-btn</span> {<br>
<span class="hl-green">position:</span> <span class="hl-red">relative;</span><br>
<span class="hl-green">overflow:</span> <span class="hl-red">hidden;</span> <span class="hl-comment">/* 💡 枠外に隠している背景を見えなくする */</span><br>
<span class="hl-green">border:</span> <span class="hl-red">2px solid #0d6efd;</span><br>
}<br><br>
<span class="hl-comment">/* 2. 疑似要素で「動く背景シート」を作る */</span><br>
<span class="hl-blue">.pseudo-slide-btn::before</span> {<br>
<span class="hl-green">content:</span> <span class="hl-red">"";</span><br>
<span class="hl-green">position:</span> <span class="hl-red">absolute;</span><br>
<span class="hl-green">top:</span> <span class="hl-red">0;</span><br>
<span class="hl-green">left:</span> <span class="hl-red">0;</span><br>
<span class="hl-green">width:</span> <span class="hl-red">100%;</span><br>
<span class="hl-green">height:</span> <span class="hl-red">100%;</span><br>
<span class="hl-green">background-color:</span> <span class="hl-red">#0d6efd;</span> <span class="hl-comment">/* 変化後の背景色 */</span><br>
<span class="hl-green">z-index:</span> <span class="hl-red">1;</span> <span class="hl-comment">/* 💡 階層は「1」 */</span><br>
<span class="hl-green">transform:</span> <span class="hl-red">scaleX(0);</span> <span class="hl-comment">/* 最初は横幅をゼロにして隠しておく */</span><br>
<span class="hl-green">transform-origin:</span> <span class="hl-red">left top;</span> <span class="hl-comment">/* 左端を起点にする */</span><br>
<span class="hl-green">transition:</span> <span class="hl-red">transform 0.4s ease;</span><br>
}<br><br>
<span class="hl-comment">/* 3. ⭕️ 中の文字は z-index: 2 で背景より上に引っ張り上げる! */</span><br>
<span class="hl-blue">.btn-text</span> {<br>
<span class="hl-green">position:</span> <span class="hl-red">relative;</span> <span class="hl-comment">/* 💡 z-indexを有効化するために必須 */</span><br>
<span class="hl-green">z-index:</span> <span class="hl-red">2;</span> <span class="hl-comment">/* 💡 背景シート(1)より上の「2」にする */</span><br>
<span class="hl-green">transition:</span> <span class="hl-red">color 0.4s ease;</span><br>
}<br><br>
<span class="hl-comment">/* 4. ホバー時に背景を伸ばし、文字色を反転させる */</span><br>
<span class="hl-blue">.pseudo-slide-btn:hover::before</span> {<br>
<span class="hl-green">transform:</span> <span class="hl-red">scaleX(1);</span> <span class="hl-comment">/* 💡 横幅を100%に引き伸ばして満たす */</span><br>
}<br>
<span class="hl-blue">.pseudo-slide-btn:hover .btn-text</span> {<br>
<span class="hl-green">color:</span> <span class="hl-red">#ffffff;</span> <span class="hl-comment">/* 青背景が来るので、文字を白に反転 */</span><br>
}
</div>
</div>.element-pseudo-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
/* === 💡 背景スライドボタンのスタイル === */
.pseudo-slide-btn {
position: relative;
display: inline-block;
padding-top: 15px;
padding-right: 30px;
padding-bottom: 15px;
padding-left: 30px;
text-decoration-line: none;
border-style: solid;
border-width: 2px;
border-color: #0d6efd;
border-radius: 6px;
background-color: #ffffff; /* 通常時は白背景 */
/* はみ出る背景をカット */
overflow: hidden;
}
/* スライドしてくる背景シート */
.pseudo-slide-btn::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #0d6efd;
/* 低い階層 */
z-index: 1;
/* 最初は左に縮めておく */
transform: scaleX(0);
transform-origin: left top;
transition-property: transform;
transition-duration: 0.35s;
transition-timing-function: ease-out;
}
/* ボタン内のテキスト */
.btn-text {
/* 💡 背景シートに隠されないよう最前面へ浮上させる */
position: relative;
z-index: 2;
color: #0d6efd;
font-weight: bold;
font-size: 16px;
transition-property: color;
transition-duration: 0.35s;
transition-timing-function: ease;
}
/* ホバー時の連動エフェクト */
.pseudo-slide-btn:hover::before {
/* 背景を右いっぱいに伸ばす */
transform: scaleX(1);
}
.pseudo-slide-btn:hover .btn-text {
/* 背景が青になるので文字は白に変える */
color: #ffffff;
}
/* =コード解説エリア(エディタ風)= */
.element-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left-style: solid;
border-left-width: 4px;
border-left-color: #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }CSSのホバーエフェクト(:hover)は、サイトに躍動感を与える機能ですが、実務の現場では「コード通りに書いたはずなのに、なぜかhoverが効かないと頭を抱えるシーンが多いです。
さらに現代のWeb制作において無視できないのが、スマートフォン(スマホ)やタブレットといった「タッチパネル端末」の存在です。
PC環境と同じ感覚でホバーエフェクトを放置すると、モバイル環境で予期せぬUXの低下を招きます。
ここでは、ホバーが効かない原因のデバッグ方法、スマホの課題である「ホバーの残存問題」を解決する記述方法まで解説します。
:hoverが効かない・文字色が変らない時のチェックリストhoverが残る問題と無効化(@media (hover: hover)):disabled)やクラスによるhoverの解除記述はしたのにホバーエフェクトが無視される場合、原因のほとんどは「CSSの詳細度(優先順位)の競合」か「要素の記述順序のエラー」です。
特に、「LVHAの法則(:link → :visited → :hover → :active)」の順序が崩れていると、訪問済みリンクに対してホバーが効かなくなります。
また、要素自体が他の見えない要素(絶対配置された透明なdivなど)に覆い隠されていて、マウスのセンサーが物理的に届いていないケースも実務ではよくあります。
ホバー時のスタイル変化を通すには、文字色などを変更したいターゲットが子要素にある場合、親要素をトリガーにしつつ、変更命令はターゲットである子要素へ名指しで指定することです。(例:.parent:hover .child { color: red; })。
これにより、子要素自身の持つ通常スタイルを上書き(オーバーライド)し、詳細度の競合による『効かないバグ』を回避できます。
⭕️ ホバーして確認!子要素に色がついてる場合は「親:hover 子要素」の形で上書きしろ!
<div class="debug-hover-wrapper">
<p class="debug-caption">⭕️ ホバーして確認!子要素に色がついてる場合は「親:hover 子要素」の形で上書きしろ!</p>
<div class="debug-demo-area">
<div class="debug-box">
<p class="demo-title">❌ 罠(文字色が変わらない)</p>
<div class="card-trigger-fail">
親カードをホバー中
<a href="#" class="inner-link-fail">子要素のリンクテキスト</a>
</div>
<p class="demo-desc">※親要素をホバーしても、子要素のリンク(青)の<br>詳細度に負けてしまい、文字色が変わりません。</p>
</div>
<div class="debug-box">
<p class="demo-title">⭕️ 成功(確実に色が変わる)</p>
<div class="card-trigger-success">
親カードをホバー中
<a href="#" class="inner-link-success">子要素のリンクテキスト</a>
</div>
<p class="demo-desc">※「親:hover 子要素」と指定しているため、<br>詳細度が上回り、確実に緑色へ変化します。</p>
</div>
</div>
<div class="debug-code-area">
<span class="hl-comment">/* ❌ 罠:親にホバーをかけても、子要素(aタグ)の持つ個別スタイルに勝てない */</span><br>
<span class="hl-blue">.inner-link-fail</span> {<br>
<span class="hl-green">color:</span> <span class="hl-red">#0d6efd;</span> <span class="hl-comment">/* 子要素自身の強力な個別色 */</span><br>
}<br>
<span class="hl-blue">.card-trigger-fail:hover</span> {<br>
<span class="hl-green">color:</span> <span class="hl-red">#dc3545;</span> <span class="hl-comment">/* 🚨 親に指定しても、子要素の青色には遺伝せず無視される */</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ 親をトリガーにして、目的の子要素(aタグ)を直接指定して上書きする! */</span><br>
<span class="hl-blue">.inner-link-success</span> {<br>
<span class="hl-green">color:</span> <span class="hl-red">#0d6efd;</span><br>
<span class="hl-green">transition:</span> <span class="hl-red">color 0.3s;</span><br>
}<br>
<span class="hl-blue">.card-trigger-success:hover .inner-link-success</span> {<br>
<span class="hl-green">color:</span> <span class="hl-red">#198754;</span> <span class="hl-comment">/* 💡 子要素を名指しで指定しているため、確実に青から緑に変わる */</span><br>
}
</div>
</div>.debug-hover-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.debug-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
.debug-demo-area {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 30px;
background-color: #e9ecef;
padding: 40px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 40px;
}
.debug-box {
background-color: #ffffff;
padding: 20px;
border: 1px solid #ced4da;
border-radius: 4px;
text-align: center;
width: 100%;
max-width: 250px;
}
.demo-title {
margin-top: 0;
margin-bottom: 20px;
font-size: 14px;
font-weight: bold;
}
.debug-box:first-child .demo-title { color: #dc3545; }
.debug-box:last-child .demo-title { color: #198754; }
.demo-desc {
margin-top: 20px;
margin-bottom: 0;
font-size: 11px;
color: #6c757d;
line-height: 1.6;
}
/* === ❌ 罠:親へのホバー指定 === */
.card-trigger-fail {
background-color: #f1f3f5;
padding: 15px;
border-radius: 6px;
font-weight: bold;
font-size: 14px;
color: #333;
}
.inner-link-fail {
display: block;
margin-top: 10px;
color: #0d6efd;
text-decoration: underline;
}
.card-trigger-fail:hover {
color: #dc3545; /* 遺伝を期待するが効かない */
}
/* === ⭕️ 成功:子要素への名指しホバー指定 === */
.card-trigger-success {
background-color: #f1f3f5;
padding: 15px;
border-radius: 6px;
font-weight: bold;
font-size: 14px;
color: #333;
}
.inner-link-success {
display: block;
margin-top: 10px;
color: #0d6efd;
text-decoration: underline;
transition-property: color;
transition-duration: 0.3s;
transition-timing-function: ease;
}
.card-trigger-success:hover .inner-link-success {
color: #198754;
}
/* =コード解説エリア(エディタ風)= */
.debug-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left-style: solid;
border-left-width: 4px;
border-left-color: #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }CSSの詳細度における優先順位について詳しく知りたい人は「CSSはどこに書く?HTMLでの正しい読み込み順と優先順位のルール」を一読ください。
スマートフォンなどのタッチパネル端末には、「マウスカーソル」という概念がありません。
そのため、スマホ画面でボタンをタップした際、ブラウザは擬似的に「タップされた瞬間にホバーした」と解釈します。
ここからが問題なのですが、ボタンを押し終わって指を離した後も、「次に画面の別の場所をタップするまで、ホバー時のスタイル(背景色など)が残り続けてしまう」という現象が発生します。
ボタンを押した後に色が変わりっぱなしになるのは、ユーザーに「ボタンがフリーズした?」「押しっぱなしになっている?」という誤解を与え、モバイルデザインの致命的な障壁となります。
スマホのホバー残りを防ぐには、画面幅(幅のpx数)で判定するのではなく、その端末が『マウスによるホバー操作をサポートしているかどうか』を直接判定するメディアクエリ@media (hover: hover)を使用することです。
この中にホバー用のスタイルを一括して記述すれば、スマホやタブレットではホバーが自動的に無効化され、PCでのみホバーが発動するコードになります。
💡 スマホでの見え方をシミュレート!ホバーエフェクトは「できる端末のみ」に絞り込め!
❌ 罠(スマホで色残る)
※タッチ端末でタップすると、
指を離してもホバー色が残り続けます。
⭕️ 成功(スマホで残らない)
※タッチ端末ではホバーが完全に無効化。
タップした一瞬だけ反応する理想的な挙動に。
<div class="mobile-hover-wrapper">
<p class="debug-caption">💡 スマホでの見え方をシミュレート!ホバーエフェクトは「できる端末のみ」に絞り込め!</p>
<div class="debug-demo-area">
<div class="debug-box">
<p class="demo-title is-error">❌ 罠(スマホで色残る)</p>
<button class="btn-touch-remain">
普通に書いたボタン
</button>
<p class="demo-desc">※タッチ端末でタップすると、<br>指を離してもホバー色が残り続けます。</p>
</div>
<div class="debug-box">
<p class="demo-title is-success">⭕️ 成功(スマホで残らない)</p>
<button class="btn-touch-clean">
プロ仕様のボタン
</button>
<p class="demo-desc">※タッチ端末ではホバーが完全に無効化。<br>タップした一瞬だけ反応する理想的な挙動に。</p>
</div>
</div>
<div class="debug-code-area">
<span class="hl-comment">/* ❌ 良くない例:そのまま書くとスマホで色が残り続ける原因に */</span><br>
<span class="hl-blue">.btn-touch-remain:hover</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#dc3545;</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ ホバー操作ができる「PC環境のみ」に限定する魔法のメディアクエリ */</span><br>
<span class="hl-blue">.btn-touch-clean</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#6c757d;</span><br>
<span class="hl-green">transition:</span> <span class="hl-red">background-color 0.3s;</span><br>
}<br>
<span class="hl-blue">@media (hover: hover)</span> {<br>
<span class="hl-comment">/* 💡 マウス操作ができるデバイス環境(PCなど)でのみこの中身が実行される */</span><br>
<span class="hl-blue">.btn-touch-clean:hover</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#198754;</span><br>
<span class="hl-green">cursor:</span> <span class="hl-red">pointer;</span><br>
}<br>
}
</div>
</div>.mobile-hover-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.is-error { color: #dc3545; }
.is-success { color: #198754; }
/* === ❌ 罠:そのままhoverを指定 === */
.btn-touch-remain {
background-color: #6c757d;
color: #ffffff;
border-top-style: none;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
padding-top: 12px;
padding-right: 20px;
padding-bottom: 12px;
padding-left: 20px;
border-radius: 6px;
font-weight: bold;
font-size: 14px;
}
.btn-touch-remain:hover {
background-color: #dc3545; /* スマホでもこの命令を無理やり実行しようとして色が残る */
}
/* === ⭕️ 成功:@media (hover: hover) で限定 === */
.btn-touch-clean {
background-color: #6c757d;
color: #ffffff;
border-top-style: none;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
padding-top: 12px;
padding-right: 20px;
padding-bottom: 12px;
padding-left: 20px;
border-radius: 6px;
font-weight: bold;
font-size: 14px;
transition-property: background-color;
transition-duration: 0.3s;
transition-timing-function: ease;
}
/* 💡 ホバー機能があるデバイス(PCなど)のみ適用 */
@media (hover: hover) {
.btn-touch-clean:hover {
background-color: #198754;
cursor: pointer;
}
}
/* =コード解説エリア(エディタ風)= */
.debug-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left-style: solid;
border-left-width: 4px;
border-left-color: #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }メディアクエリ(@media)の使い方を詳しく知りたい人は「【CSS】メディアクエリの書き方:レスポンシブ対応・ブレイクポイント・複数指定・効かない対策」を一読ください。
お問い合わせフォームなどで、必須項目が未入力のときに送信ボタンを押せない状態にする「無効化(disabled属性)」をHTMLで実装することがあります。
しかし、要素を単に無効化しただけでは、CSSの:hover設定はそのまま生きてしまうため、「押せないボタンなのに、マウスを乗せるとホバーアニメーションが動いてしまい、ユーザーが『押せる』と勘違いする」というおかしな挙動になってしまいます。
そのため、無効化されている状態や特定の状況では意図的にホバーエフェクトを解除・削除する条件分岐が必要です。
ホバーを除外するには、手動で上書き消去するのではなくCSSの否定疑似クラス:not()を使い、『disabled(無効化)になって“いない”状態のときだけ、ホバーを有効にする』という除外の書き方をすることです。
もしくは、ホバー自体を無効化するpointer-events: none;をdisabled時に1行指定するだけで、マウスの接触判定自体を消し去ってホバーをシャットアウトすることです。
⭕️ ホバーして比較!「:not」や「pointer-events: none」を使えば、無効化時のホバー解除がたった1行!
❌ 罠(押せないのに動く)
※disabledなのにホバーすると
色が変わってしまい、
押せるような誤解を与えます。
⭕️ 成功(ホバーが完全解除)
※無効化されている時はマウスを乗せても
一切反応せず、動かないため、
「押せないボタン」だと直感的に伝わります。
<div class="remove-hover-wrapper">
<p class="debug-caption">⭕️ ホバーして比較!「:not」や「pointer-events: none」を使えば、無効化時のホバー解除がたった1行!</p>
<div class="remove-demo-area">
<div class="debug-box">
<p class="demo-title is-error">❌ 罠(押せないのに動く)</p>
<button class="btn-disabled-trap" disabled>
送信不可 (Trap)
</button>
<p class="demo-desc">※disabledなのにホバーすると<br>色が変わってしまい、<br>押せるような誤解を与えます。</p>
</div>
<div class="debug-box">
<p class="demo-title is-success">⭕️ 成功(ホバーが完全解除)</p>
<button class="btn-disabled-success" disabled>
送信不可 (Success)
</button>
<p class="demo-desc">※無効化されている時はマウスを乗せても<br>一切反応せず、動かないため、<br>「押せないボタン」だと直感的に伝わります。</p>
</div>
</div>
<div class="debug-code-area">
<span class="hl-comment">/* ❌ 罠:disabledを考慮せずそのままhoverを書くと、無効ボタンも光ってしまう */</span><br>
<span class="hl-blue">.btn-disabled-trap:hover</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#0b5ed7;</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ :not()を使い、「無効化されていない時だけ」ホバーを有効化 */</span><br>
<span class="hl-blue">.btn-disabled-success:not(:disabled):hover</span> {<br>
<span class="hl-green">background-color:</span> <span class="hl-red">#0b5ed7;</span> <span class="hl-comment">/* 💡 disabled時はこのホバー処理自体がスキップされる */</span><br>
<span class="hl-green">cursor:</span> <span class="hl-red">pointer;</span><br>
}<br><br>
<span class="hl-comment">/* ⭕️ あるいは、disabled時に「判定そのもの」を消し去る(一番お手軽) */</span><br>
<span class="hl-blue">.btn-disabled-success:disabled</span> {<br>
<span class="hl-green">opacity:</span> <span class="hl-red">0.5;</span> <span class="hl-comment">/* 見た目を半透明にする */</span><br>
<span class="hl-green">pointer-events:</span> <span class="hl-red">none;</span> <span class="hl-comment">/* 💡 マウスの接触判定自体を消滅させ、hoverを強制解除する */</span><br>
<span class="hl-green">cursor:</span> <span class="hl-red">not-allowed;</span> <span class="hl-comment">/* 禁止マークのカーソルにする */</span><br>
}
</div>
</div>.remove-hover-wrapper {
background-color: #f8f9fa;
padding: 30px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.debug-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
.remove-demo-area {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 30px;
background-color: #e9ecef;
padding: 40px 20px;
border-radius: 8px;
border: 1px dashed #adb5bd;
margin-bottom: 40px;
}
.debug-box {
background-color: #ffffff;
padding: 20px;
border: 1px solid #ced4da;
border-radius: 4px;
text-align: center;
width: 100%;
max-width: 250px;
}
.demo-title {
margin-top: 0;
margin-bottom: 20px;
font-size: 14px;
font-weight: bold;
}
.is-error {
color: #dc3545;
}
.is-success {
color: #198754;
}
.demo-desc {
margin-top: 20px;
margin-bottom: 0;
font-size: 11px;
color: #6c757d;
line-height: 1.6;
}
/* === ❌ 罠:無効化を無視するホバースタイル === */
.btn-disabled-trap {
background-color: #0d6efd;
color: #ffffff;
border-top-style: none;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
padding-top: 12px;
padding-right: 20px;
padding-bottom: 12px;
padding-left: 20px;
border-radius: 6px;
font-weight: bold;
font-size: 14px;
opacity: 0.5; /* disabled用の半透明 */
}
.btn-disabled-trap:hover {
background-color: #0b5ed7;
cursor: pointer;
}
/* === ⭕️ 成功:スマートにホバーを除外するスタイル === */
.btn-disabled-success {
background-color: #0d6efd;
color: #ffffff;
border-top-style: none;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
padding-top: 12px;
padding-right: 20px;
padding-bottom: 12px;
padding-left: 20px;
border-radius: 6px;
font-weight: bold;
font-size: 14px;
}
/* disabled時のスタイル */
.btn-disabled-success:disabled {
opacity: 0.5;
/* 💡 接触判定そのものを消してhoverを発生させない */
pointer-events: none;
}
/* PCなどのホバー対応環境かつ、disabledじゃない時だけホバーを実行(:not) */
@media (hover: hover) {
.btn-disabled-success:not(:disabled):hover {
background-color: #0b5ed7;
cursor: pointer;
}
}
/* =コード解説エリア(エディタ風)= */
.debug-code-area {
background-color: #282c34;
color: #abb2bf;
padding: 20px;
border-radius: 6px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left-style: solid;
border-left-width: 4px;
border-left-color: #0d6efd;
overflow-x: auto;
text-align: left;
}
.hl-blue {
color: #61afef;
font-weight: bold;
}
.hl-red {
color: #e06c75;
font-weight: bold;
}
.hl-green {
color: #98c379;
font-weight: bold;
}
.hl-comment {
color: #6c757d;
font-style: italic;
}疑似クラスである:notや無効化で利用するdisabledの使い方を詳しく知りたい人は以下から一読ください。
分かりやすいようにまとめを記載します。
divやspanでボタンを作る際は、ホバー時にcursor: pointer;を指定してクリック可能であることを伝える。:link→:visited→:hover→:activeの順序で上から下に記述する。transitionは:hover側ではなく通常時のベース要素に記述する。widthは変えずにtransform: scale()を使い、親要素にoverflow: hidden;を指定する。transformは宣言のたびに上書きされるため、回転と移動などを組み合わせる場合は数数繋ぎで同時に記述する。text-decorationや物理的なborderの追加はガタつきの原因。::before/::after)を自作し、scaleで伸ばす。親:hover 子、隣の要素は兄:hover + 弟を使用。:has()セレクタを使い、親:has(子:hover)という関数型の書き方で子から親のスタイルを逆引き変更できる。display: noneは使わず、visibilityとopacityを併用し、親要素との隙間をpadding等で埋める。.parent:hover .child)で指定する。@media (hover: hover) {}の中に記述してPC環境のみに限定する。:not(:disabled):hoverでホバーをスキップするか、pointer-events: none;で判定自体を消滅させる。マウス操作ができるデバイスに限定してホバーを適用するメディアクエリ@media (hover: hover)を使用するのが確実な解決策です。
CSSのホバーに関する記述をすべて@media (hover: hover) { ... }で囲むことで、PCなどマウスのある環境のみでホバーが動き、タッチ操作しかできないスマホやタブレットではホバーが自動的に無効化されるため、色が残り続ける現象を防止できます。
中にある子要素(<a>タグなど)自体に、すでに個別の文字色(color)が指定されていることが原因です。
CSSの優先順位ルール(詳細度)により、親要素から引き継がれる色よりも、子要素自身に直接指定されている色が勝ってしまいます。
文字色を変えたい場合は、.parent:hover .child { color: red; }のように、親のホバーをトリガーにしつつ、変更したい子要素を名指しで指定して上書きしてください。
transitionプロパティが、通常のベースとなる要素ではなく:hover疑似クラス側のスタイルの中に記述されている可能性が高いです。
:hoverの中に書くと、マウスが離れてホバー状態が解除された瞬間にtransitionの命令自体が消滅するため、帰りのアニメーションが効かなくなります。
transitionはホバー前の「通常時の要素」に対して記述してください。
常の兄弟結合子(+や~)では、HTML上で自分より「後ろ」にある要素しか操作できないため、前の要素を動かすことはできません。
ただし、モダンブラウザであれば:has()セレクタ を用いることで、「特定の子要素がホバーされている時の親要素」といった指定が可能になり、間接的に手前の要素や親要素のスタイルを逆引きで変更できるようになりました。
HTMLのdisabled属性を付与しただけでは、CSSの:hover設定は自動的に解除されません。
対策として、CSS側でホバーをかける際に否定疑似クラスを組み合わせ、.btn:not(:disabled):hover { ... }という書き方をするか、無効化時のスタイルとして.btn:disabled { pointer-events: none; }を指定してマウスの接触判定自体を消滅させ、ホバーを強制解除してください。
サイト制作でお困りの人はお気軽にご連絡ください。
どんなお悩み事も丁寧に返信させて頂きます。
「どのサーバーを選べばいいか分からない…」そんな悩みを解決!
WordPressデビューに最適なサーバーを徹底比較しました。
