単語カードのようにめくれるボタン

モーション種別
  • クルッと系
コンポーネント
  • ボタン
トリガー
  • ホバー
技術
  • CSSのみ

概要

単語カードのように、ホバーするとテキストがめくれて、次のテキストが表示されるボタンです。

遊び心があり、「これどう動くのだろう?」って気になってついホバーしたくなるアニメーションです。

画面に向かって縦方向を軸に回転するため、横幅の確保が必要です。そのため、あまり長い文字には適してはいないです。

FAQの質問と回答を「一問一答」形式で出したり、「Next」→「ページ2へ」のような補足説明をつけたりという運用がいいかと思います。

実装については、buttonタグの中にspanタグを2つ、リング用のSVGを用意し、重ね合わせています。

ホバーすることで1つ目のspanタグがrotate3dによって立体的に回転し、それと同時にz-indexを操作し、2つ目のspanタグが前に出てくるように指定います。

リングはSVGで「C」のように完全な円にせず、位置を調整することでspanタグ2つを繋げているように見せています。

ホバー時には、このリングは長さと回転で動きを調整することで、めくる時に極力不自然な繋ぎ目が見えにくいようにしています。

コードサンプル

HTML

<button class="button" type="button" aria-label="more">
  <span class="button__cover">お問い合わせ</span>
  <span class="button__more">送信フォームへ</span>
  <svg class="ring" viewBox="0 0 28 28" width="25" height="25" aria-hidden="true">
    <circle cx="14" cy="14" r="12.5" pathLength="1" />
  </svg>
</button>

CSS

/* ボタン本体:重ね合わせの土台 */
.button {
  --duration: 0.5s;
  --ease: ease-in-out;

  position: relative;
  display: inline-grid;
  grid-template-areas: "button";
  width: fit-content;
  border: 0;
  background: transparent;
  cursor: pointer;
  isolation: isolate; /* z-indexの干渉を防止 */
}

/* カードの共通見た目 */
.button__cover,
.button__more {
  grid-area: button;
  position: relative;
  display: inline-block;
  padding: 0.4em 0.8em;
  width: 100%;
  font-size: 1rem;
  color: #f4f5f9;
  text-align: center;
  line-height: 1;
  transform-origin: -3px -3px;
  transition: transform var(--duration) var(--ease);
}

/* 疑似要素(黒い丸) */
.button__cover::before,
.button__more::before {
  content: "";
  position: absolute;
  inset-inline-start: 5px;
  inset-block-start: 5px;
  width: 5px;
  height: 5px;
  background: #333333;
  border-radius: 50%;
}

/* 初期状態:coverが上、moreが斜め下にオフセット */
.button__cover {
  background: #08254f;
  z-index: 2;
}
.button__more {
  background: #7d8797;
  transform: translate(4px, -4px);
  z-index: 1;
}

/* リング(SVG) */
.ring {
  position: absolute;
  inset-block-start: -12.5px;
  inset-inline-start: -12.5px;
  rotate: 40deg;
  z-index: 3;
}
.ring circle {
  fill: none;
  stroke: #F7DAB0;
  stroke-width: 2;
  stroke-dasharray: 1;
  stroke-dashoffset: 0.17;
  stroke-linecap: butt;
  shape-rendering: geometricPrecision;
}

/* =========================
   Hover(ホバー対応デバイスのみ)
   ========================= */
@media (any-hover: hover) {
  .button:hover .button__cover {
    transform: rotate3d(0, -1, 0.5, 360deg) translate(4px, -4px);
    z-index: 1;
  }
  .button:hover .button__more {
    transform: translate(0);
    z-index: 2;
  }
  .button:hover .ring {
    animation: ringRotate var(--duration) var(--ease);
  }
  .button:hover .ring circle {
    animation: ringRotateCircle var(--duration) var(--ease);
  }
}

/* =========================
   Focus-visible(まとめて)
   ========================= */
.button:focus-visible {
  outline: none;
}
.button:focus-visible .button__cover {
  transform: rotate3d(0, -1, 0.5, 360deg) translate(4px, -4px);
  z-index: 1;
}
.button:focus-visible .button__more {
  transform: translate(0);
  z-index: 2;
}
.button:focus-visible .ring {
  animation: ringRotate var(--duration) var(--ease);
}
.button:focus-visible .ring circle {
  animation: ringRotateCircle var(--duration) var(--ease);
}

/* アニメーション */
@keyframes ringRotate {
  0% {
    rotate: 25deg;
  }
  100% {
    rotate: 40deg;
  }
}
@keyframes ringRotateCircle {
  0% {
    stroke-dashoffset: 0.12;
  }
  100% {
    stroke-dashoffset: 0.17;
  }
}

/* 低モーション環境の配慮 */
@media (prefers-reduced-motion: reduce) {
  .button__cover,
  .button__more {
    transition: none;
  }
  .button:focus-visible .ring,
  .button:focus-visible .ring circle {
    animation: none;
  }
  @media (any-hover: hover) {
    .button:hover .ring,
    .button:hover .ring circle {
      animation: none;
    }
  }
}

お問い合わせ Contact

制作のご依頼やその他ご相談は、お問い合わせフォームにて受け付けております。

お問い合わせフォームへ