CSSで文字にグラデーションの色を付ける方法

​​CSS​HTML,Playwright

文字にグラデーションの色を付けるには、要素の背景に linear-gradient() を敷き、background-clip: text で背景を文字の形に切り抜いて、文字の色を transparent にします。見出しの幅が文字より広いと、グラデーションの途中までしか文字に乗らないので、width: fit-content で幅を文字に合わせます。折り返した行で色を行ごとにかけ直すには box-decoration-break: clone を、影を付けるには text-shadow ではなく filter: drop-shadow() を使います。

補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。HTML と CSS だけのページを Playwright で開き、スクリーンショットの画素を読んで、文字の左端と右端の色を調べたり、白でない画素の数を数えたりしました。Firefox と Safari、スマートフォンのブラウザ、グラデーションを動かすアニメーションは試していません。

背景を文字の形で切り抜いて、文字の色を透明にする

基本の CSS は次のとおりです。

css
.gradient-text {
  background-image: linear-gradient(90deg, #00c8e8, #d85878);
  -webkit-background-clip: text; /* Chrome 119 以前・Safari 13 以前向け */
  background-clip: text; /* 背景を文字の形で切り抜く */
  color: transparent; /* 文字の色を透明にして、後ろの背景を見せる */
}
html
<h1 class="gradient-text fit">猫の手を借りる</h1>

fit は、見出しの幅を文字の幅に合わせるクラスです(次の節で説明します)。

linear-gradient() で、左の水色から右のピンクへ変わる背景を作ります。background-clip は背景をどこまで描くかを決めるプロパティで、text を指定すると、背景は文字の形の中にだけ描かれます。MDN の英語版の background-clip のページの説明によると、文字の色が不透明か半透明だと、background-clip: text の効果はほとんど見えません。そのため color: transparent を合わせて書きます。

上下2段の比較。上は「background-clip なし」で、水色からピンクへ変わる横長の四角だけが見え、文字は見えない。下は「background-clip: text あり」で、「猫の手を借りる」の文字が左の水色から右のピンクへ色が変わっている
background-clip: text がないと、背景の四角が見えて文字は消える(上)。付けると文字の形に切り抜かれる(下)

background-clip: text を外すと、背景が要素の四角いっぱいに描かれ、透明な文字は見えなくなりました。要素の範囲の画素は、すべて白ではなくなっています。付けたときは、白でない画素は要素の範囲の 33.1% で、文字の形の部分だけでした。文字の左端の色は #0fc2e2、右端の色は #ca6282 で、水色からピンクへ変わっています。

見出しの右端までピンクにならないときは、幅を文字に合わせる

h1 のようなブロック要素は、ふつう親の幅いっぱいに広がります。背景のグラデーションは要素の幅いっぱいに描かれるので、文字が短いと、グラデーションの左側だけが文字に乗ります。

上下2段の比較。上は「ブロックのまま」で、点線で示した要素の幅が横いっぱいに広く、「猫の手」の3文字はどれも水色。下は「width: fit-content」で、点線の幅が文字にぴったり合い、「猫の手」が水色からピンクへ変わっている
要素の幅が広いと、文字には水色の側だけが乗る(上)。幅を文字に合わせると、ピンクまで乗る(下)

幅 640px の見出しに「猫の手」の3文字(幅 144px)を書いたところ、右端の文字の色は #2eb1d1 で、ほとんど水色のままでした。見出しに width: fit-content を付けると、要素の幅が文字の幅(144px)になり、右端の色は #c86181 のピンクになりました。

css
.fit {
  width: fit-content;
}
html
<h1 class="gradient-text fit">猫の手</h1>

折り返した文字で、行ごとに水色から始めたいときは box-decoration-break: clone

文の中の一部を span で囲んでグラデーションにすると、span が2行に折り返すことがあります。既定では、1行目の最後で止まった色の続きから、2行目が始まります。

上下2段の比較。どちらも「猫の手も借りたいほど忙しい日」の1行目と「のメモ」の2行目に折り返している。上の「既定(slice)」は、1行目の右端が紫がかった色で、2行目はその続きの紫からピンクへ変わる。下の「box-decoration-break: clone」は、1行目も2行目も、それぞれ左の水色から右のピンクへ変わる
既定では2行がひと続きのグラデーションになる(上)。clone にすると、行ごとに水色からピンクになる(下)

幅 624px の1行目と、幅 192px の2行目に折り返した span で測りました。既定のままだと、1行目の右端は #9e7898 の紫がかった色で、2行目の左端は #a97090、右端は #d45b7b でした。2行をつないだ長さで、1本のグラデーションを描いています。

box-decoration-break は、折り返したインライン要素の背景や枠線を、行ごとにどう描くかを決めるプロパティです。既定の slice は、折り返す前の1つのボックスとして描いてから、行ごとに分けます。clone を指定すると、背景を行ごとに描き直します。

css
.clone {
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
html
<p><span class="gradient-text clone">猫の手も借りたいほど忙しい日のメモ</span></p>

clone にすると、1行目は #0dc3e3 から #ce607f、2行目は #11bfdf から #c86181 になりました。どちらの行も、水色で始まってピンクで終わっています。

MDN の互換性データ(@mdn/browser-compat-data 8.1.8)では、接頭辞なしの box-decoration-break は Chrome 130 から、Firefox 32 から使えます。Chrome 129 以前と Safari は -webkit-box-decoration-break だけに対応していて、インライン要素にだけ効くと書かれています。これらのブラウザにも対応するなら、上のように両方を書きます。

影を付けるなら text-shadow ではなく filter: drop-shadow()

グラデーションの文字に text-shadow を付けると、文字が暗くなります。text-shadow の影は文字の後ろに置かれますが、文字の色を透明にしているので、文字の形の内側にも影の色が見えてしまいます。

上下2段の比較。上の「text-shadow」は、「猫の手を借りる」の文字全体が暗く濁った色になっている。下の「filter: drop-shadow()」は、文字は明るい水色からピンクのままで、右下に薄い灰色の影が付いている
text-shadow だと影が透けて文字が暗くなる(上)。drop-shadow() なら文字の色はそのままで、外側にだけ影が付く(下)

text-shadow: 2px 2px 2px rgb(0 0 0 / 0.4) を付けて、影を付けない文字と同じ位置の画素の明るさを比べました。影がないときの平均 138.6 に対して、text-shadow を付けると 101.0 まで下がり、文字の色が濁って見えました。MDN の background-clip のページの例でも、background-clip: text にするときは text-shadow: none にしています。

影を付けたいときは、filter の drop-shadow() を使います。drop-shadow() は、描かれた形(透明でない部分)に沿って影を作ります。

css
.shadow-filter {
  filter: drop-shadow(2px 2px 2px rgb(0 0 0 / 0.4));
}
html
<h1 class="gradient-text fit shadow-filter">猫の手を借りる</h1>

こちらは、文字の部分の明るさの平均が 138.1 で、影がないときとほとんど変わりませんでした。文字の外側では、影がないときに白かった画素のうち 4,251 個が白でなくなり、影が付いています。

background-clip: text が使えないブラウザでは単色にする

background-clip: text が効かず、背景と color: transparent だけが効くと、最初の比較画像の上の段(background-clip: text を外したもの)と同じになります。背景の四角だけが見えて、文字は読めません。MDN の background-clip のページでも、@supports で対応しているかを確かめ、対応していなければ代わりの表示にすることが勧められています。

css
.safe-gradient-text {
  color: #067a98;
}

@supports (background-clip: text) or (-webkit-background-clip: text) {
  .safe-gradient-text {
    background-image: linear-gradient(90deg, #00c8e8, #d85878);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

Chromium 153 では、この書き方で、白でない画素の割合と左右の端の色が基本の CSS と同じになり、color の計算値は rgba(0, 0, 0, 0)、background-clip は text でした。比べるために、@supports の条件をわざと成り立たないもの(background-clip: no-such-value)に変えたところ、背景は付かず、文字は #067a98 の単色で表示されました。

MDN の互換性データでは、接頭辞なしの background-clip: text は Chrome 120 から、Firefox 49 から、Safari 14 から使えます。ただし Firefox は、background-attachment: fixed と組み合わせたときなどに効かない場合があるという注記付きの、部分的な対応の扱いです。Chrome 119 以前と Safari 13.1 以前は、-webkit-background-clip: text だけに対応していました。検証環境の Chromium 153 では、CSS.supports() で調べると、接頭辞のあるほうもないほうも true でした。

​Posted by 86