メッシュグラデーション
複数の radial-gradient を重ねて有機的な色面を作り、JSで各色溜まりを漂わせるメッシュ背景。SaaSやポートフォリオのヒーローに最適です。
ライブデモ
使用例(お題: SaaS FlowDesk)
この技法を「SaaS FlowDesk」というテーマのダミーサイトで実際に使った例です。
HTML
<!-- FlowDesk:メッシュグラデを背に、機能訴求+ダッシュボードのチラ見せ -->
<section class="fm-stage">
<!-- ★主役:4つの色溜まりを漂わせるメッシュグラデーション -->
<div class="fm-mesh" id="fmMesh" aria-hidden="true"></div>
<div class="fm-grid">
<div class="fm-copy">
<span class="fm-tag">FEATURES</span>
<h1 class="fm-title">ダッシュボードで<br>すべてが見渡せる</h1>
<p class="fm-sub">プロジェクトの進捗、メンバーの稼働、今日のタスク。散らばった情報をひとつの画面に集約します。</p>
<ul class="fm-list">
<li>リアルタイム同期</li>
<li>権限管理&監査ログ</li>
<li>API/Webhook連携</li>
</ul>
</div>
<!-- 製品の世界観:浮かぶダッシュボードカード -->
<div class="fm-panel" aria-hidden="true">
<div class="fm-panel__bar">
<span class="fm-dot"></span><span class="fm-dot"></span><span class="fm-dot"></span>
<small>FlowDesk ・ 概要</small>
</div>
<div class="fm-stat">
<div><b>98.4%</b><span>稼働率</span></div>
<div><b>+12</b><span>今週の完了</span></div>
</div>
<div class="fm-chart">
<i style="--h:40%"></i><i style="--h:62%"></i><i style="--h:48%"></i>
<i style="--h:78%"></i><i style="--h:66%"></i><i style="--h:90%"></i>
</div>
</div>
</div>
</section>
CSS
/* FlowDesk:紺地のメッシュグラデを背景に、機能訴求とダッシュボードカード */
* { box-sizing: border-box; margin: 0; padding: 0; }
.fm-stage {
position: relative;
min-height: 400px;
height: 400px;
overflow: hidden;
background: #0f1b34;
font-family: "Segoe UI", "Hiragino Sans", system-ui, sans-serif;
color: #fff;
}
/* ★主役:複数の radial-gradient を重ねたメッシュ(位置はJSで揺らす) */
.fm-mesh {
position: absolute;
inset: -20%;
z-index: 0;
--x1: 18%; --y1: 22%;
--x2: 82%; --y2: 18%;
--x3: 24%; --y3: 82%;
--x4: 80%; --y4: 80%;
background:
radial-gradient(38% 38% at var(--x1) var(--y1), #4f7cff 0%, transparent 70%),
radial-gradient(40% 40% at var(--x2) var(--y2), #7b5cff 0%, transparent 72%),
radial-gradient(42% 42% at var(--x3) var(--y3), #21a9fd 0%, transparent 70%),
radial-gradient(40% 40% at var(--x4) var(--y4), #2b3f7a 0%, transparent 72%);
filter: blur(44px) saturate(130%);
}
.fm-grid {
position: relative;
z-index: 1;
height: 100%;
display: grid;
grid-template-columns: 1.1fr 0.9fr;
align-items: center;
gap: 22px;
padding: 0 30px;
}
.fm-tag {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.22em;
color: #9bc0ff;
}
.fm-title {
margin-top: 10px;
font-size: 30px;
font-weight: 800;
line-height: 1.22;
text-shadow: 0 4px 22px rgba(0,0,0,0.4);
}
.fm-sub {
margin-top: 12px;
font-size: 13px;
line-height: 1.8;
color: rgba(255,255,255,0.88);
max-width: 340px;
}
.fm-list {
margin-top: 14px;
list-style: none;
display: flex;
flex-direction: column;
gap: 7px;
}
.fm-list li {
font-size: 12.5px;
color: rgba(255,255,255,0.92);
padding-left: 22px;
position: relative;
}
.fm-list li::before {
content: "✓";
position: absolute;
left: 0;
color: #6ff0c8;
font-weight: 800;
}
/* ダッシュボードカード(ガラス風) */
.fm-panel {
background: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.22);
border-radius: 16px;
padding: 14px;
-webkit-backdrop-filter: blur(12px);
backdrop-filter: blur(12px);
box-shadow: 0 18px 50px rgba(0,0,0,0.4);
}
.fm-panel__bar {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 12px;
}
.fm-dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(255,255,255,0.5); }
.fm-panel__bar small { margin-left: 6px; font-size: 10px; color: rgba(255,255,255,0.7); }
.fm-stat { display: flex; gap: 10px; margin-bottom: 14px; }
.fm-stat > div {
flex: 1;
background: rgba(255,255,255,0.08);
border-radius: 10px;
padding: 10px;
}
.fm-stat b { display: block; font-size: 20px; font-weight: 800; color: #fff; }
.fm-stat span { font-size: 10px; color: rgba(255,255,255,0.7); }
/* ミニ棒グラフ */
.fm-chart {
display: flex;
align-items: flex-end;
gap: 8px;
height: 70px;
padding: 0 2px;
}
.fm-chart i {
flex: 1;
height: var(--h);
border-radius: 5px 5px 2px 2px;
background: linear-gradient(to top, #4f7cff, #7fbfff);
}
@media (prefers-reduced-motion: reduce) {
.fm-mesh { animation: none; }
}
JavaScript
// メッシュの各色溜まり(CSS変数)を時間で揺らし、有機的に漂わせる
(() => {
const mesh = document.getElementById("fmMesh");
if (!mesh) return; // null安全
// モーション低減なら静止
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduce) return;
// 4点の基準位置と位相
const pts = [
{ kx: "--x1", ky: "--y1", bx: 18, by: 22, px: 0.0, py: 1.2 },
{ kx: "--x2", ky: "--y2", bx: 82, by: 18, px: 2.1, py: 3.3 },
{ kx: "--x3", ky: "--y3", bx: 24, by: 82, px: 4.0, py: 1.6 },
{ kx: "--x4", ky: "--y4", bx: 80, by: 80, px: 1.0, py: 5.0 },
];
const amp = 8; // 揺れ幅(%)
let raf = 0;
const tick = (t) => {
const s = t / 1000;
for (const p of pts) {
const x = p.bx + Math.sin(s * 0.32 + p.px) * amp;
const y = p.by + Math.cos(s * 0.27 + p.py) * amp;
mesh.style.setProperty(p.kx, x.toFixed(2) + "%");
mesh.style.setProperty(p.ky, y.toFixed(2) + "%");
}
raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
// タブ非表示でループ停止
document.addEventListener("visibilitychange", () => {
cancelAnimationFrame(raf);
if (!document.hidden) raf = requestAnimationFrame(tick);
});
})();
コード
HTML
<!-- メッシュグラデーション: 複数の放射状グラデを重ねて有機的な色面を作る -->
<div class="mesh-stage">
<div class="mesh-blobs" id="meshBlobs"></div>
<div class="mesh-content">
<h1 class="mesh-title">Mesh Gradient</h1>
<p class="mesh-sub">複数の radial-gradient を重ね、柔らかな色の溜まりを表現。ヒーロー背景に最適です。</p>
</div>
</div>
CSS
/* 複数の放射グラデを background に重ね、各点をゆっくり漂わせる */
* { box-sizing: border-box; margin: 0; padding: 0; }
.mesh-stage {
position: relative;
min-height: 360px;
overflow: hidden;
display: grid;
place-items: center;
background: #0e1020;
font-family: "Segoe UI", "Hiragino Sans", system-ui, sans-serif;
}
/* メッシュ本体: 4つの色溜まりをカスタムプロパティで位置制御 */
.mesh-blobs {
position: absolute;
inset: -20%;
--x1: 20%; --y1: 25%;
--x2: 80%; --y2: 20%;
--x3: 25%; --y3: 80%;
--x4: 78%; --y4: 78%;
background:
radial-gradient(38% 38% at var(--x1) var(--y1), #ff6ec4 0%, transparent 70%),
radial-gradient(40% 40% at var(--x2) var(--y2), #7873f5 0%, transparent 72%),
radial-gradient(42% 42% at var(--x3) var(--y3), #21d4fd 0%, transparent 70%),
radial-gradient(40% 40% at var(--x4) var(--y4), #f9d423 0%, transparent 72%);
filter: blur(40px) saturate(135%);
transition: --x1 0.6s linear; /* JS未対応ブラウザ向けの無害な保険 */
}
.mesh-content {
position: relative;
z-index: 2;
text-align: center;
color: #fff;
padding: 0 24px;
max-width: 520px;
}
.mesh-title {
font-size: 44px;
font-weight: 800;
letter-spacing: 0.01em;
text-shadow: 0 6px 30px rgba(0, 0, 0, 0.45);
}
.mesh-sub {
margin-top: 14px;
font-size: 14px;
line-height: 1.85;
color: rgba(255, 255, 255, 0.88);
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
}
@media (prefers-reduced-motion: reduce) {
.mesh-blobs { transition: none; }
}
JavaScript
// メッシュの各色溜まりの位置(CSS変数)を時間で揺らし、有機的な動きを作る
(() => {
const blobs = document.getElementById("meshBlobs");
if (!blobs) return; // null安全
// モーション低減なら静止
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduce) return;
// 4点それぞれの基準位置と揺れの位相を定義
const pts = [
{ kx: "--x1", ky: "--y1", bx: 20, by: 25, px: 0.0, py: 1.1 },
{ kx: "--x2", ky: "--y2", bx: 80, by: 20, px: 2.0, py: 3.4 },
{ kx: "--x3", ky: "--y3", bx: 25, by: 80, px: 4.0, py: 1.7 },
{ kx: "--x4", ky: "--y4", bx: 78, by: 78, px: 1.0, py: 5.2 },
];
const amp = 9; // 揺れ幅(%)
let raf = 0;
const tick = (t) => {
const s = t / 1000;
for (const p of pts) {
const x = p.bx + Math.sin(s * 0.35 + p.px) * amp;
const y = p.by + Math.cos(s * 0.28 + p.py) * amp;
blobs.style.setProperty(p.kx, x.toFixed(2) + "%");
blobs.style.setProperty(p.ky, y.toFixed(2) + "%");
}
raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
// タブ非表示時はループを止める
document.addEventListener("visibilitychange", () => {
cancelAnimationFrame(raf); // 二重ループ防止
if (!document.hidden) raf = requestAnimationFrame(tick);
});
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「メッシュグラデーション」の効果を追加してください。
# 追加してほしい効果
メッシュグラデーション(背景 & グラデーション)
複数の radial-gradient を重ねて有機的な色面を作り、JSで各色溜まりを漂わせるメッシュ背景。SaaSやポートフォリオのヒーローに最適です。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- メッシュグラデーション: 複数の放射状グラデを重ねて有機的な色面を作る -->
<div class="mesh-stage">
<div class="mesh-blobs" id="meshBlobs"></div>
<div class="mesh-content">
<h1 class="mesh-title">Mesh Gradient</h1>
<p class="mesh-sub">複数の radial-gradient を重ね、柔らかな色の溜まりを表現。ヒーロー背景に最適です。</p>
</div>
</div>
【CSS】
/* 複数の放射グラデを background に重ね、各点をゆっくり漂わせる */
* { box-sizing: border-box; margin: 0; padding: 0; }
.mesh-stage {
position: relative;
min-height: 360px;
overflow: hidden;
display: grid;
place-items: center;
background: #0e1020;
font-family: "Segoe UI", "Hiragino Sans", system-ui, sans-serif;
}
/* メッシュ本体: 4つの色溜まりをカスタムプロパティで位置制御 */
.mesh-blobs {
position: absolute;
inset: -20%;
--x1: 20%; --y1: 25%;
--x2: 80%; --y2: 20%;
--x3: 25%; --y3: 80%;
--x4: 78%; --y4: 78%;
background:
radial-gradient(38% 38% at var(--x1) var(--y1), #ff6ec4 0%, transparent 70%),
radial-gradient(40% 40% at var(--x2) var(--y2), #7873f5 0%, transparent 72%),
radial-gradient(42% 42% at var(--x3) var(--y3), #21d4fd 0%, transparent 70%),
radial-gradient(40% 40% at var(--x4) var(--y4), #f9d423 0%, transparent 72%);
filter: blur(40px) saturate(135%);
transition: --x1 0.6s linear; /* JS未対応ブラウザ向けの無害な保険 */
}
.mesh-content {
position: relative;
z-index: 2;
text-align: center;
color: #fff;
padding: 0 24px;
max-width: 520px;
}
.mesh-title {
font-size: 44px;
font-weight: 800;
letter-spacing: 0.01em;
text-shadow: 0 6px 30px rgba(0, 0, 0, 0.45);
}
.mesh-sub {
margin-top: 14px;
font-size: 14px;
line-height: 1.85;
color: rgba(255, 255, 255, 0.88);
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
}
@media (prefers-reduced-motion: reduce) {
.mesh-blobs { transition: none; }
}
【JavaScript】
// メッシュの各色溜まりの位置(CSS変数)を時間で揺らし、有機的な動きを作る
(() => {
const blobs = document.getElementById("meshBlobs");
if (!blobs) return; // null安全
// モーション低減なら静止
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduce) return;
// 4点それぞれの基準位置と揺れの位相を定義
const pts = [
{ kx: "--x1", ky: "--y1", bx: 20, by: 25, px: 0.0, py: 1.1 },
{ kx: "--x2", ky: "--y2", bx: 80, by: 20, px: 2.0, py: 3.4 },
{ kx: "--x3", ky: "--y3", bx: 25, by: 80, px: 4.0, py: 1.7 },
{ kx: "--x4", ky: "--y4", bx: 78, by: 78, px: 1.0, py: 5.2 },
];
const amp = 9; // 揺れ幅(%)
let raf = 0;
const tick = (t) => {
const s = t / 1000;
for (const p of pts) {
const x = p.bx + Math.sin(s * 0.35 + p.px) * amp;
const y = p.by + Math.cos(s * 0.28 + p.py) * amp;
blobs.style.setProperty(p.kx, x.toFixed(2) + "%");
blobs.style.setProperty(p.ky, y.toFixed(2) + "%");
}
raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
// タブ非表示時はループを止める
document.addEventListener("visibilitychange", () => {
cancelAnimationFrame(raf); // 二重ループ防止
if (!document.hidden) raf = requestAnimationFrame(tick);
});
})();
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。