UI/UX
投稿日:

カードUIの”選択肢整理” ─ ホバーで一枚だけ持ち上げる

こんにちは、株式会社ファストコーディングのBigViです。最近、夫と一緒に近所のから揚げ専門店に行きました。メニューが10種類もあって、迷いました。。でも写真が大きいやつが目に入って、それを選びました。おいしかったです。

先日、お客さまから「料金プランのカードが3つ並んでいるけど、どれを選べばいいか迷う」と相談がありました。カードのデザインは全部同じサイズ、同じ色。お客さまの担当者も「プランページの離脱率が高い」と困っていました。

私もよくあります。同じ見た目のカードが並んでいると、全部読まないと違いがわからないです。途中で面倒になって、ページを閉じてしまいます。。

そこで私が提案したのが「ホバーリフト」です。マウスを乗せたカードだけ、少しだけ持ち上がります。影が濃くなって、ほんの少し大きくなります(1.02倍)。他のカードは元のまま。「今これを見ていますよ」と教える感じです。

なぜ「少しだけ」がいいのか

「もっと大きくすればいいのでは?」と思うかもしれません。でも、やりすぎは逆効果です。

1.1倍まで大きくしたことがあります。クライアントに見せたら「なんか飛び出してきて怖い」と言われました。。1.02倍にしたら「あ、これはちょうどいい」と言ってくれました。

大事なのは「気づく」けど「驚かない」ことです。自分でマウスを動かしているから、ちょっと反応してくれると心地いい。でも大きすぎると「えっ」ってなります。。

影も同じです。いきなり黒い影が出ると重たい感じになります。薄い影がちょっとだけ広がる、これがちょうどいいです。

HTML構造

HTMLはシンプルです。普通のカードに、少しだけクラスを付けます。

<div class="card-group">
  <a href="/plan/light" class="card-item">
    <div class="card-icon">💡</div>
    <h3 class="card-title">ライト</h3>
    <p class="card-price"><span class="price-number">980</span>円/月</p>
    <ul class="card-features">
      <li>5ページまで</li>
      <li>メールサポート</li>
      <li>SSL対応</li>
    </ul>
    <span class="card-cta">詳しく見る →</span>
  </a>

  <a href="/plan/standard" class="card-item">
    <div class="card-icon">⭐</div>
    <h3 class="card-title">スタンダード</h3>
    <p class="card-price"><span class="price-number">2,480</span>円/月</p>
    <ul class="card-features">
      <li>20ページまで</li>
      <li>チャットサポート</li>
      <li>独自ドメイン</li>
    </ul>
    <span class="card-cta">詳しく見る →</span>
  </a>

  <a href="/plan/premium" class="card-item">
    <div class="card-icon">👑</div>
    <h3 class="card-title">プレミアム</h3>
    <p class="card-price"><span class="price-number">4,980</span>円/月</p>
    <ul class="card-features">
      <li>無制限</li>
      <li>電話+専任担当</li>
      <li>優先対応</li>
    </ul>
    <span class="card-cta">詳しく見る →</span>
  </a>
</div>

カードを<a>タグにしました。こうすると、カードのどこを押しても詳細ページに飛びます。<div>にしてボタンを中に置くやり方もありますが、全体が押せるほうが楽です。

CSSスタイル

ホバーリフトのCSSです。transformbox-shadowの変化だけで作れます。

.card-group {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  max-width: 960px;
  margin: 0 auto;
  padding: 32px 16px;
}

.card-item {
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 12px;
  padding: 32px 24px;
  text-align: center;
  text-decoration: none;
  color: inherit;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  will-change: transform;
}

.card-item:hover {
  transform: translateY(-4px) scale(1.02);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.10);
}

.card-item:focus-visible {
  transform: translateY(-4px) scale(1.02);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.10);
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}

.card-icon {
  font-size: 2rem;
  margin-bottom: 12px;
}

.card-title {
  font-size: 1.25rem;
  font-weight: 700;
  margin: 0 0 8px;
  color: #111827;
}

.card-price {
  font-size: 1rem;
  color: #6b7280;
  margin: 0 0 20px;
}

.price-number {
  font-size: 1.75rem;
  font-weight: 700;
  color: #111827;
}

.card-features {
  list-style: none;
  padding: 0;
  margin: 0 0 24px;
  font-size: 0.875rem;
  color: #4b5563;
}

.card-features li {
  padding: 6px 0;
  border-bottom: 1px solid #f3f4f6;
}

.card-features li:last-child {
  border-bottom: none;
}

.card-cta {
  display: inline-block;
  font-size: 0.875rem;
  font-weight: 600;
  color: #3b82f6;
}

/* アニメーション苦手な人向け */
@media (prefers-reduced-motion: reduce) {
  .card-item {
    transition: none;
  }
  .card-item:hover,
  .card-item:focus-visible {
    transform: none;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.10);
  }
}

/* スマホ対応 */
@media (max-width: 768px) {
  .card-group {
    grid-template-columns: 1fr;
    gap: 16px;
  }
}

ポイントを説明します。

translateY(-4px)で上に4px動かします。scale(1.02)でちょっとだけ大きくします。この2つを一緒にやると、「浮いた」みたいになります。

box-shadowも変えます。普通のときは0 1px 3pxの薄い影。ホバーしたら0 8px 24pxの広い影。影が広がると浮いている感じが出ます。

transitionは0.2秒にしました。最初0.1秒にしたら速すぎて、何が起きたかわからなかったです。。0.4秒だと遅くてもたつきます。0.2秒がちょうどよかったです。

will-change: transformも入れています。ブラウザに「この要素は動きますよ」って教えるやつです。GPUで処理してくれるから、カクカクしません。でも全部の要素に付けるとメモリを使いすぎるので、動く要素だけにしてください。

タッチ端末への対応

スマホにはマウスがないです。ホバーが使えない。なので別の方法が必要です。

1つ目は:focus-visible。CSSに書きました。キーボードで操作したとき、ホバーと同じ見た目になります。

2つ目はJSです。タッチしたときにちょっとだけ反応を出します。

document.addEventListener('DOMContentLoaded', function() {
  var cards = document.querySelectorAll('.card-item');
  var isTouchDevice = 'ontouchstart' in window;

  if (!isTouchDevice) return;

  cards.forEach(function(card) {
    card.addEventListener('touchstart', function() {
      this.classList.add('is-touched');
    }, { passive: true });

    card.addEventListener('touchend', function() {
      var el = this;
      setTimeout(function() {
        el.classList.remove('is-touched');
      }, 150);
    });
  });
});
.card-item.is-touched {
  transform: translateY(-2px) scale(1.01);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

タッチ端末では、持ち上がりを小さくしています。-2px1.01倍。スマホは画面が小さいので、大きく動くと邪魔です。

{ passive: true }はスクロールを邪魔しないためのオプションです。これがないと、タッチしたときスクロールが止まったりします。スマホはスクロールが大事なので、邪魔しちゃだめです。

A/Bテストでの効果

実際にこのホバーリフトを導入した案件で、A/Bテストをしました。テスト期間は2週間、対象は約3,000セッションです。

リフトなし(元のデザイン)だと、カード詳細への遷移率は18.2%でした。リフトありだと23.7%。約30%も上がりました。

あと、プランページにいる時間も変わりました。リフトなしだと平均42秒。リフトありだと31秒。短くなったのは、迷う時間が減ったからだと思います。早く選べるようになった。これはいい結果です。

この数字をクライアントに見せたら、すぐに「本番に入れましょう」になりました。「たったこれだけで変わるんですね」って言ってくれました。

prefers-reduced-motionについて

アニメーションが苦手な人もいます。CSSに@media (prefers-reduced-motion: reduce)を書いています。

OSの設定で「視差効果を減らす」をオンにしている人には、動きを止めます。transformをなしにして、影だけちょっと変えます。動きはないけど、見た目の違いはちゃんと出ます。

これはアクセシビリティです。WCAG 2.1の達成基準 2.3.3「インタラクションによるアニメーション」に沿った対応です。小さいことですが、やったほうがいいです。

まとめ

今回は、カードUIのホバーリフトについて書きました。やっていることはシンプルです。マウスを乗せたら、そのカードだけ少し持ち上がる。それだけです。

大事なことは3つです。

  • translateYscaleは控えめにする。-4pxと1.02倍がちょうどいい
  • transitionは0.2秒。速すぎず遅すぎない
  • タッチ端末とアクセシビリティの配慮を忘れない

A/Bテストでは遷移率が18.2%から23.7%に上がりました。CSSだけで作れる小さい変化ですが、ちゃんと効果がありました。

株式会社ファストコーディングでは、こういうUIの改善もやっています。カードだけじゃなくて、フォームとかCTAとか、いろいろお手伝いできます。もし気になったら、お問い合わせフォームから連絡してください。


※本記事は弊社外国人スタッフによる投稿です。言い回しや表現が不十分な個所がありますことご容赦いただきますようお願いいたします。