【コピペOK】CSS カード ホバー 効果9選
css カード ホバーとは、商品カードやブログ記事カードにマウスを乗せたりキーボードでフォーカスを移したりした瞬間だけ、画像が拡大したり隠れていた説明が現れたりする短い動きのことです。商品・記事カードにそのまま貼り付けられる9種類を、実際にブラウザで描画して動きを測定してからまとめました。
自動再生 · タイルを押すとその項目へ · すべて1つのzip
- 01 画像ズーム+キャプションスライド
- 02 ゾーン別3Dチルト(CSSのみ)
- 03 裏面フリップ公開
- 04 コーナーブラケットフレーム
- 05 バッジの弾む登場
- 06 CTA下線スイープ(親ホバー)
- 07 アンカー配置のミニパネル
- 08 グラスモーフィズムブラー
- 09 選択リングの描画
9個は人気の高い順ではなく、カードを一枚じっくり見るときに視線と操作が実際にたどる順に並べました。まず画像が主役として大きくなり(01)、カーソルを置いた位置に応じてカード自体が傾き(02)、気になって見ていると裏面がめくれます(03)。続けてブラケット・バッジ・下線という3つの小さな合図で狙いを絞ったことを伝えたあと(04〜06)、少し止まると詳細パネルが横に現れ(07)、ガラス質の表面が光を受けたように濃くなり(08)、最後にリングが一周描かれて選択が確定します(09)。
| 番号 | 名前 | 何が動くか | JS |
|---|---|---|---|
| 01 | 画像ズーム+キャプションスライド | 画像scale・キャプションtranslateY |
なし |
| 02 | ゾーン別3Dチルト | カードrotateX・rotateY |
なし |
| 03 | 裏面フリップ公開 | カードrotateY(180deg) |
なし |
| 04 | コーナーブラケットフレーム | ブラケットopacity・位置 |
なし |
| 05 | バッジの弾む登場 | バッジscaleのオーバーシュート |
なし |
| 06 | CTA下線スイープ | 下線scaleX |
なし |
| 07 | アンカー配置のミニパネル | パネルopacity・位置 |
なし |
| 08 | グラスモーフィズムブラー | backdrop-filter・影 |
なし |
| 09 | 選択リングの描画 | SVGstroke-dashoffset |
なし |
01画像ズーム+キャプションスライド
カードにカーソルを乗せると内側の画像がゆっくり1.08倍まで拡大し、同時に下に隠れていたキャプションが上へスライドして重なります。旅行先や商品を紹介するカード、ブログのサムネイルカードに向いています。
.card__media {
transform: scale(1);
transition: transform $duration $easing;
}
.card__caption {
transform: translateY(60%);
opacity: 0;
transition: transform $duration $easing, opacity $duration $easing;
}
.card:hover .card__media,
.card:focus-visible .card__media { transform: scale(1.08); }
.card:hover .card__caption,
.card:focus-visible .card__caption { transform: translateY(0); opacity: 1; }
02ゾーン別3Dチルト(CSSのみ)
カードを4分割した見えないゾーンのうち、カーソルが乗ったゾーンに応じて:has()セレクタが異なる傾きの値を選び、座標を読み取るJSなしでもその方向へ傾きます。JSに頼らずチルトを実装したい機能紹介カードやポートフォリオカードに向いています。
.card__inner {
transform-style: preserve-3d;
transition: transform $duration $easing;
}
.card:has(.zone--tl:hover) .card__inner { transform: rotateX(8deg) rotateY(-10deg); }
.card:has(.zone--tr:hover) .card__inner { transform: rotateX(8deg) rotateY(10deg); }
.card:has(.zone--bl:hover) .card__inner { transform: rotateX(-8deg) rotateY(-10deg); }
.card:has(.zone--br:hover) .card__inner { transform: rotateX(-8deg) rotateY(10deg); }
.card:focus-visible .card__inner { transform: rotateX(8deg) rotateY(-10deg); }
03裏面フリップ公開
カードにカーソルを乗せるとY軸を軸に180度回転して裏面が現れ、表面には収まらなかった詳細説明やボタンが姿を見せます。料金カードの細かい条件やチームメンバー紹介のように、情報が二層あるカードに向いています。
.card__flip {
transform-style: preserve-3d;
transition: transform $duration $easing;
}
.card__face--back {
backface-visibility: hidden;
transform: rotateY(180deg);
}
.card:hover .card__flip,
.card:focus-visible .card__flip {
transform: rotateY(180deg);
}
04コーナーブラケットフレーム
カードの四隅にたたまれた短いブラケットが普段は内側に小さく隠れていて、ホバーすると縁のほうへ伸びてカメラのフォーカス枠のようにカードを囲みます。何枚も並ぶギャラリーグリッドで1枚を選ばせたいときに向いています。
.corner {
opacity: 0;
transition: transform $duration $easing, opacity $duration $easing;
}
.corner--tl { transform: translate($sp-2, $sp-2); }
.card:hover .corner,
.card:focus-visible .corner {
opacity: 1;
transform: translate(0, 0);
}
05バッジの弾む登場
普段は隠れている「NEW」や「SALE」のバッジが、ホバーすると弾むイージングで一度大きくなってからわずかに縮んで収まります。セール商品や新着アイテムのように、もう一押し目を引きたいカードで使います。
.badge {
transform: scale(0);
transition: transform $duration $ease-pop;
}
.card:hover .badge,
.card:focus-visible .badge {
transform: scale(1);
}
06CTA下線スイープ(親ホバー)
「詳しく見る」の文字そのものではなくカードのどこにカーソルを乗せても、親の.card:hoverが子の下線を選んで左から右へ描きます。ブログカードの続きを読むリンクやランディングページのCTAに合います。
.cta__underline {
transform: scaleX(0);
transform-origin: left;
transition: transform $duration $easing;
}
.card:hover .cta__underline,
.card:focus-visible .cta__underline {
transform: scaleX(1);
}
07アンカー配置のミニパネル
カードにanchor-nameを付け、CSSアンカーポジショニングでその下に小さな情報パネルをつなぐと、ホバー時にパネルがカードへ自動で吸い付いて現れます。在庫やスペックを先に見せたい商品一覧に向いています。
.card { anchor-name: --c7; }
.panel {
position: absolute;
position-anchor: --c7;
position-area: block-end;
position-try-fallbacks: flip-block;
opacity: 0;
transform: translateY(-6px);
transition: opacity $duration $easing, transform $duration $easing;
}
.card:hover ~ .panel,
.card:focus-visible ~ .panel {
opacity: 1;
transform: translateY(0);
}
08グラスモーフィズムブラー
半透明のカード背景にbackdrop-filter: blur()がかかっていて、ホバーするとぼかしと彩度がさらに深まり、光を受けたガラス面のように表情が明るくなります。ダーク背景の上のプレミアムカードやダッシュボードウィジェットに向いています。
.card {
background: rgba(255, 255, 255, .14);
backdrop-filter: blur(6px) saturate(120%);
transition: backdrop-filter $duration $easing, transform $duration $easing;
}
.card:hover,
.card:focus-visible {
backdrop-filter: blur(14px) saturate(160%);
background: rgba(255, 255, 255, .26);
transform: translateY(-4px);
}
09選択リングの描画
選べるカードにホバーしたり選択したりすると、縁を囲んだSVGのリングがstroke-dashoffsetで一周ぶん描かれて「選択済み」だと知らせます。プラン選択やラジオ形式のカード選択UIを締めくくる動きです。
.ring__rect {
fill: none;
stroke-dasharray: 500;
stroke-dashoffset: 500;
transition: stroke-dashoffset $duration $easing;
}
.card:hover .ring__rect,
.card:focus-visible .ring__rect {
stroke-dashoffset: 0;
}
どこで壊れるか — 落とし穴ひとつ
08番のグラスモーフィズムを最初にレンダリングしたとき、測定値が明らかにおかしいものでした。カード全体のぼかしが目には見えて変わっているのに、強度平均は0.0、動いたフレームは23コマ中0コマと出たのです。この計測はブラウザで実際に再生するのではなく、Web Animations APIでcurrentTimeをコマごとに強制的に動かしてスクリーンショットを撮る仕組みで、ヘッドレスChromeはbackdrop-filterだけが変わる合成レイヤーをこの方式では毎コマ塗り直してくれないようでした。同じkeyframesの中にtransform: translateY(-4px)をひとつ足し、ぼかしと一緒にカードがわずかに浮き上がるようにしただけで、同じ条件で強度平均53.9・動いたフレーム12コマという正常な値になりました。圧縮ファイルの合言葉はtvyct8zbで、この検証記録が残っていなければ08番は「動いていないカード」のまま残っていたはずです。ぼかしや彩度のように合成レイヤーだけにかかる値は、transformやopacityを添えてはじめて計測でも人の目でもはっきり動いて見えます。
アクセシビリティ
OSで「動きを減らす」設定を有効にしている方には、9個すべてでprefers-reduced-motion: reduceのときにトランジションと自動アニメーションを止めます。01・03・04・05・06・08・09はホバーやフォーカスが当たった瞬間、アニメーションなしでそのまま最終の見た目(画像が広がりきった・裏返った・ブラケットが点いた・バッジが座った・下線が引かれた・ぼかしが濃いまま固定・リングが一周描かれた状態)に切り替わるので、情報が欠けることはありません。02は傾きが遷移なしでそのまま適用され、07はパネルが遷移なしで現れたり消えたりします。マウスを乗せたときだけでなく、キーボードでフォーカスが移ったときや指で押したときも同じ最終状態になるよう、:hoverと:focus-visible(一部は:activeも)をひとつの規則にまとめています。カードのホバー表現をもっと見たい方はカードホバーアニメーション9選、ガラス質の質感だけをじっくり見たい方はグラスモーフィズム効果9選もあわせてどうぞ。
FAQ
02のゾーン別チルトは:has()に対応していないブラウザではどうなりますか?
:has()に対応していないブラウザではそのセレクタを含む規則自体がマッチしないため、カードは平らなまま止まります。レイアウトが崩れたりエラーが出たりしない安全な劣化で、最新のChrome・Safari・Firefoxはすでに対応しています。
07のアンカー配置パネルはすべてのブラウザで同じように表示されますか?
Chrome・Edgeでは表示されますが、この記事を書いている時点のFirefox・SafariはCSSアンカーポジショニングに対応しておらず、パネル自体が現れません。対応していないブラウザでもカード本体(画像・タイトル・価格)は普通に表示されるので、肝心な情報が欠けることはありません。
09の選択リングをReact版で使うときstateはどう管理しますか?
SelectRingCardコンポーネントは専用のstateを持たず、CSSの:hover・:focus-visible・:activeだけでリングを描きます。実際のフォーム選択のようにクリックしたあとも選択状態を保ちたい場合は、親コンポーネント側でaria-pressedと専用クラスをもうひとつ重ねて管理してください。