一見シンプルに見えるけれど、いざ実装しようとすると手間がかかったり、ちょっとした知識が必要だったりする。
そんなウェブ制作の「現場あるある」とその対処法を紹介していくシリーズ。
今回のテーマは、「背景がグラデーション(linear-gradient)の要素を、ホバー時にアニメーションさせる方法」です。
完成デモ1:疑似要素::beforeの利用
完成デモ2:カスタムプロパティ (@property) を利用
linear-gradient
に transition
は効かない
現場でよくあるのが、背景色がグラデーションになっている「ボタン」のホバーエフェクト。
指示書には「ホバーした時、背景の色はフワッと変化させてください!」みたいなメモが気軽にあるんだけど、実はCSSのlinear-gradient
にtransition
プロパティは効かない。。
そのため、transition
が効いているように「見せる」ために、CSSに一工夫加える必要があります。
主な手法としては、「疑似要素を利用する方法」と、「CSSカスタムプロパティ (@property) を活用する方法」が考えられます。
解決策1:疑似要素の opacity
を切り替える
一般的なテクニックです。
ボタンに ::before
や ::after
の疑似要素を追加し、背景全体を覆うように配置します。
通常時は opacity: 0
、ホバー時に opacity: 1
へアニメーションさせることで、あたかも linear-gradient
の背景色が滑らかに変化したかのように見せかけます。
デモ
HTML
<button>ボタン</button>
CSS ※要点のみ抜粋
button {
position: relative;
background: linear-gradient(90deg, #53C1FD, #3274E2);
overflow: hidden;
z-index: 1;
/* ホバー時用の背景色(非表示状態) */
&::before {
content: "";
opacity: 0;
position: absolute;
inset: 0;
background: #2759AD;
transition: opacity .3s;
z-index: -1;
}
/* ホバーで表示 */
@media (any-hover: hover) {
&:hover::before {
opacity: 1;
}
}
}
補足
HTMLのデモはスマホでも効果が確認出来るよう@media (any-hover: hover){}
指定をあえて解除しています。
アイコン付きボタンへの応用
ボタンデザインには、アイコンが含まれることも多いと思います。
アイコンも、CSSの疑似要素で実装されていることが多いため、ボタン本体の::before
/ ::after
は既にアイコンに使われている事も多いです。
このような場合は、ボタン内のテキスト部分をspan
等のタグで囲み、その要素に対して疑似要素を使えば干渉せずに実装可能です。
HTML
<button><span>ボタン</span></button>
CSS ※要点のみ抜粋
button {
position: relative;
display: grid;
grid-template-columns: 1fr auto 1fr;
align-items: center;
gap: 1em;
background: linear-gradient(90deg, #53C1FD, #3274E2);
overflow: hidden;
z-index: 1;
/* アイコンの反対側に同サイズの余白を生成(Gridの活用) */
&::before {
content: "";
}
/* アイコン */
&::after {
content: "";
display: block;
justify-self: end;
width: .8em;
aspect-ratio: 1/1;
mask: url(/img/ico_arrow.png) center center / contain no-repeat;
background-color: #fff;
}
/* ホバー時用の背景色(非表示状態) */
&>span::before {
content: "";
opacity: 0;
position: absolute;
inset: 0;
background: #2759AD;
transition: opacity .3s;
z-index: -1;
}
/* ホバーで表示 */
@media (any-hover: hover) {
&:hover>span::before {
opacity: 1;
}
}
}
解決策2:CSSカスタムプロパティ (@property
) を使う
比較的新しい方法として、CSSの @property
ルールを使ったアプローチがあります。こっちのほうがコードの記述量は抑えられてシンプルです。また見せかけではなく、ちゃんとtransition
が効きます。
方法としては、アニメーションさせたい色をカスタムプロパティとして定義し、ホバー時にその値を変更することで、グラデーションの色が transition
によって滑らかに変化します。
デモ
CSS カスタムプロパティ
@property --custom-color-1 {
syntax: "<color>";
inherits: false;
initial-value: #53C1FD;
}
@property --custom-color-2 {
syntax: "<color>";
inherits: false;
initial-value: #3274E2;
}
CSS ※要点のみ抜粋
.button {
box-sizing: border-box;
background: linear-gradient(90deg, var(--custom-color-1), var(--custom-color-2));
transition: --custom-color-1 .3s, --custom-color-2 .3s;
/* ホバーで背景色を変更 */
@media (any-hover: hover) {
&:hover {
--custom-color-1: #2759AD;
--custom-color-2: #2759AD;
}
}
}
通常のCSS変数ではダメみたい
仕組みだけ一見すると、@property
を使わずに通常のCSS変数(例:–custom-color-3、–custom-color-4;)でも実現できそうに思えますが、実際には色の変化がアニメーションせず、一瞬で切り替わってしまいました。
※ダメな事例:CSS変数の利用
button {
--custom-color-3: #53C1FD;
--custom-color-4: #3274E2;
background: linear-gradient(90deg, var(--custom-color-3), var(--custom-color-4));
transition: --custom-color-3 .3s, --custom-color-4 .3s;
@media (any-hover: hover) {
&:hover {
--custom-color-3: #2759AD;
--custom-color-4: #2759AD;
}
}
}
調べたところ、通常のCSS変数の定義は、ただの「文字列」なので、ブラウザはその値をアニメーションすることができないようです。
一方で、@property
で定義したカスタムプロパティは、単なる文字列ではなく「型情報」を持ちます。
今回の場合、 型のプロパティとしてcolor
を明示的に定義しているため、ブラウザは、その値が「色」であることを認識出来き、中間色を計算して、transition
に従ってアニメーションしてくれる……と解釈したが合ってるかな?
参照
- @property – CSS: カスケーディングスタイルシート | MDN
https://developer.mozilla.org/ja/docs/Web/CSS/@property - CSS Properties and Values API Level 1
https://drafts.css-houdini.org/css-properties-values-api/#at-property-rule
まとめ
以上、「CSSグラデーション(linear-gradient)の色をアニメーションで変化させる方法」でした。
linear-gradient
の背景色をホバーでアニメーションさせるには、少し工夫が必要です。
疑似要素の opacity
を利用する方法は、古くから使われているメジャーな手法で、安定した動作が期待できます。
@property
を利用する方法は、より宣言的で、コードもスッキリしますが、比較的新しい機能のため、ブラウザの対応状況に注意が必要です。といっても2025年現在、主要なブラウザでは対応済みなのでそこまで心配はいりません。
プロジェクトの要件や対象ブラウザに応じて、適切な方法を選択してください。