デザイナーさんがグラデーションのついたボタンの色をマウスホバー時に変化させる(しかもSVGのアイコン付きなのでさらに面倒さが倍増)という案を出してきて、実装に苦労したので備忘録として残しておきます。
background: linear-gradient には transition が効かないので、じわっと変化させようとしても背景色だけ一瞬で切り替わってしまいます。
そこで before 疑似要素を重ねて opacity を変化させることで transition を実現しています(JavaScript は使いたくなかった)。
デモ
まずはデモ。
ソースコード見ればすぐに真似できると思います。
コード
JavaScript は使わず CSS のみで実現しています。
html
<div class="container">
<button class="button">
<div class="text">ボタン</div>
<div class="icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path class="icon-path" d="M0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM294.6 135.1l99.9 107.1c3.5 3.8 5.5 8.7 5.5 13.8s-2 10.1-5.5 13.8L294.6 376.9c-4.2 4.5-10.1 7.1-16.3 7.1C266 384 256 374 256 361.7l0-57.7-96 0c-17.7 0-32-14.3-32-32l0-32c0-17.7 14.3-32 32-32l96 0 0-57.7c0-12.3 10-22.3 22.3-22.3c6.2 0 12.1 2.6 16.3 7.1z"/>
</svg>
</div>
</button>
</div>
SCSS
$primary-color: #066435;
$light-color: #0bb660;
$text-color: #fff;
$transition-time: 1s;
.container {
display: inline-block;
}
.text {
position: relative;
}
.button {
position: relative;
width: 300px;
color: $text-color;
border: solid 1px $primary-color;
height: 36px;
border-radius: 18px;
display: flex;
justify-content: center;
align-items: center;
transition: all $transition-time;
&::before {
content: '';
background: linear-gradient(90deg, $light-color, $primary-color);
border-radius: 18px;
position: absolute;
width: 100%;
height: 100%;
transition: all $transition-time;
}
path {
transition: all $transition-time;
&.icon-path {
fill: $text-color;
}
}
&:hover {
color: $primary-color;
&::before {
opacity: 0;
}
path.icon-path {
fill: $primary-color;
}
}
.icon {
position: absolute;
top: 8px;
right: 9px;
width: 18px;
height: 18px;
}
}
解説
まず hover したときの状態のスタイルを定義しておきます。
background-color は transparent なので省いています。
.button {
border: solid 1px $primary-color;
}
次にその上に before 疑似要素をかぶせます。
これが hover していないときの表示なるので、ここにグラデーションの背景を設定します。
&::before {
background: linear-gradient(90deg, $light-color, $main-color);
}
あとは hover 時に before 疑似要素を消すようにします。
&:hover {
&::before {
opacity: 0;
}
}
ボタン内に配置した SVG の色も同時に変化させるために、path 要素の fill も CSS で設定します。
アイコンは配色がボタンと逆になるので、一見クラス名と色名が一致していないように見えますが、これで合っています。
path {
&.icon-path {
fill: $text-color;
}
}