【背景依存なし!】CSSのborderで円環グラデーションを実装する方法
こんにちは、こじ(@kojiWebCode)です。
先日、グラデーションの円環を実装する場面がありました。
少し悩んだので共有します。その時の私の悩みは以下のようなものでした。
◆ borderをグラデーションにするにはborder-imageを使えばいいことがわかったけど、border-radiusが効かない…。
◆ 円環は実装できたけど背景を塗りつぶす方法しかないのかな…。背景が透過した円環を作りたい…。
こんなお悩みを持つ方向けに解説します。円環でなくとも、角丸のグラデーションborderも同様に実装できるので、ぜひ参考にしていただければと思います。
CSSのborderで円環(角丸)グラデーションを表現するのが難しい理由
border-imageにborder-radiusが効かない
円環、というより角丸のborderグラデーションを表現しようとしたときに直面する問題がこれかと思います。私もまさにそうでした。
border-imageを使えばグラデーションにできるのか…。じゃあ、これにborder-radiusを…って角丸にならない!!
背景透過にするのが難しい
現状、borderで実装するのは難しいとわかりました。
そこで、擬似的にグラデーションのborderを表現しようと試みます。
イメージとしては、以下の方法です。
グラデーションの円を作ったうえで、円内部を塗りつぶして擬似的に円環に見せるという方法です。
この方法の問題点は、円内部を塗りつぶす故に透過にならないことです。
背景が単色塗りつぶしの場合であれば問題ないですが、背景画像を透過させたい場合には使えません。
円環(角丸)グラデーションを表現する2つの方法
【おすすめ】背景透過の円環グラデーション
<div class="wrapper">
<div class="circle-grad"></div>
</div>
.wrapper {
text-align: center;
padding: 200px 0;
background-color: #1D1E22;
}
/* ここから */
.circle-grad {
display: inline-block;
width: 200px;
aspect-ratio: 1 / 1;
position: relative;
&::before {
content: "";
position: absolute;
inset: 0;
border-radius: 50%;
border: 5px solid transparent;
background: linear-gradient(90deg, rgba(226, 207, 255, 1), rgba(251, 253, 191, 1)) border-box;
// FireFox
mask-image: linear-gradient(#fff 0 0), linear-gradient(#fff 0 0);
mask-clip: padding-box, border-box;
mask-composite: exclude;
// それ以外の主要ブラウザ
-webkit-mask-image: linear-gradient(#fff 0 0), linear-gradient(#fff 0 0);
-webkit-mask-clip: padding-box, border-box;
-webkit-mask-composite: destination-out;
}
}
この方法では、2種類のマスクを使いそれらの差分を表示させることで円環を表現します。
順番に解説していきます。
まず、borderを任意の太さで、透明で指定します。
次に、backgroundでグラデーションを作ります。この時backgrouned-clip: border-box;
を指定しないと、以下のようにborderの部分だけグラデーションが切れてしまうので、忘れずに指定しておきましょう。
1つ目のマスクは、padding-boxを指定してborderの内周より内側の領域をカバーしています。
2つ目のマスクは、border-boxを指定してborderの外周より内側の領域をカバーしています。
そして、mask-compostie: exclude;
を指定することで、それら2つの領域で重なっている部分が除外されます。つまり、borderの領域だけ背景色が残り、それ以外の内部の領域は抜き取られた状態になります。
このようにして、円環グラデーションを実装することができます。
注意点として、
maskに関するプロパティの中には、すべてのブラウザでサポートされていないものもあります。今回の場合、mask-imageやmask-clip、mask-compositeに関しては、現状Firefoxでしかサポートされていません。そこで、それ以外の主要ブラウザで動作するように、以下を追加します。
-webkit-mask-image: linear-gradient(#fff 0 0), linear-gradient(#fff 0 0);
-webkit-mask-clip: padding-box, border-box;
-webkit-mask-composite: destination-out;
ちなみに、mask-imageのlinear-gradientの中身は任意です。
mask-clipの順番ですが、border-box, padding-boxのように順番を逆にした場合、Firefoxでは問題ないですが、webkit系は、-webkit-mask-composite: source-out; とする必要があります(もしくは、xorでもよさそうです)。webkit系の場合、マスクのレイヤーに気を付ける必要があります。
-webkit-mask-image: linear-gradient(#fff 0 0), linear-gradient(#fff 0 0);
-webkit-mask-clip: border-box, padding-box;
-webkit-mask-composite: source-out; // xorでもOK!
-webkit-mask-compositeについての詳細は、こちら。
補足ですが、要素自身ではなく、擬似要素に対してスタイルを当てているのは理由があります。
要素内にコンテンツを含む場合、マスクを適用すると内部が見えなくなってしまいます。
コンテンツの無いものであれば、要素自身にスタイルを当てても問題ありませんが、ボタンのようにテキストが内部に入る場合には、擬似要素を使ってスタイルを当てるのが良さそうです。
【簡単】背景塗りつぶしの円環グラデーション
子要素で塗りつぶす
親子関係を作って、子要素で円内部を塗りつぶす方法です。
この方法の場合、親要素のpaddingの大きさで、円環部分のborderの太さを表現します。
例えば、以下のように表現できます。
<div class="wrapper">
<div class="circle-grad">
<div class="circle-grad__inner"></div>
</div>
</div>
.wrapper {
text-align: center;
padding: 200px 0;
background-color: #fff;
}
.circle-grad {
display: inline-block;
width: 200px;
aspect-ratio: 1 / 1;
background: linear-gradient(90deg, rgba(226, 207, 255, 1), rgba(251, 253, 191, 1)); // グラデーション
border-radius: 50%;
padding: 5px; // border-widthに相当
}
.circle-grad__inner {
width: 100%;
height: 100%;
background-color: #fff; // 背景色と同じ色で塗りつぶし
border-radius: 50%;
}
擬似要素を使って塗りつぶす
次に、擬似要素を使う方法です。
この方法の場合、擬似要素で背景のグラデーションを作り、円内部をその要素のbackgroundで塗りつぶします。
<div class="wrapper">
<div class="circle-grad"></div>
</div>
.wrapper {
text-align: center;
padding: 200px 0;
background-color: #fff;
position: relative;
z-index: 0;
}
.circle-grad {
display: inline-block;
width: 200px;
aspect-ratio: 1 / 1;
border-radius: 50%;
background-color: #fff; // 背景色と同じ色で塗りつぶし
position: relative;
// 擬似要素でグラデーションを作成
&::before {
content: "";
position: absolute;
inset: -5px;
z-index: -1;
background: linear-gradient(90deg, rgba(226, 207, 255, 1), rgba(251, 253, 191, 1));
border-radius: 50%;
}
}
円環(角丸)グラデーションの実装例
円環グラデーション:権威性風
<div class="wrapper">
<div class="circle-grad">スピード<br>No.1</div>
<div class="circle-grad">クオリティ<br>No.1</div>
<div class="circle-grad">満足度<br>90%</div>
</div>
.wrapper {
padding: 200px 0;
background-color: #1D1E22;
text-align: center;
}
.circle-grad {
width: 200px;
aspect-ratio: 1 / 1;
color: #fff;
font-size: 36px;
font-family: 'Noto Sans JP', sans-serif;
line-height: 1.4;
display: inline-flex;
flex-direction: column;
justify-content: space-around;
position: relative;
& + & {
margin-left: 40px;
}
&::before {
content: "";
position: absolute;
inset: 0;
border: 5px solid transparent;
border-radius: 50%;
background: linear-gradient(90deg, rgba(226, 207, 255, 1), rgba(251, 253, 191, 1)) border-box;
// FireFox用
mask-image: linear-gradient(#fff 0 0), linear-gradient(#fff 0 0);
mask-clip: padding-box, border-box;
mask-composite: exclude;
// それ以外の主要ブラウザ
-webkit-mask-image: linear-gradient(#fff 0 0), linear-gradient(#fff 0 0);
-webkit-mask-clip: padding-box, border-box;
-webkit-mask-composite: destination-out;
}
}
角丸グラデーション:ボタン
<div class="wrapper">
<a href="#" class="btn-grad">ボタン</a>
</div>
.wrapper {
padding: 200px 0;
background-color: #1D1E22;
text-align: center;
}
.btn-grad {
display: inline-block;
text-decoration: none;
color: #fff;
font-size: 32px;
font-family: 'Noto Sans JP', sans-serif;
padding: 30px 60px;
position: relative;
transition: opacity .3s ease 0s;
&:hover {
opacity: .7;
}
&::before {
content: "";
position: absolute;
inset: 0;
border: 5px solid transparent;
border-radius: 20px;
background: linear-gradient(90deg, rgba(226, 207, 255, 1), rgba(251, 253, 191, 1)) border-box;
// FireFox用
mask-image: linear-gradient(#fff 0 0), linear-gradient(#fff 0 0);
mask-clip: padding-box, border-box;
mask-composite: exclude;
// それ以外の主要ブラウザ
-webkit-mask-image: linear-gradient(#fff 0 0), linear-gradient(#fff 0 0);
-webkit-mask-clip: padding-box, border-box;
-webkit-mask-composite: destination-out;
}
}
まとめ
borderで円環グラデーションを実装する方法をまとめました。
初見では、実装するのが難しいうえ、背景透過の部分も悩むかと思います。
この記事が少しでも参考になれば幸いです。
なお、今回ご紹介した方法は一例にすぎず、他の実装方法もあるかと思います。
ご自身の状況に合わせて色々とカスタマイズしていただければと思います。
以上です。最後までお読みいただきありがとうございました。
ホームページに関するお困りごとがございましたら、お気軽にご相談ください!
TwitterのDMからもご相談を承っております。