【HTMLとCSSのみ】実務でよく使いそうなボタンホバーアニメーションについてまとめてみる(備忘録)

今回は、ボタンリンクのホバーアニメーションについて、いろんなWebデザインギャラリーを見て、使われているもの中心に紹介していければと思います。

独断と偏見で選んでいること、実装方法も正攻法とは限りませんので、予めご了承ください。

なお、Webデザインギャラリーについては、定期的に見ていますので、そこで新しいボタンリンクのホバーアニメーションを発見しましたら、本記事に追加していければと考えています。

それでは実装に入っていきます。

HTMLの記述について

<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="robots" content="index, follow" />
  <title>CSSのみでよく使いそうなボタンホバーアニメーション(備忘録)</title>
  <link rel="stylesheet" href="../reset.css">
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <main>
    <section>
      <div class="ly_inner">
        <h2>枠線から塗りつぶしに</h2>
        <div class="md_textblock">
          <p><a href="" class="el_btn01">ボタンテキスト</a></p>
        </div>
      </div>
      <div class="ly_inner">
        <h2>影がつく</h2>
        <div class="md_textblock">
          <p><a href="" class="el_btn02">ボタンテキスト</a></p>
        </div>
      </div>
      <div class="ly_inner">
        <h2>丸い装飾が横線に</h2>
        <div class="md_textblock">
          <p><a href="" class="el_btn03">ボタンテキスト</a></p>
        </div>
      </div>
      <div class="ly_inner">
        <h2>点線の円が回転している</h2>
        <div class="md_textblock">
          <p><a href="" class="el_btn04">ボタンテキスト</a></p>
        </div>
      </div>
      <div class="ly_inner">
        <h2>矢印の装飾がスライド</h2>
        <div class="md_textblock">
          <p><a href="" class="el_btn05">ボタンテキスト<span class="arrow01"></span></a></p>
        </div>
      </div>
      <div class="ly_inner">
        <h2>円の周りに薄い円がひろがる</h2>
        <div class="md_textblock">
          <p><a href="" class="el_btn06">ボタン</a></p>
        </div>
      </div>
      <div class="ly_inner">
        <h2>アイコンとテキストが同時に色変更</h2>
        <div class="md_textblock">
          <p><a href="" class="el_btn07">ボタンテキスト</a></p>
        </div>
      </div>
      <div class="ly_inner">
        <h2>テキストが上部にスライドしつつ色変更</h2>
        <div class="md_textblock">
          <p><a href="" class="el_btn08">
              <span>
                <span>ボタンテキスト</span>
                <span>ボタンテキスト</span>
              </span></a></p>
        </div>
      </div>
      <div class="ly_inner">
        <h2>横線が右へスライドして新たな横線が現れる</h2>
        <div class="md_textblock">
          <p><a href="" class="el_btn09">ボタンテキスト</a></p>
        </div>
      </div>
      <div class="ly_inner">
        <h2>下から丸い背景が覆われる</h2>
        <div class="md_textblock">
          <p><a href="" class="el_btn10"><span>ボタンテキスト</span></a></p>
        </div>
      </div>
      <div class="ly_inner">
        <h2>下から上へ背景と文字がスライドして現れる</h2>
        <div class="md_textblock">
          <p>
            <a href="" class="el_btn11">
              <span class="btn_textWrap">
                <span class="btn_content">
                  <span class="btn_text">ボタンテキスト</span>
                </span>
                <span class="btn_content" aria-hidden="true" data-nosnippet="">
                  <span class="btn_text">ボタンテキスト</span>
                </span>
              </span>
            </a>
          </p>
        </div>
      </div>
      <div class="ly_inner">
        <h2>上下の線がくっつく</h2>
        <div class="md_textblock">
          <p><a href="" class="el_btn12">ボタンテキスト</a></p>
        </div>
      </div>
    </section>
  </main>
</body>

</html>

構造はシンプルですので、特に解説は省略させていただきます。

CSSの記述について

@charset "utf-8";

/* ==========================
  初期設定
========================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}
img {
  width: 100%;
}

/* ==========================
  コンテンツの中身
========================== */
.ly_inner {
  width: 100%;
  max-width: 1080px;
  margin: 40px auto;
  padding: 20px;
  background-color: #fff;
}
.ly_inner h2 {
  font-size: 130%;
  font-weight: bold;
  margin-bottom: 30px;
}
.md_textblock > * + * {
  margin-top: 10px;
}

/* ==========================
  枠線から塗りつぶしに
========================== */

.el_btn01 {
  display: grid;
  place-content: center;
  width: 240px;
  max-width: 100%;
  min-height: 44px;
  border-radius: 40px;
  border: 1px solid #222;
  color: #222;
  transition: background-color 0.4s cubic-bezier(0.19, 1, 0.22, 1),
    border 0.4s cubic-bezier(0.19, 1, 0.22, 1),
    color 0.4s cubic-bezier(0.19, 1, 0.22, 1);
}
.el_btn01:hover {
  color: #fff;
  background-color: #5e2525;
  border: 1px solid #5e2525;
}

/* ==========================
  影がつく
========================== */

.el_btn02 {
  display: grid;
  place-content: center;
  width: 240px;
  max-width: 100%;
  min-height: 44px;
  border-radius: 40px;
  color: #fff;
  background-color: #c78181;
  transition: filter 0.4s cubic-bezier(0.19, 1, 0.22, 1),
    -webkit-filter 0.4s cubic-bezier(0.19, 1, 0.22, 1);
}
.el_btn02:hover {
  -webkit-filter: drop-shadow(0px 6px 13px rgba(0, 0, 0, 0.15));
  filter: drop-shadow(0px 6px 13px rgba(0, 0, 0, 0.15));
}

/* ==========================
  丸い装飾が横線に
========================== */

.el_btn03 {
  position: relative;
  display: grid;
  place-content: center;
  width: 240px;
  max-width: 100%;
  min-height: 44px;
  border-radius: 40px;
  color: #fff;
  background-color: #5e2525;
  transition: 0.4s ease-out;
}
.el_btn03::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: 32px;
  margin: auto;
  display: block;
  width: 5px;
  height: 5px;
  background-color: #fff;
  border-radius: 5px;
  transition: 0.4s ease-out;
}
.el_btn03:hover {
  background-color: #c78181;
}
.el_btn03:hover::after {
  width: 16px;
  height: 1px;
  right: 24px;
}

/* ==========================
  点線の円が回転している
========================== */

.el_btn04 {
  position: relative;
  display: grid;
  place-content: center;
  width: 240px;
  max-width: 100%;
  min-height: 44px;
  color: #fff;
  background-color: #c78181;
  transition-duration: 0.5s;
  transition-timing-function: cubic-bezier(0.075, 0.82, 0.165, 1);
  border: 2px solid #c78181;
}
.el_btn04::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  margin: auto;
  right: 16px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border-width: 1px;
  border-style: dashed;
  border-color: #fff;
  -webkit-animation: rotateAnime 8s linear infinite;
  animation: rotateAnime 8s linear infinite;
  transition-duration: 0.5s;
  transition-timing-function: cubic-bezier(0.075, 0.82, 0.165, 1);
}
.el_btn04::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  margin: auto;
  right: 25px;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 5px 0 5px 10px;
  border-color: transparent transparent transparent #fff;
  transition-duration: 0.5s;
  transition-timing-function: cubic-bezier(0.075, 0.82, 0.165, 1);
}
.el_btn04:hover {
  border: 2px solid #222;
}
.el_btn04:hover::before {
  width: 32px;
  height: 32px;
}
@keyframes rotateAnime {
  0% {
    transform: rotate(0);
  }
  100% {
    transform: rotate(360deg);
  }
}

/* ==========================
  矢印の装飾がスライド
========================== */

.el_btn05 {
  position: relative;
  display: grid;
  place-content: center;
  width: 240px;
  max-width: 100%;
  min-height: 44px;
  border-radius: 40px;
  background-color: #5e2525;
  color: #fff;
  transition: 0.4s cubic-bezier(0.19, 1, 0.22, 1);
}
.el_btn05 .arrow01 {
  overflow: hidden;
  display: block;
  position: absolute;
  width: 10px;
  height: 10px;
  right: 24px;
  top: 50%;
  transform: translateY(-50%);
}
.el_btn05 .arrow01::before,
.el_btn05 .arrow01::after {
  content: " ";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: url(arrow01.svg) no-repeat;
}
.arrow01::after {
  transform: translate(-100%, 100%);
  transition-delay: 0s;
}
.el_btn05:hover .arrow01::after {
  opacity: 1;
  transform: translate(0, 0);
  transition-duration: 0.7s;
  transition-timing-function: cubic-bezier(0.075, 0.82, 0.165, 1);
  transition-delay: 0.2s;
}
.el_btn05:hover .arrow01::before {
  transition-duration: 0.4s;
  transition-timing-function: cubic-bezier(0.075, 0.82, 0.165, 1);
  transform: translate(100%, -100%);
}

/* ==========================
  円の周りに薄い円がひろがる
========================== */

.el_btn06 {
  margin-left: 40px;
  position: relative;
  display: grid;
  place-content: center;
  width: 120px;
  height: 120px;
  border-radius: 50%;
  background-color: #5e2525;
  color: #fff;
  transition: 0.4s cubic-bezier(0.19, 1, 0.22, 1);
  z-index: 1;
}
.el_btn06::after {
  content: "";
  display: block;
  position: absolute;
  background: #5e2525;
  opacity: 0.07;
  width: 120px;
  height: 120px;
  z-index: -1;
  border-radius: 50%;
  top: 50%;
  right: 50%;
  transform: translateX(50%) translateY(-50%);
  transition-duration: 0.3s;
}
.el_btn06:hover::after {
  transform: translateX(50%) translateY(-50%) scale(1.5);
}

/* ==========================
  アイコンとテキストが同時に色変更
========================== */

.el_btn07 {
  position: relative;
  display: grid;
  place-content: center;
  padding-left: 32px;
  width: 240px;
  max-width: 100%;
  min-height: 44px;
  border-radius: 40px;
  color: #fff;
  background-color: #5e2525;
  transition: 0.4s cubic-bezier(0.19, 1, 0.22, 1);
  border: 1px solid #fff;
}
.el_btn07::before {
  content: "";
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  left: 56px;
  width: 16px;
  height: 14px;
  background: url(mail01.svg) no-repeat;
  transition: 0.4s cubic-bezier(0.19, 1, 0.22, 1);
}
.el_btn07:hover {
  background-color: #fff;
  color: #5e2525;
  border: 1px solid #5e2525;
}
.el_btn07:hover::before {
  background: url(mail01_hover.svg) no-repeat;
}

/* ==========================
  テキストが上部にスライドしつつ色変更
========================== */

.el_btn08 {
  display: grid;
  place-content: center;
  width: 240px;
  max-width: 100%;
  min-height: 44px;
  border-radius: 40px;
  background-color: #5e2525;
  border: 1px solid #5e2525;
  color: #fff;
  transition: background-color 0.4s cubic-bezier(0.19, 1, 0.22, 1),
    border 0.4s cubic-bezier(0.19, 1, 0.22, 1),
    color 0.4s cubic-bezier(0.19, 1, 0.22, 1);
}
.el_btn08 > span {
  display: flex;
  flex-direction: column;
  height: 1em;
  line-height: 1;
  position: relative;
  overflow: hidden;
}
.el_btn08 > span > span {
  transition: transform 0.5s cubic-bezier(0.215, 0.61, 0.355, 1) 0s;
}
.el_btn08:hover {
  background-color: #fff;
  border: 1px solid #5e2525;
  color: #5e2525;
}
.el_btn08:hover > span > span {
  transform: translateY(-1em);
}

/* ==========================
  横線が右へスライドして新たな横線が現れる
========================== */

.el_btn09 {
  position: relative;
  display: grid;
  place-content: center;
  width: 240px;
  max-width: 100%;
  min-height: 44px;
  border-radius: 40px;
  color: #fff;
  background-color: #c78181;
  transition: 0.4s cubic-bezier(0.19, 1, 0.22, 1);
}
.el_btn09::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: 2rem;
  margin: auto;
  width: 1rem;
  height: 2px;
  background-color: #fff;
  background-color: var(--btn-clr-main, #fff);
  transition: all 0.4s ease-out;
  transition: var(--transit-default, all 0.4s ease-out);
}
.el_btn09:hover {
  color: #fff;
  background-color: #5e2525;
}
.el_btn09:hover::after {
  animation: arrowLine 0.8s cubic-bezier(0.5, -0.1, 0.5, 1) forwards;
}
@keyframes arrowLine {
  0% {
    transform: scale(1, 1);
    transform-origin: 100% 0;
  }
  50% {
    transform: scale(0, 1);
    transform-origin: 100% 0;
  }
  50.1% {
    transform: scale(0, 1);
    transform-origin: 0 0;
  }
  100% {
    transform: scale(1, 1);
    transform-origin: 0 0;
  }
}

/* ==========================
  下から丸い背景が覆われる
========================== */

.el_btn10 {
  position: relative;
  overflow: hidden;
  display: grid;
  place-content: center;
  width: 240px;
  max-width: 100%;
  min-height: 44px;
  border-radius: 40px;
  color: #fff;
  background-color: #c78181;
}
.el_btn10::after {
  content: "";
  width: 240px;
  height: 140px;
  position: absolute;
  left: 50%;
  transform: translate(-50%);
  bottom: -173px;
  background-color: #5e2525;
  border-radius: 50% 50% 0 0;
  transition: 1s;
}
.el_btn10 span {
  position: relative;
  z-index: 1;
}
.el_btn10:hover::after {
  width: 300px;
  height: 300px;
}

/* ==========================
  下から上へ背景と文字がスライドして現れる
========================== */

.el_btn11 {
  position: relative;
  overflow: hidden;
  display: grid;
  place-content: center;
  width: 240px;
  max-width: 100%;
  min-height: 44px;
  border-radius: 40px;
  color: #fff;
  transition: background-color 0.4s cubic-bezier(0.19, 1, 0.22, 1),
    border 0.4s cubic-bezier(0.19, 1, 0.22, 1),
    color 0.4s cubic-bezier(0.19, 1, 0.22, 1);
}

.el_btn11::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  transition-property: transform;
  background-color: #c78181;
  transition-duration: 0.6s;
  border-radius: 40px;
}
.el_btn11::after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  transition-property: transform;
  transition-duration: 0.6s;
  background-color: #5e2525;
  border-radius: 40px;
  transform: translateY(101%);
}
.el_btn11:hover::after {
  transform: translateY(0);
}
.el_btn11 .btn_textWrap {
  position: relative;
  z-index: 3;
  display: flex;
  overflow: hidden;
}
.el_btn11 .btn_content {
  transition-property: transform;
  transition-duration: 0.6s;
  transform: translateY(0);
  visibility: visible;
}
.el_btn11:hover .btn_content {
  transform: translateY(-100%);
}
.el_btn11 .btn_content:nth-of-type(2) {
  position: absolute;
  left: 0;
  top: 100%;
}
.el_btn11 .btn_text {
  transition-property: transform;
  transition-duration: 0.6s;
}

/* ==========================
  上下の線がくっつく
========================== */

.el_btn12 {
  position: relative;
  display: grid;
  place-content: center;
  width: 240px;
  max-width: 100%;
  min-height: 120px;
  border-radius: 40px;
  color: #222;
  transition: background-color 0.4s cubic-bezier(0.19, 1, 0.22, 1),
    border 0.4s cubic-bezier(0.19, 1, 0.22, 1),
    color 0.4s cubic-bezier(0.19, 1, 0.22, 1);
}
.el_btn12::before {
  position: absolute;
  content: "";
  left: 0;
  display: block;
  width: 100%;
  border: 0 solid #222;
  transition: height 0.6s cubic-bezier(0.19, 1, 0.22, 1);
  border-width: 1px;
  top: 0;
  border-bottom-width: 0;
  border-radius: 1rem 1rem 0 0;
  height: 1.5rem;
}
.el_btn12::after {
  position: absolute;
  content: "";
  left: 0;
  display: block;
  width: 100%;
  border: 0 solid #222;
  transition: height 0.6s cubic-bezier(0.19, 1, 0.22, 1);
  border-width: 1px;
  bottom: 0;
  border-top-width: 0;
  border-radius: 0 0 1rem 1rem;
  height: 1.5rem;
}
.el_btn12:hover::before {
  height: 50%;
}
.el_btn12:hover::after {
  height: 50%;
}

枠線から塗りつぶしに

ポイントは、hover前と後で、borderを設定しておくことです。これを設定していないと枠線のずれが生まれてしまいます。

影がつく

こちらはdrop-shadowの使い方が分かれば対応できると思いますので、解説は省略します。

丸い装飾が横線に

疑似要素で丸をつくって、hover時に横線に変化させています。

点線の円が回転している

keyframesを使って回転アニメーションをつけています。

矢印の装飾がスライド

空タグを作って、overflowをhiddenにしてはみ出さないよう設定した上で、疑似要素のbeforeとafterに画像を設定して、hover前後で動きの設定をしています。

円の周りに薄い円がひろがる

疑似要素に薄い円を作って、hover時に広がるように設定しています。z-indexの値の設定にも注意すれば比較的かんたんに実装できます。

アイコンとテキストが同時に色変更

疑似要素にsvg画像を設定しています。hover時に色変更する場合は、別途別の色のsvg画像を用意する必要があります。

ただ、こちらについては、変更に強いコードとは言えませんので、疑似要素よりは、html上にsvgコードを記載して、fillなどのCSSプロパティを使って実装するほうが望ましいかも知れません。

テキストが上部にスライドしつつ色変更

原理はさきほど紹介した「矢印の装飾がスライド」と同様ですので、解説は省略します。

横線が右へスライドして新たな横線が現れる

これは見た目は新しい横線が現れているように見えますが、実際は伸び縮みしているだけです。

animationのkeyframesを使って、transformのscaleの数値でコントロールしています。

下から丸い背景が覆われる

疑似要素で上が丸い形のシェイプを作り、hover時に拡大させるとかんたんに実装できます。

下から上へ背景と文字がスライドして現れる

hover時に現れる背景については、hover前はtransformのtranslateY(101%)を指定して隠しています。あとはhover時に0%にすれば実装できます。
テキストについては、「矢印の装飾がスライド」と原理は同様ですので解説は省略します。

上下の線がくっつく

ボタンといっていいのか迷いましたが、紹介します。疑似要素にborderと高さを指定することで、見切れた見た目にすることができます。

あとはhover時にbeforeとafterを使って、高さを変えてあげることで、線がをくっつかせるアニメーションが実現できます。

以上ですが、今後も実務で使えそうなボタンのホバーアニメーションを見つけましたら随時更新していきたいと思います。