TECH · グラデーション
濁らせず境界を思い通りに
CSSグラデーションのサンプル集。境界線がはっきりした塗り分けと縞模様・進捗バー・円グラフ、中間が灰色に濁らない色空間の指定、画像を使わないメッシュ風の背景と縞を消すノイズ、読めなくならない文字のグラデーションを収録します。
01 / SAMPLESサンプルとコード
同じ位置に2つの色の停止を重ねると、補間される幅が無くなり境界がくっきり分かれます。縞模様・進捗バー・円グラフを、画像を使わず1枚の背景で描いています。
表示領域より広い幅は、見本の枠内で横にスクロールできます。
HTML
<div class="grid">
<figure><div class="swatch smooth"></div><figcaption>停止位置が離れている(滑らかに移る)</figcaption></figure>
<figure><div class="swatch split"></div><figcaption>50% に2色を重ねる(境界がはっきり)</figcaption></figure>
<figure><div class="swatch stripes"></div><figcaption>repeating で縞模様</figcaption></figure>
<figure>
<!-- 見た目だけでなく、値を role="progressbar" と aria-valuenow で伝える -->
<div class="progress" role="progressbar" aria-valuenow="64" aria-valuemin="0" aria-valuemax="100" aria-label="アップロードの進み具合"></div>
<figcaption>進捗バー(64%)</figcaption>
</figure>
<figure><div class="pie" role="img" aria-label="内訳: 青45%、緑30%、黄25%"></div><figcaption>conic-gradient の円グラフ</figcaption></figure>
</div> CSS
/* 滑らかに移る。色の停止位置が離れているので、間が補間される */
.smooth { background: linear-gradient(90deg, #2563eb 0%, #22c55e 100%); }
/* 境界線をはっきりさせる。同じ位置(50%)に2色の停止を重ねると、
その間の距離が0になり、補間される幅が無くなる */
.split { background: linear-gradient(90deg, #2563eb 50%, #22c55e 50%); }
/* 縞模様。停止位置を0にした2つ目以降は「直前の位置」扱いになるので、
繰り返しの1周期ぶんだけ書けばよい */
.stripes {
background: repeating-linear-gradient(
-45deg,
#1e293b 0 12px,
#334155 0 24px
);
}
/* 進捗バー。値をカスタムプロパティで渡し、境界の位置に使う。
バーの要素を2つ重ねずに1枚の背景で描ける */
.progress {
--value: 64%;
height: 14px;
border-radius: 99px;
background: linear-gradient(90deg, #2563eb var(--value), #e2e6ee var(--value));
}
/* 円グラフ。conic-gradient も同じ考え方で、角度を重ねると境界がはっきりする */
.pie {
width: 110px;
aspect-ratio: 1;
border-radius: 50%;
background: conic-gradient(
#2563eb 0 45%,
#22c55e 0 75%,
#f59e0b 0 100%
);
} AIへの指示文
CSS の linear-gradient と conic-gradient で、次の5つを作ってください。画像は使いません。
要件:
- 青 #2563eb から緑 #22c55e へ滑らかに移る帯。
- 同じ2色を50%の位置でくっきり塗り分けた帯。同じ位置に2つの停止を重ねると
補間される幅が0になる、と理由をコメントで書く。
- repeating-linear-gradient で斜めの縞模様。2つ目以降の停止位置を0と書くと
直前の位置と同じ扱いになる、と書く。
- 進捗バー。値をカスタムプロパティ --value で渡し、境界の位置に使う。
role="progressbar" と aria-valuenow も付ける。
- conic-gradient の円グラフ(45%・30%・25%)。role="img" と内訳を書いた aria-label を付ける。
青から黄色へのように色相環で反対側にある2色は、sRGB で結ぶと真ん中が灰色にくすみます。補間の色空間を oklch にすると、鮮やかなまま移ります。
表示領域より広い幅は、見本の枠内で横にスクロールできます。
HTML
<div class="row"><p>in srgb: 中間が灰色に濁る</p><div class="bar srgb"></div></div>
<div class="row"><p>in oklch: 色相を回って鮮やかなまま移る</p><div class="bar oklch"></div></div>
<div class="row"><p>in oklab: 暗く沈むのは防ぐが、中間の彩度は落ちる</p><div class="bar oklab"></div></div>
<div class="row"><p>非対応ブラウザにも備えた書き方</p><div class="bar safe"></div></div> CSS
/* in srgb を明示してで青から黄色へ。中間がくすんだ灰色になる。
sRGB は RGB の値をそのまま直線で結ぶので、反対色どうしの間で
彩度が落ちる */
.srgb { background: linear-gradient(90deg, #2563eb, #facc15); background: linear-gradient(in srgb 90deg, #2563eb, #facc15); }
/* 補間の色空間に oklch を指定する。明るさ・鮮やかさ・色相を分けて
補間するので、中間でも彩度が保たれる(青→緑→黄と色相を回って移る) */
.oklch { background: linear-gradient(in oklch 90deg, #2563eb, #facc15); }
/* oklab は色相を回らず直線で結ぶ。中間が暗く沈むのは防げるが、
彩度は落ちる。色相を経由させたくないとき(別の色が混ざって見えると
困るとき)はこちら */
.oklab { background: linear-gradient(in oklab 90deg, #2563eb, #facc15); }
/* 非対応ブラウザには、中間色を手で1つ足した sRGB の指定が先に効く。
後から書いた in oklch が読めるブラウザだけ上書きされる */
.safe {
background: linear-gradient(90deg, #2563eb, #16a34a, #facc15);
background: linear-gradient(in oklch 90deg, #2563eb, #facc15);
} AIへの指示文
青 #2563eb から黄 #facc15 への横向きのグラデーションを4本並べて、違いを比べられるようにしてください。
要件:
- 1本目は in srgb を明示する。中間が灰色に濁る理由を、RGB の値を直線で結ぶため、とコメントで書く。
- 2本目は linear-gradient(in oklch 90deg, ...)。明るさ・鮮やかさ・色相を分けて
補間するので中間でも彩度が保たれる、と書く。
- 3本目は in oklab。中間が暗く沈むのは防げるが彩度は落ちる。途中で別の色相が
混ざって見えると困るときに向く、と書く。
- 4本目は非対応ブラウザへの備え。先に中間色を1つ足した sRGB の指定を書き、
後から in oklch の指定で上書きする。
- 各帯の上に、何を比べているかを1行で書く。
位置と大きさの違う radial-gradient を4つ重ねています。広い面の緩やかなグラデーションは色の段が縞に見えやすいので、SVG で作った薄いノイズを重ねて消しています。
表示領域より広い幅は、見本の枠内で横にスクロールできます。
HTML
<section class="mesh">
<h2>請求業務を、毎月3日から30分へ。</h2>
<p>背景は4つの円形のグラデーションを重ねただけです。色と位置を変えるだけで、季節やキャンペーンに合わせた表情を作れます。</p>
</section> CSS
.mesh {
position: relative;
isolation: isolate;
overflow: hidden;
padding: clamp(2.5rem, 7vw, 4.5rem) clamp(1.5rem, 5vw, 3rem);
color: #fff;
border-radius: 20px;
/* 地の色の上に、位置と大きさの違う radial を重ねる。
重ねる順は「後に書いたものほど下」。いちばん下に地の色を置く */
background:
radial-gradient(40% 55% at 15% 20%, #7c3aed 0%, transparent 70%),
radial-gradient(45% 60% at 85% 15%, #ec4899 0%, transparent 70%),
radial-gradient(50% 65% at 70% 90%, #f59e0b 0%, transparent 70%),
radial-gradient(45% 60% at 20% 85%, #2563eb 0%, transparent 70%),
#0f172a;
}
/* 縞(バンディング)を消すノイズ。広い面の緩やかなグラデーションは
8bit の色で段が見えやすい。ごく薄い粒を重ねると段が目立たなくなる。
ノイズは SVG の feTurbulence を data URI で埋め込み、画像ファイルを増やさない */
.mesh::before {
content: "";
position: absolute;
inset: 0;
z-index: -1;
opacity: .18;
mix-blend-mode: overlay;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
pointer-events: none;
}
.mesh h2 { margin: 0 0 .6rem; font-size: clamp(1.5rem, 4vw, 2.2rem); line-height: 1.4; }
/* 背景が明るい部分に来ても読めるよう、文字の後ろにだけ薄い影を敷く。
背景全体を暗くするより、色の鮮やかさを保てる */
.mesh h2, .mesh p { text-shadow: 0 1px 12px rgba(15, 23, 42, .45); }
.mesh p { margin: 0; max-width: 34em; line-height: 1.85; } AIへの指示文
ヒーロー用の、メッシュ風のグラデーション背景を作ってください。画像ファイルは使いません。
要件:
- 地の色 #0f172a の上に、位置と大きさの違う radial-gradient を4つ重ねる
(紫 #7c3aed、桃 #ec4899、橙 #f59e0b、青 #2563eb)。各色は transparent 70% で消える。
- 後に書いた背景ほど下に重なる、とコメントで書く。
- ::before に、SVG の feTurbulence で作ったノイズを data URI で敷き、
不透明度0.18・mix-blend-mode: overlay で重ねる。
広い面の緩やかなグラデーションは8bitの色で段が縞に見える、と理由を書く。
- 背景は isolation: isolate で閉じ、ノイズを z-index: -1 で文字の下に置く。
- 見出しと本文の後ろにだけ薄い text-shadow を敷き、明るい部分に来ても読めるようにする。
背景全体を暗くするより色の鮮やかさを保てる、と書く。
対応環境でだけ文字を透明にして背景を切り抜き、非対応や強制カラーモードでは単色の文字に戻します。角度は @property で登録して回し、動きを減らす設定では止めます。
表示領域より広い幅は、見本の枠内で横にスクロールできます。
HTML
<div class="hero">
<h1 class="grad-text">見出しだけに色を集める</h1>
<p>本文は単色のまま。グラデーションは1画面に1か所だけにします。</p>
</div> CSS
/* 角度を動かせるよう、型付きのカスタムプロパティとして登録する。
登録しないと、カスタムプロパティは文字列として扱われ、アニメーションしない */
@property --angle {
syntax: "<angle>";
initial-value: 90deg;
inherits: false;
}
.grad-text {
margin: 0;
font-size: clamp(2rem, 6vw, 3.4rem);
font-weight: 800;
line-height: 1.3;
/* 背景が描けない環境(強制カラーモードなど)で、この色がそのまま出る */
color: #4338ca;
}
/* 対応している環境でだけ、文字を透明にして背景を文字の形に切り抜く。
先に color を透明にしてから非対応だと、文字が消えて何も読めなくなる */
@supports (background-clip: text) or (-webkit-background-clip: text) {
.grad-text {
/* いちばん明るい色でも地とのコントラストが 4.5:1 を切らない範囲に留める */
background: linear-gradient(var(--angle), #4338ca, #be185d 55%, #c2410c);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: turn 8s linear infinite;
}
}
@keyframes turn { to { --angle: 450deg; } }
/* 動きを減らす設定では止める。止めても色の並びはそのまま残る */
@media (prefers-reduced-motion: reduce) {
.grad-text { animation: none; }
}
/* 強制カラーモードでは背景が消されるので、文字色を元に戻す */
@media (forced-colors: active) {
.grad-text { color: CanvasText; background: none; }
} AIへの指示文
見出しの文字をグラデーションで塗ってください。
要件:
- 既定の color に単色(#4338ca)を指定しておく。
- @supports (background-clip: text) の中でだけ、背景を文字の形に切り抜き、color を透明にする。
先に透明にしてから非対応だと文字が消えて読めなくなる、と理由を書く。
- いちばん明るい色でも、地とのコントラストが4.5:1を切らない3色を選ぶ。
- 角度を @property で <angle> 型として登録し、8秒で1回転させる。
登録しないとカスタムプロパティは文字列扱いでアニメーションしない、と書く。
- prefers-reduced-motion では回転を止める。
- forced-colors: active では背景を外し、文字色を CanvasText に戻す。
02 / OVERVIEWこの技術について
CSS のグラデーションは、2010年ごろにベンダープレフィックス付きで使えるようになり、いまではヒーローの背景からボタン、見出しの文字まで、画像を使わずに色の変化を作る標準の手段になりました。1行で書けて画像より軽く、色を変えるのも値を差し替えるだけです。
その手軽さのぶん、思った通りにならない場面も決まっています。真ん中が濁る、縞が出る、文字が読めなくなる。どれも原因がはっきりしていて、書き方で避けられます。
境界をはっきりさせるのも、グラデーションの仕事
グラデーションというと滑らかな色の移り変わりを思い浮かべますが、境界をくっきり分ける使い方のほうが実務では頻繁に出てきます。2色の帯、縞模様、進捗バー、円グラフ。どれも画像や要素を重ねずに、背景1枚で描けます。
中間が濁るのは、色空間のせい
CSS Images Level 4では既定の補間色空間をOklabとしています。一方、従来のHEXやRGBだけの指定をsRGBとして扱う互換動作もあるため、比較するときは in srgb / in oklab / in oklch を明示します。
青から黄色へのグラデーションを sRGB で補間すると、真ん中がくすんだ灰色になります。色の指定が間違っているのではありません。sRGB では、RGB の値をそのまま直線で結ぶので、色相環で反対側にある2色の間では彩度が落ちるのです。
反対色どうしを結ぶときは in oklch を付ける。非対応ブラウザには、中間色を1つ手で足した sRGB の指定を先に書いておけば、同じ見た目に近づきます。
広い面の縞は、ノイズで消す
ヒーローの背景のように広い面へ緩やかなグラデーションを敷くと、色が段になって縞模様のように見えることがあります。8bitの画像や表示経路では1チャンネルあたり256段階に限られ、変化が緩やかなほど1段の幅が広がって目に入るからです。
ノイズは SVG の feTurbulence を data URI で埋め込むと、画像ファイルを1つも増やさずに済みます。複数の radial-gradient を重ねて作るメッシュ風の背景と組み合わせるのが定番です。
文字に使うなら、読めることを先に確保する
見出しの文字をグラデーションで塗るのは効果的ですが、壊れ方が2つあります。1つは、いちばん明るい部分で地とのコントラストが足りなくなること。もう1つは、背景を文字の形に切り抜く指定に対応していない環境で、透明にした文字が消えて何も読めなくなることです。
崩れる形いきなり color: transparent にする
切り抜きに対応していない環境や、強制カラーモードで背景が外された環境では、透明な文字だけが残って見出しが消えます。
効く形単色を既定にして、対応環境でだけ切り抜く
既定の color に単色を入れ、@supports の中でだけ透明にします。強制カラーモードでは背景を外して文字色を戻すと、どの環境でも見出しが読めます。
グラデーションは、1画面に1か所だけにとどめると効きます。見出しも背景もボタンも塗ると、どこが主役か分からなくなり、どれも目立たなくなります。
03 / NOTES使いどころとつまずきどころ
向いている場面
- ヒーローやCTAの背景
- 色の切り替えで区切る帯や見出し
- 進捗バーや円グラフのような簡単な図
- 見出しの一部だけを目立たせる文字の塗り
つまずきやすい点
- 反対色どうしを sRGB で結ぶと、中間が灰色に濁る
- 広い面の緩やかなグラデーションは、段が縞になって見える
- 文字の塗りは、いちばん明るい色でコントラストが切れる
- 非対応環境で文字を透明にすると、何も読めなくなる
- 画面のあちこちに使うと、どこが主役か分からなくなる
04 / FAQよくある質問
境界線がはっきりしたグラデーションはどう書く?
同じ位置に2つの色の停止を置きます。linear-gradient(90deg, 青 50%, 緑 50%) のように書くと、50%の位置で補間される幅が0になり、くっきり塗り分けられます。縞模様なら repeating-linear-gradient を使います。
グラデーションの真ん中が灰色っぽくなるのはなぜ?
sRGB 補間では、RGB の値を直線で結ぶので、青と黄のような反対色の間で彩度が落ちます。linear-gradient(in oklch 90deg, ...) のように補間の色空間を指定すると、鮮やかなまま移ります。
背景のグラデーションに縞が出る
広い面の緩やかなグラデーションでは、8bit の色の段差が縞として見えます。色の差を大きくするか、ごく薄いノイズを重ねると目立たなくなります。ノイズは SVG の feTurbulence を data URI で埋め込めば、画像ファイルを増やさずに済みます。
文字のグラデーションは検索エンジンや読み上げに影響する?
文字そのものは HTML に残るので、検索エンジンにも読み上げにも普通の文字として渡ります。影響が出るのは画像の文字に置き換えた場合です。気をつけるべきは、いちばん明るい色の部分のコントラストです。