カードを持ち上げる・傾けるCSS|transformとperspectiveで奥行きを出すコピペ4種類

カードを持ち上げる・傾けるCSSの見本とコード

一覧に並んだカードは、マウスを乗せたときに少し持ち上げたり、奥へ傾けたりすると、「ここは押せる」と伝わりやすくなります。CSSの transform(形や位置を変える指定)と影を組み合わせるだけで作れます。

使いどころは、押すとページが移るカードです。動きは「押せる」の合図なので、押せないカードには付けません。傾きは12度くらいまで、持ち上げは8pxくらいまでにすると、文字を読む邪魔になりません。

この記事の要点

  • transform と影を組み合わせると、カードを持ち上げる・傾ける・裏返す動きがCSSだけで作れる(マウスに合わせて傾ける形だけ短いJSを足す)
  • 使いどころは、押すとページが移るカード。動きは「押せる」の合図なので、押せないものには付けない
  • スマホにはマウスを乗せる操作がないので、動きがなくても押せると分かる見た目にしておく。動きを減らす設定の人には動きを止める
トミー

連載「WEBの技術」の48回目です。
今回はカードの奥行き。派手にもできる技術なので、「どこまでにするか」を中心に書きます。

完成見本:奥行きの出し方4種類

見本は自動で、マウスを乗せた状態と離した状態をくり返します。3はマウスの位置の代わりに、自動で角度を変えています。

LIFT1. ふわっと持ち上がる一覧のカード全般に。いちばん使う形
TILT2. 奥へ傾く作品や商品を、少し特別に見せたいカードに
FOLLOW3. マウスの位置に合わせて傾くトップページで1枚だけ目立たせたいカードに
FLIP4. くるっと裏返る表は写真、裏は補足、と分けたいときに

※ 見本は読み上げソフトでは読まれないようにしています。動きを減らす設定にしている方には、止まって見えます。実際にマウスを乗せて試せる見本は、記事の後半にあります。

コピペ用のコード

CSSは、サイトのCSSファイルや、WordPressならテーマの「追加CSS」などに入れます。クラス名の頭の kd-3d は、サイトのほかのデザインと名前がぶつからないようにするための目印です。1のCSSの前半(カードの見た目)は、2と3でも使います。HTMLの photo.jpg と /menu/ は、自分の画像とリンク先に置きかえてください。

1. ふわっと持ち上がる

上へ少し動かす translateY(-8px) と、影を大きく・うすく広げる指定を同時に変えます。影が広がることで「机から浮いた」ように見えます。キーボードで選んだときも同じ動きにするため、:focus-visible にも同じ指定をします。

HTML
<a class="kd-3d-card kd-3d-lift" href="/menu/">
  <img src="photo.jpg" alt="" width="520" height="320">
  <span class="kd-3d-body">
    <span class="kd-3d-title">春の新メニュー</span>
    <span class="kd-3d-text">写真と短い説明を並べたカードです</span>
  </span>
</a>
CSS
/* 1〜3で使う:カードの見た目 */
.kd-3d-card {
  display: block;
  width: 260px;
  max-width: 100%;
  border-radius: 14px;
  background-color: #FFFFFF;
  box-shadow: 0 2px 6px rgba(16, 40, 58, 0.12);
  color: #10283A;
  text-decoration: none;
  overflow: hidden;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.kd-3d-card img {
  display: block;
  width: 100%;
  height: auto;
}
.kd-3d-body {
  display: block;
  padding: 14px 16px 16px;
}
.kd-3d-title {
  display: block;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.4;
}
.kd-3d-text {
  display: block;
  margin-top: 4px;
  font-size: 13px;
  line-height: 1.6;
  color: #33495B;
}
.kd-3d-card:focus-visible {
  outline: 3px solid #0284C7;
  outline-offset: 4px;
}
/* マウスを乗せた・キーボードで選んだとき */
.kd-3d-lift:hover,
.kd-3d-lift:focus-visible {
  transform: translateY(-8px);
  box-shadow: 0 18px 32px rgba(16, 40, 58, 0.18);
}
/* 動きを減らす設定の人には、影だけ変える */
@media (prefers-reduced-motion: reduce) {
  .kd-3d-lift:hover,
  .kd-3d-lift:focus-visible {
    transform: none;
  }
}

2. 奥へ傾く

perspective(800px) で「見ている人からの距離」を決め、rotateX(横の軸で回す=上下に傾く)と rotateY(縦の軸で回す=左右に傾く)で傾けます。影も傾いた向きにずらすと、奥行きが出ます。

HTML
<a class="kd-3d-card kd-3d-tilt" href="/menu/">
  <img src="photo.jpg" alt="" width="520" height="320">
  <span class="kd-3d-body">
    <span class="kd-3d-title">春の新メニュー</span>
    <span class="kd-3d-text">写真と短い説明を並べたカードです</span>
  </span>
</a>
CSS
.kd-3d-tilt:hover,
.kd-3d-tilt:focus-visible {
  transform: perspective(800px) rotateX(8deg) rotateY(-12deg);
  box-shadow: 14px 18px 30px rgba(16, 40, 58, 0.18);
}
@media (prefers-reduced-motion: reduce) {
  .kd-3d-tilt:hover,
  .kd-3d-tilt:focus-visible {
    transform: none;
  }
}

3. マウスの位置に合わせて傾く

傾ける角度をCSSの変数(--rx と --ry)にしておき、短いJSでマウスの位置から角度を入れます。カードの中心を0として、端へ行くほど大きく傾きます。マウスで操作していて、動きを減らす設定でないときだけ動かします。

HTML
<a class="kd-3d-card kd-3d-follow" href="/menu/">
  <img src="photo.jpg" alt="" width="520" height="320">
  <span class="kd-3d-body">
    <span class="kd-3d-title">春の新メニュー</span>
    <span class="kd-3d-text">写真と短い説明を並べたカードです</span>
  </span>
</a>
CSS
.kd-3d-follow {
  --rx: 0deg;
  --ry: 0deg;
  transform: perspective(800px) rotateX(var(--rx)) rotateY(var(--ry));
  transition: transform 0.15s ease-out, box-shadow 0.3s ease;
}
.kd-3d-follow:hover {
  box-shadow: 0 18px 32px rgba(16, 40, 58, 0.18);
}
JavaScript
// マウスで操作していて、動きを減らす設定でないときだけ動かす
function kdCanTilt() {
  if (!window.matchMedia('(hover: hover) and (pointer: fine)').matches) return false;
  return !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
}
if (kdCanTilt()) {
  document.querySelectorAll('.kd-3d-follow').forEach(function (card) {
    card.addEventListener('pointermove', function (e) {
      var r = card.getBoundingClientRect();
      var x = (e.clientX - r.left) / r.width - 0.5;  /* 左端 -0.5 〜 右端 0.5 */
      var y = (e.clientY - r.top) / r.height - 0.5;  /* 上端 -0.5 〜 下端 0.5 */
      card.style.setProperty('--ry', (x * 16) + 'deg');
      card.style.setProperty('--rx', (y * -16) + 'deg');
    });
    card.addEventListener('pointerleave', function () {
      card.style.setProperty('--rx', '0deg');
      card.style.setProperty('--ry', '0deg');
    });
  });
}

4. くるっと裏返る

表と裏の2枚を同じ場所に重ね、包んでいる要素ごと rotateY(180deg) で半回転させます。transform-style: preserve-3d(中の要素を3Dの空間に置く)と、backface-visibility: hidden(裏を向いた面を見せない)の2つがそろうと、裏返ったように見えます。裏のリンクにキーボードで進んだときも裏返るよう、:focus-within(中の要素が選ばれているとき)にも同じ指定をします。

HTML
<div class="kd-3d-flip">
  <div class="kd-3d-flip-in">
    <div class="kd-3d-face">
      <img src="photo.jpg" alt="" width="520" height="320">
      <span class="kd-3d-title">春の新メニュー</span>
    </div>
    <div class="kd-3d-face kd-3d-back">
      <span class="kd-3d-title">4月1日から</span>
      <span class="kd-3d-text">旬の野菜を使った3品です</span>
      <a href="/menu/">くわしく見る</a>
    </div>
  </div>
</div>
CSS
.kd-3d-flip {
  width: 260px;
  max-width: 100%;
  perspective: 1000px;
}
.kd-3d-flip-in {
  display: grid;
  transform-style: preserve-3d;
  transition: transform 0.6s ease;
}
.kd-3d-face {
  grid-area: 1 / 1;
  border-radius: 14px;
  background-color: #FFFFFF;
  box-shadow: 0 2px 6px rgba(16, 40, 58, 0.12);
  overflow: hidden;
  backface-visibility: hidden;
}
.kd-3d-face img {
  display: block;
  width: 100%;
  height: auto;
}
.kd-3d-face img + .kd-3d-title {
  padding: 14px 16px 16px;
}
.kd-3d-back {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  padding: 20px;
  background-color: #0369A1;
  color: #FFFFFF;
  transform: rotateY(180deg);
}
.kd-3d-back .kd-3d-text {
  color: #FFFFFF;
}
.kd-3d-back a {
  align-self: flex-start;
  margin-top: 8px;
  font-size: 14px;
  font-weight: 700;
  color: #FFFFFF;
}
.kd-3d-flip:hover .kd-3d-flip-in,
.kd-3d-flip:focus-within .kd-3d-flip-in {
  transform: rotateY(180deg);
}
@media (prefers-reduced-motion: reduce) {
  .kd-3d-flip-in {
    transition: none;
  }
}

仕組み:動かしているのは「見た目」だけ

transform は、まわりを押しのけない

margin でカードを上へずらすと、まわりの要素まで動いたり、並びが崩れたりすることがあります。transform は、MDNにあるとおり、文書の流れを崩さずに形と位置を変えます。カードが8px上がっても、下の行は動きません。

影が「高さ」を伝える

持ち上がったように見えるのは、位置より影のおかげです。机に置いた紙を持ち上げると、影はうすく、大きく、少し下に離れます。1では、影のぼかし(32px)と下へのずれ(18px)を大きくして、それを再現しています。

perspective は「目からの距離」

3Dの回転は、そのままでは平たく見えます。perspective は、見ている人の目と画面との距離を決める指定です。MDNでは、値が小さいほど奥行きが深くなる(ゆがみが強くなる)と説明されています。

書き方は2つあります。2と3の transform: perspective(800px) … は、そのカードだけに距離を付けます。4の perspective: 1000px はプロパティで、付けた要素の子に効きます。4では、外側の .kd-3d-flip に距離を付け、中の .kd-3d-flip-in を回しています。

裏返すときの3つの指定

4は、表と裏の2枚を grid-area: 1 / 1 で同じ場所に重ねています。裏は最初から rotateY(180deg) で裏向きにしておき、backface-visibility: hidden で見えなくしておきます。包んでいる要素ごと半回転すると、表が裏向きになって消え、裏が表向きになって見えます。

包んでいる要素の transform-style: preserve-3d がないと、中の2枚が平たくつぶれて、裏返りません。ここでつまずきやすい点があります。MDNによると、overflow: hidden や、1より小さい opacity などを同じ要素に付けると、preserve-3d が効かなくなります。角を丸く切りたいときは、今回のように面のほうに overflow: hidden を付けます。

数値を変えるとどうなるか

持ち上げる距離

2pxでは動いたことに気づきにくく、20pxでは跳ねたように見えます。カードの大きさにもよりますが、6〜10pxくらいが目安です。

影を変えるか、変えないか

同じ8pxでも、影がそのままだと「ずれた」だけに見えます。持ち上げるときは、影も一緒に変えます。

perspective の値

同じ35度の傾きで、perspective() の値(目からの距離)だけを変えました。

250pxでは手前がぐっと大きくなり、2000pxではほとんど平らに見えます。カードなら800〜1000pxくらいが自然です。

傾ける角度

30度まで傾けると、文字がななめになって読みにくくなります。カードの中の文字を読ませたいなら、10度前後までにします。

実際に試せる見本

ここの4枚は、コピペ用のコードそのままです。マウスを乗せる、またはキーボードのTabキーで選ぶと動きます。3はマウスで操作しているときだけ動きます。

スマホと、見る人への配慮

スマホでは、動きがない前提で作る

MDNによると、多くのスマホは「マウスを乗せる」操作ができない端末として扱われます(hover: none)。持ち上がる動きは、スマホではほとんど見えません。動きがなくても、影・角丸・写真とタイトルのまとまりで「押せるカード」と分かる見た目にしておきます。

3のJSは、(hover: hover) and (pointer: fine)(マウスのように細かく指せる道具で操作しているとき)だけ動かしています。指で触った位置に合わせて傾くと、スクロールのたびにカードが揺れるためです。

4の裏の情報は、気づかれないことがある

マウスを乗せないと裏が見えないので、スマホでは裏に気づかれないことがあります。裏には、見なくても困らない補足だけを置きます。値段や申し込みのリンクなど、大事なものは表に置きます。裏の文字はHTMLにあるので、読み上げソフトでは表と裏の両方が読まれます。

動きを減らす設定の人には止める

1・2・4のCSSには prefers-reduced-motion: reduce(動きを減らす設定)のときの指定を入れています。1と2は動きを止めて影だけ変え、4は回転の途中を見せずに切りかえます。3はJSの中で、この設定のときは動かさないようにしています。W3Cのアクセシビリティの基準(2.3.3、レベルAAA)でも、操作をきっかけにした動きは、欠かせないもの以外は止められるようにすることが求められています。

キーボードで選んだときも同じに

:hover だけだと、マウスを使わない人には変化が見えません。1と2は :focus-visible(キーボードで選んだとき)、4は :focus-within(中のリンクが選ばれたとき)にも同じ指定をしています。Chromeでも、Tabキーで裏のリンクに進むと裏返ることを確かめました。選んだ場所が分かる枠(outline)も消さずに残しています。

やりすぎ注意

ページの全部のカードを傾ける、スクロールしただけで傾く、大きく回転させる。どれも、読む人の目が文字より動きに引っぱられます。奥行きは「押せる」の合図にとどめます。

AIに頼むなら

Claudeなどの生成AIに頼むときは、動きの大きさと、スマホ・動きを減らす設定での扱いまで伝えると、そのまま使いやすいコードになります。

リンクになっているカードに、マウスを乗せると8px持ち上がり、影が広がる動きを付けて。
:focus-visible でも同じにして、outline は消さないで。
prefers-reduced-motion: reduce のときは移動を止めて影だけ変えて。transition は transform と box-shadow だけ。

AIが出したコードは、そのまま使う前に、次の3つを確かめます。

  • 持ち上げる距離と角度。20px以上動く、20度以上傾く、のように大きすぎないか。文字が読めるかを見ます
  • transition: all になっていないか。関係ない指定まで動いてしまうので、transform と box-shadow だけにします
  • 裏返す形で overflow: hidden の場所。preserve-3d と同じ要素に付いていると裏返りません。Tabキーでの操作と、動きを減らす設定も試します

KAKERUならこう使う/使わない

最後に、作り手としての判断です。

使う場面

  • ブログや制作実績の一覧。1の持ち上がる形を使います。押せることが伝わり、並びも崩れません
  • トップページで、1枚だけ目立たせたいカード。2か3を、その1枚だけに使います
  • 写真が主役で、補足が短いもの。スタッフ紹介の「ひとこと」のように、見なくても困らない情報なら4を使います

使わない場面

  • 押してもどこにも移らないカード。動くと押せそうに見えて、押しても何も起きない、という形になります
  • 料金表や比較表。傾くと数字が読みにくくなります
  • 値段や申し込みボタンを裏に置く4。スマホでは裏に気づかれないことがあるので、大事な情報は表に置きます
トミー

KAKERUなら、まず1の持ち上がる形から入れます。
傾ける・裏返すは、そのページで「ここだけ」と言える1か所に絞ると効きます。

まとめ

  • 持ち上げるなら translateY と影を一緒に変える。距離は6〜10pxくらい
  • 傾けるなら perspective で目からの距離を決め、rotateX・rotateY で10度前後まで
  • 裏返すなら preserve-3d と backface-visibility: hidden。overflow: hidden は面のほうに付ける
  • スマホでは動きが見えない前提で作り、動きを減らす設定の人には止める。:focus-visible にも同じ指定を
  • 動きは「押せる」の合図。押せないカードには付けない

まずは1のコードをコピーして、自分のサイトのカード一覧に入れてみてください。距離と影の数字を少し変えるだけで、印象が変わります。

参考にした資料

いずれも2026年11月8日に確認しました。ブラウザの対応状況は変わることがあります。最新の情報は各ページでご確認ください。

ふだんの制作の話や、作ったものの裏側は Instagram と X でも出しています。

関連記事

マウスを乗せると動くボタンをCSSだけで コピペで使える5種類と、スマホ・キーボードへの配慮
影の付け方をCSSで覚える。box-shadowの見本とコード
画像に乗せるとズームする動きをCSSだけで コピペで使える5種類と、まわりを崩さない書き方
動きが苦手な人への配慮をCSSで。ふつうの見え方と動きを減らす設定の見え方を並べた見本とコード
カードを持ち上げる・傾けるCSSの見本とコード

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

CAPTCHA


ABOUTこの記事をかいた人

25歳からWEBの世界に携わり、WEBデザインから始めて、今はWEBシステムまで一貫して制作しています。問題点を洗い出し、改善を提案する中で、WEB・グラフィック・動画・システムを縦断して提案するのが得意です。