WithCodeMedia-1-pc
previous arrowprevious arrow
next arrownext arrow

WithCodeMedia-1-sp
previous arrowprevious arrow
next arrownext arrow

CSS画像をホバーで拡大・ズームする方法|おしゃれ演出コピペ

生徒

サムネイル画像にマウスを乗せたらふわっと拡大する演出、どう作るのが正解ですか?

ペン博士

親要素をoverflow:hiddenにして、画像側をtransform:scale()で拡大するのが定番だよ。今日はキャプション表示やグレースケール解除も一緒に紹介するね。

ポートフォリオサイトやブログのサムネイル画像にホバーで拡大エフェクトを付けたい——そう思ったとき、JavaScriptは不要です。CSSのtransform: scale()overflow: hiddenを組み合わせるだけで、クリーンでパフォーマンスの良いズームエフェクトが実装できます。

この記事では基本のズームから始め、暗転オーバーレイとキャプション表示の組み合わせ、filterによるモノクロ→カラー変換、カード全体ホバーで画像だけ動かす手法まで、そのままコピペして使えるコードを段階的に解説します。

アクセシビリティ面でキーボードフォーカス時にも同じ演出を発火させる実装も含めて紹介するので、実務でそのまま使えます。

  • overflow:hidden+transform:scaleによる基本ズームの仕組み
  • 暗転オーバーレイとキャプションのホバー表示
  • filter: grayscaleでモノクロ→カラーへ変化させる演出
  • transitionでなめらかなアニメーションに仕上げるコツ
  • カード全体ホバーで画像だけズームさせる実装
  • focus-visibleによるアクセシビリティ対応

目次

ホバーズームの基本原理

ズームの仕組み

画像のホバーズームを実装する前に、仕組みを理解しておきましょう。ポイントは2つです。

  • overflow: hidden — 親要素の外にはみ出た部分を隠す。これがないと拡大した画像が周囲にはみ出す
  • transform: scale() — 要素を拡大縮小する。GPU処理されるためパフォーマンスが良い
プロパティ 役割 設定する要素
overflow: hidden はみ出た画像を隠す 親のdiv(ラッパー)
transform: scale(1.1) 画像を110%に拡大 imgタグ
transition: transform 0.3s なめらかに変化させる imgタグ
display: block 画像下の余白をなくす imgタグ

imgにwidth:100%とheight:100%を指定してobject-fit:coverを使うことで、親要素のサイズに合わせて画像が切り抜かれ、比率が崩れません。これが現代的なホバーズーム実装の基本形です。


基本:シンプルなホバーズーム

基本実装3ステップ

最もシンプルな実装

<div class="zoom-wrap">
  <img src="image.jpg" alt="サンプル画像" width="400" height="300">
</div>
.zoom-wrap {
  overflow: hidden;
  width: 400px;
  height: 300px;
}

.zoom-wrap img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.3s ease;
}

.zoom-wrap:hover img {
  transform: scale(1.1);
}

これだけで動作します。scale(1.1)は110%への拡大です。値が大きいほど拡大幅が増します。transition の 0.3s0.6s にすればゆっくり、0.15s にすればキレのある動きになります。

ease・ease-in-out・cubicで印象を変える

/* 標準的なズーム */
.zoom-default img {
  transition: transform 0.3s ease;
}

/* ゆっくり始まってゆっくり終わる・上品な印象 */
.zoom-smooth img {
  transition: transform 0.5s ease-in-out;
}

/* カスタムイージング:最初に少し戻ってから拡大するスプリング風 */
.zoom-spring img {
  transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* 急に始まってゆっくり終わる・勢いのある印象 */
.zoom-bold img {
  transition: transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.zoom-default:hover img,
.zoom-smooth:hover img,
.zoom-spring:hover img,
.zoom-bold:hover img {
  transform: scale(1.12);
}

cubic-bezier(0.34, 1.56, 0.64, 1)はバウンス感のあるスプリングアニメーションで、おしゃれなポートフォリオサイトでよく使われる動きです。


ズーム+暗転オーバーレイ+キャプション表示

ポートフォリオやギャラリーでよく見るパターンです。ホバーすると画像が暗くなり、中央にテキストが現れます。

基本のオーバーレイキャプション

<div class="card-zoom">
  <img src="image.jpg" alt="作品名" width="400" height="300">
  <div class="card-overlay">
    <p class="card-caption">作品タイトル</p>
    <span class="card-tag">Web Design</span>
  </div>
</div>
.card-zoom {
  position: relative;
  overflow: hidden;
  width: 400px;
  height: 300px;
  border-radius: 8px;
}

.card-zoom img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease;
}

/* オーバーレイ(初期状態は透明) */
.card-overlay {
  position: absolute;
  inset: 0; /* top:0 right:0 bottom:0 left:0 の短縮 */
  background: rgba(0, 0, 0, 0);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  transition: background 0.4s ease;
}

/* キャプション(初期状態は非表示) */
.card-caption {
  color: #fff;
  font-size: 1.25rem;
  font-weight: 700;
  margin: 0;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.3s ease 0.1s, transform 0.3s ease 0.1s;
}

.card-tag {
  color: rgba(255,255,255,0.8);
  font-size: 0.85rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.3s ease 0.15s, transform 0.3s ease 0.15s;
}

/* ホバー時 */
.card-zoom:hover img {
  transform: scale(1.08);
}

.card-zoom:hover .card-overlay {
  background: rgba(0, 0, 0, 0.55);
}

.card-zoom:hover .card-caption,
.card-zoom:hover .card-tag {
  opacity: 1;
  transform: translateY(0);
}

キャプションにtransition-delayを設定してテキストが少し遅れて現れるようにすると、演出がより洗練されます。.card-tagのdelayを0.15sにしてズレを作っています。

左から右にスライドするオーバーレイ

<div class="card-slide">
  <img src="image.jpg" alt="作品名" width="400" height="300">
  <div class="slide-overlay">
    <p>詳しく見る →</p>
  </div>
</div>
.card-slide {
  position: relative;
  overflow: hidden;
  width: 400px;
  height: 300px;
}

.card-slide img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease;
}

.slide-overlay {
  position: absolute;
  inset: 0;
  background: rgba(209, 97, 118, 0.85);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 1.1rem;
  /* 左側から外れた位置(translateX(-100%))に初期配置 */
  transform: translateX(-100%);
  transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.card-slide:hover img {
  transform: scale(1.05);
}

.card-slide:hover .slide-overlay {
  transform: translateX(0);
}

filterでモノクロ→カラーへ変化させる

デフォルトをモノクロにしてホバーでカラーに変化させるエフェクトは、ポートフォリオサイトで特に人気のある演出です。filter: grayscale()を使います。

グレースケール→カラー(基本)

<div class="grayscale-wrap">
  <img src="image.jpg" alt="作品" width="400" height="300">
</div>
.grayscale-wrap {
  overflow: hidden;
  width: 400px;
  height: 300px;
}

.grayscale-wrap img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* デフォルトはモノクロ+少し暗め */
  filter: grayscale(100%) brightness(0.85);
  transition:
    filter 0.4s ease,
    transform 0.4s ease;
}

.grayscale-wrap:hover img {
  filter: grayscale(0%) brightness(1);
  transform: scale(1.05);
}

セピア→カラーの組み合わせ

.sepia-wrap img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: sepia(80%) contrast(0.9) brightness(0.9);
  transition: filter 0.5s ease, transform 0.5s ease;
}

.sepia-wrap:hover img {
  filter: sepia(0%) contrast(1) brightness(1);
  transform: scale(1.06);
}

ブラー→シャープにフォーカスするエフェクト

ホバー前にぼかし、ホバーでシャープになる演出です。

.blur-focus-wrap {
  overflow: hidden;
  width: 400px;
  height: 300px;
}

.blur-focus-wrap img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: blur(4px) brightness(0.8);
  transform: scale(1.08); /* ぼかしでエッジが切れるためあらかじめ大きく */
  transition: filter 0.4s ease, transform 0.4s ease;
}

.blur-focus-wrap:hover img {
  filter: blur(0px) brightness(1);
  transform: scale(1.0);
}

ぼかしを使う際は、あらかじめ画像をscale(1.08)程度で拡大しておかないと、blur処理でエッジ付近が白くなります。ホバー時にscale(1.0)に戻すことで自然なフォーカス感が生まれます。


カード全体のホバーで画像だけズームさせる

よくあるブログカードやポートフォリオグリッドでは、カード全体がクリッカブルで、カードにホバーしたときに画像だけがズームするパターンが使いやすいです。

ブログカードの実装

<a href="/post/1" class="blog-card">
  <div class="blog-card-thumb">
    <img src="thumbnail.jpg" alt="記事サムネイル" width="400" height="225">
  </div>
  <div class="blog-card-body">
    <span class="blog-card-cat">CSS</span>
    <h2 class="blog-card-title">記事タイトルがここに入ります</h2>
    <p class="blog-card-date">2026年7月28日</p>
  </div>
</a>
.blog-card {
  display: block;
  text-decoration: none;
  color: inherit;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
  transition: box-shadow 0.3s ease, transform 0.3s ease;
  background: #fff;
}

/* カードごとわずかに浮き上がる */
.blog-card:hover {
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
  transform: translateY(-4px);
}

.blog-card-thumb {
  overflow: hidden;
  aspect-ratio: 16 / 9;
}

.blog-card-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease;
}

/* カードホバー時に画像だけズーム */
.blog-card:hover .blog-card-thumb img {
  transform: scale(1.08);
}

.blog-card-body {
  padding: 16px 20px 20px;
}

.blog-card-cat {
  display: inline-block;
  background: #d16176;
  color: #fff;
  font-size: 0.75rem;
  padding: 3px 10px;
  border-radius: 4px;
  margin-bottom: 8px;
}

.blog-card-title {
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.5;
  margin: 0 0 8px;
}

.blog-card-date {
  font-size: 0.8rem;
  color: #888;
  margin: 0;
}

グリッドレイアウトでの使用例

<div class="card-grid">
  <a href="/post/1" class="blog-card"><!-- 上記と同じ構造 --></a>
  <a href="/post/2" class="blog-card"></a>
  <a href="/post/3" class="blog-card"></a>
</div>
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 24px;
}

@media (max-width: 640px) {
  .card-grid {
    grid-template-columns: 1fr;
    gap: 16px;
  }
}

アクセシビリティ:focusでも同じ演出を発火させる

配慮ポイント

マウス操作ユーザーだけでなく、キーボードユーザーや支援技術ユーザーにも同じ体験を提供することが重要です。:focus-visibleを使ってホバーと同じスタイルをフォーカス時にも適用します。

/* hoverとfocus-visibleを並べて書く */
.zoom-wrap:hover img,
.zoom-wrap:focus-visible img {
  transform: scale(1.1);
}

/* カードの場合 */
.blog-card:hover,
.blog-card:focus-visible {
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
  transform: translateY(-4px);
}

.blog-card:hover .blog-card-thumb img,
.blog-card:focus-visible .blog-card-thumb img {
  transform: scale(1.08);
}

/* フォーカスリングをカスタマイズ(デフォルトのoutlineを上書き) */
.blog-card:focus-visible {
  outline: 3px solid #d16176;
  outline-offset: 2px;
}

/* オーバーレイキャプションにも対応 */
.card-zoom:hover .card-overlay,
.card-zoom:focus-visible .card-overlay {
  background: rgba(0, 0, 0, 0.55);
}

.card-zoom:hover .card-caption,
.card-zoom:focus-visible .card-caption,
.card-zoom:hover .card-tag,
.card-zoom:focus-visible .card-tag {
  opacity: 1;
  transform: translateY(0);
}

:focusではなく:focus-visibleを使うのがポイントです。:focusはクリック時にもフォーカスリングを表示しますが、:focus-visibleはキーボード操作時のみ表示するためUXが向上します。

prefers-reduced-motionへの対応

/* 動きを減らす設定がONの場合はズームしない */
@media (prefers-reduced-motion: reduce) {
  .zoom-wrap img,
  .card-zoom img,
  .blog-card-thumb img {
    transition: none;
  }

  .zoom-wrap:hover img,
  .card-zoom:hover img,
  .blog-card:hover .blog-card-thumb img {
    transform: none; /* ズームしない */
  }

  /* ズームの代わりにopacityで変化を伝える */
  .zoom-wrap:hover img {
    opacity: 0.85;
  }
}

失敗しやすいポイントと対処法

つまずき→対処
問題 原因 対処法
画像が枠の外にはみ出す overflow:hiddenが未設定 ラッパーdivにoverflow:hiddenを付ける
画像が縦に引き伸びる object-fit:coverが未設定 imgにobject-fit:coverを付ける
ズームがカクつく transform以外をアニメーション transformとopacityのみ使う
hover時に画像がぼやける Safari+小数点scale scale値を整数に近い値に丸める
キャプションが見えない z-indexが不足 overlayにz-index:1を付ける
transition が効かない display:noneからの切り替え opacityとvisibilityを使う

display:noneではなくopacity+visibilityを使う

キャプションを非表示にするとき、display: noneにするとtransitionが効きません。opacity: 0visibility: hiddenの組み合わせを使います。

/* NG: transitionが効かない */
.caption-ng {
  display: none;
}
.wrap:hover .caption-ng {
  display: block; /* transitionが効かない */
}

/* OK: opacityとvisibilityで制御 */
.caption-ok {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease;
}
.wrap:hover .caption-ok {
  opacity: 1;
  visibility: visible;
}

まとめ

画像ホバーズームは、親をoverflow:hiddenにして中の画像をtransform:scaleで拡大するのが基本です。ポイントは①拡大はscaleでtransitionは0.3s前後②キャプションはオーバーレイで重ねる③スマホはhoverが効かないのでfocusやタップも考慮、の3点。やりすぎず上品な拡大率(1.05〜1.1)が見やすくおすすめです。

よくある質問(FAQ)

transform: scaleとwidthを直接変更するのはどう違いますか?

transform: scaleはGPUで処理されるためメインスレッドへの負荷がなく、60fpsのスムーズなアニメーションを維持できます。一方、widthやheightを変更すると再レイアウト(reflow)が発生し、他の要素の位置も再計算されるためパフォーマンスへの影響が大きくなります。ホバーズームには必ずtransform: scaleを使ってください。

ズーム率はどのくらいが適切ですか?

一般的にscale(1.05)〜scale(1.15)の範囲が自然です。scale(1.05)は控えめで上品な印象、scale(1.1)が標準的、scale(1.15)以上はインパクトが強くなります。サムネイル画像には1.08〜1.1、ヒーロー画像やフルスクリーン背景には1.05程度が適しています。

Safariで画像がぼやけることがあります。対処法は?

SafariはサブピクセルのtransformをCPUレンダリングする場合があり、小数点の拡大率でぼやけが生じることがあります。transform: translateZ(0)をimgタグに追加してGPUレイヤーに強制昇格させるか、backface-visibility: hiddenを付与することで改善するケースがあります。

Next.js(next/image)やWordPressのアイキャッチ画像でも同じ実装が使えますか?

使えます。Next.jsのnext/imageはimgタグをラップするdivにoverflowが設定されていることがあるので、スタイルを上書きする際は親要素のクラスを確認してください。WordPressのアイキャッチはpost-thumbnailクラスが付くimgタグに対して.blog-card:hover .post-thumbnail imgのようにセレクターを書けば同様に動作します。

スマートフォンでhoverは効きますか?

タッチデバイスのホバー動作は不安定です。タップ時に一瞬hoverが発火するブラウザもあれば、まったく効かないものもあります。スマートフォンでは「タップしたら拡大」の演出は不自然に見えることがあるので、@media (hover: hover)メディアクエリで「真のホバー可能デバイスにのみ適用する」と安全です。例:@media (hover: hover) { .zoom-wrap:hover img { transform: scale(1.1); } }


あわせて読みたい関連記事

CSS・Webデザイン実装の総まとめ

CSSの実装テクニックを「レイアウト・装飾・アニメーション・基礎・トラブル解決」の目的別に探せる総まとめページを用意しています。実装で迷ったときの索引としてどうぞ。

AIスキルで、未来の自分をアップデート!

今なら完全無料でAIを学べる!WithAI

今なら完全無料でAIを学べる!

  • 動画や実践で楽しく学べる:初心者でも安心のカリキュラム
  • スマホ・PCどちらでもOK:好きな時間に学習できる
  • 料金は一切ナシ0円でAIスキルが身につく

目的に合わせて選べる「AI副業」「AI転職」「AI活用」の3コースを用意。副収入・キャリアチェンジ・日常の生産性アップまで、あなたのゴールに合わせてAIを学べます。

会員登録はカンタン30秒で完了します。まずは公式LINEから、無料でAI学習をスタートしましょう!

この記事を書いた人

WithCodeでWeb制作を習得後、フリーランスエンジニアとして活動。HTML/CSS・JavaScript・WordPress案件を中心に年間20件以上の制作実績を持つ。「難しい技術をわかりやすく」をモットーに、初心者〜中級者向けの技術記事を執筆。副業・フリーランス独立を目指す方に向けた情報発信に注力している。

– service –WithGroupの運営サービス

  • WithCode
    - ウィズコード -

    スクール

    「未経験」から
    現場で通用する
    スキルを身に付けよう!

    詳細はこちら
  • WithFree
    - ウィズフリ -

    実案件サポート

    制作会社のサポート下で
    実務経験を積んでいこう!

    詳細はこちら

公式サイト より
今すぐ
無料カウンセリング
予約!

目次