ライブデモ Live Demo
5つのぼかしたラジアルグラデーション(orb)が CSS キーフレームで浮遊し、暗い背景の上で有機的に混ざり合う FV ヒーロー背景です。WebGL 不要 — CSS カスタムプロパティと transform だけで実装できます。スライダーでアニメーション速度・ぼかし量を調整し、テーマセレクターで配色を切り替えてください。
Five blurred radial-gradient orbs float on CSS keyframes and organically blend above a dark base — a hero FV background that needs no WebGL. Pure CSS custom properties and transforms. Adjust animation speed and blur with the sliders, and switch color palettes with the theme selector.
Build something beautiful
Pure CSS hero background — no WebGL, no canvas,
just blurred radial gradients in motion.
AI向け説明 AI Description
`M-015` は WebGL を使わない CSS のみのヒーロー背景エフェクトです。5つの `.mesh-orb` 要素を `position: absolute` で `.mesh-bg` 内に配置し、それぞれに `radial-gradient(circle at center, 色 0%, transparent 70%)` と `filter: blur(--mesh-blur)` を適用してアンビエントな発光を作ります。各 orb は独立した `@keyframes`(mesh-float-1〜5)で `translate()` と `scale()` を組み合わせて浮遊し、`animation-delay` に負の値を設定してページロード直後から途中フレームから再生します。カラーテーマは CSS カスタムプロパティ `--c1`〜`--c5` で管理し、`.theme-sunset` / `.theme-forest` クラスの付け替えで切り替えます。JS はアニメーション速度(各 orb の `animationDuration` を基準値 ÷ 倍率で上書き)・ぼかし量(`--mesh-blur` の更新)・テーマ切替(クラス操作と `background` の直接更新)のみを担当します。`prefers-reduced-motion: reduce` で全 orb のアニメーションを停止します。
`M-015` is a CSS-only hero background effect requiring no WebGL. Five `.mesh-orb` elements are placed absolutely inside `.mesh-bg`, each given a `radial-gradient(circle at center, color 0%, transparent 70%)` background and `filter: blur(--mesh-blur)` to produce ambient glow. Each orb floats via its own independent `@keyframes` (mesh-float-1 through 5) combining `translate()` and `scale()`, with a negative `animation-delay` so playback begins mid-cycle at page load. Color themes are managed through CSS custom properties `--c1`–`--c5`, toggled by adding `.theme-sunset` or `.theme-forest` to `.mesh-hero`. JavaScript handles only: animation speed (overwriting each orb's `animationDuration` with base ÷ multiplier), blur (updating `--mesh-blur`), and theme switching (class manipulation + direct `background` update). Under `prefers-reduced-motion: reduce`, all orb animations are stopped.
調整可能パラメータ Adjustable Parameters
- --mesh-blur — `filter: blur()` の値(40〜140px)。大きいほど滑らかに広がり、小さいほど発光源が明確になる `filter: blur()` value (40–140px). Larger = softer spread; smaller = distinct glow centers
- animation-duration — 各 orb の基準周期(12s・9s・11s・14s・8s)を速度倍率で割って設定。デフォルト倍率 1.0× Base durations (12s, 9s, 11s, 14s, 8s) divided by the speed multiplier. Default 1.0×
- --c1 〜 --c5 — 5つの orb カラー。テーマクラスで一括切替(Aurora / Sunset / Forest) Colors for the five orbs; swapped in bulk via theme class (Aurora / Sunset / Forest)
- orb size / position — 各 `.mesh-orb-N` の `width`・`height`・`top`・`left`・`right`・`bottom` で重なり具合とグラデーションの分布を調整 Adjust `width`, `height`, and position props on each `.mesh-orb-N` to change overlap and gradient distribution
- opacity — `.mesh-orb` の `opacity`(デフォルト 0.85)を下げると落ち着いた印象に、上げると鮮やかになる Lower `.mesh-orb` opacity (default 0.85) for a muted look; raise it for more vivid gradients
- prefers-reduced-motion — 全 orb のアニメーションを停止し、静止したグラデーション背景として表示 Stops all orb animations, rendering the gradients as a still background
実装 Implementation
HTML
<div class="mesh-hero">
<div class="mesh-bg">
<div class="mesh-orb mesh-orb-1"></div>
<div class="mesh-orb mesh-orb-2"></div>
<div class="mesh-orb mesh-orb-3"></div>
<div class="mesh-orb mesh-orb-4"></div>
<div class="mesh-orb mesh-orb-5"></div>
</div>
<div class="mesh-card">
<h2>Your Hero Heading</h2>
<p>Subtitle copy here.</p>
</div>
</div>
CSS
/* Add .theme-sunset or .theme-forest to .mesh-hero to switch palettes */
.mesh-hero {
position: relative;
overflow: hidden;
background: #080814;
--c1: #7c3aed; --c2: #db2777; --c3: #2563eb; --c4: #6d28d9; --c5: #0e7490;
--mesh-blur: 80px;
}
.mesh-hero.theme-sunset {
--c1: #ea580c; --c2: #db2777; --c3: #d97706; --c4: #dc2626; --c5: #9333ea;
background: #140a04;
}
.mesh-hero.theme-forest {
--c1: #059669; --c2: #0d9488; --c3: #65a30d; --c4: #16a34a; --c5: #0284c7;
background: #040f08;
}
.mesh-bg { position: absolute; inset: 0; pointer-events: none; }
.mesh-orb {
position: absolute;
border-radius: 50%;
filter: blur(var(--mesh-blur));
opacity: 0.85;
will-change: transform;
}
.mesh-orb-1 {
width: 60%; height: 75%; top: -25%; left: -10%;
background: radial-gradient(circle at center, var(--c1) 0%, transparent 70%);
animation: mesh-float-1 12s ease-in-out infinite;
animation-delay: -3s;
}
.mesh-orb-2 {
width: 55%; height: 65%; top: -15%; right: -10%;
background: radial-gradient(circle at center, var(--c2) 0%, transparent 70%);
animation: mesh-float-2 9s ease-in-out infinite;
animation-delay: -5s;
}
.mesh-orb-3 {
width: 50%; height: 60%; bottom: -20%; left: 15%;
background: radial-gradient(circle at center, var(--c3) 0%, transparent 70%);
animation: mesh-float-3 11s ease-in-out infinite;
animation-delay: -7s;
}
.mesh-orb-4 {
width: 42%; height: 55%; top: 25%; right: 5%;
background: radial-gradient(circle at center, var(--c4) 0%, transparent 70%);
animation: mesh-float-4 14s ease-in-out infinite;
animation-delay: -2s;
}
.mesh-orb-5 {
width: 38%; height: 50%; bottom: -10%; right: -5%;
background: radial-gradient(circle at center, var(--c5) 0%, transparent 70%);
animation: mesh-float-5 8s ease-in-out infinite;
animation-delay: -4s;
}
@keyframes mesh-float-1 {
0%, 100% { transform: translate(0%, 0%) scale(1); }
50% { transform: translate(18%, 22%) scale(1.08); }
}
@keyframes mesh-float-2 {
0%, 100% { transform: translate(0%, 0%) scale(1); }
33% { transform: translate(-22%, 18%) scale(1.05); }
66% { transform: translate(8%, -12%) scale(0.95); }
}
@keyframes mesh-float-3 {
0%, 100% { transform: translate(0%, 0%) scale(1); }
50% { transform: translate(-18%, -22%) scale(1.1); }
}
@keyframes mesh-float-4 {
0%, 100% { transform: translate(0%, 0%) scale(1); }
40% { transform: translate(22%, 12%) scale(1.06); }
80% { transform: translate(-12%, -18%) scale(0.94); }
}
@keyframes mesh-float-5 {
0%, 100% { transform: translate(0%, 0%) scale(1); }
50% { transform: translate(-28%, 18%) scale(1.12); }
}
@media (prefers-reduced-motion: reduce) {
.mesh-orb { animation: none; }
}
/* Content overlay */
.mesh-card {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
padding: clamp(24px, 5vw, 44px);
pointer-events: none;
/* Re-enable clicks on interactive children: */
/* .mesh-card a, .mesh-card button { pointer-events: auto; } */
}
JavaScript(速度・ぼかし・テーマ切替)
const hero = document.querySelector('.mesh-hero');
const orbs = hero.querySelectorAll('.mesh-orb');
const BASE_DURATIONS = [12, 9, 11, 14, 8]; // seconds, one per orb
const THEME_BG = {
aurora: '#080814',
sunset: '#140a04',
forest: '#040f08',
};
function setSpeed(multiplier) {
orbs.forEach((orb, i) => {
orb.style.animationDuration = (BASE_DURATIONS[i] / multiplier) + 's';
});
}
function setBlur(px) {
hero.style.setProperty('--mesh-blur', px + 'px');
}
function setTheme(name) {
hero.classList.remove('theme-sunset', 'theme-forest');
if (name !== 'aurora') hero.classList.add('theme-' + name);
hero.style.background = THEME_BG[name] ?? THEME_BG.aurora;
}
React (JSX + CSS)
// react/M-015.jsx
import "./M-015.css";
const THEMES = {
aurora: {
bg: "#080814",
colors: ["#7c3aed", "#db2777", "#2563eb", "#6d28d9", "#0e7490"],
},
sunset: {
bg: "#140a04",
colors: ["#ea580c", "#db2777", "#d97706", "#dc2626", "#9333ea"],
},
forest: {
bg: "#040f08",
colors: ["#059669", "#0d9488", "#65a30d", "#16a34a", "#0284c7"],
},
};
const BASE_DURATIONS = [12, 9, 11, 14, 8];
const ORB_POSITIONS = [
{ top: "-25%", left: "-10%" },
{ top: "-15%", right: "-10%" },
{ bottom: "-20%", left: "15%" },
{ top: "25%", right: "5%" },
{ bottom: "-10%", right: "-5%" },
];
const ORB_SIZES = [
{ width: "60%", height: "75%" },
{ width: "55%", height: "65%" },
{ width: "50%", height: "60%" },
{ width: "42%", height: "55%" },
{ width: "38%", height: "50%" },
];
const ORB_DELAYS = ["-3s", "-5s", "-7s", "-2s", "-4s"];
export default function GradientMeshHero({
theme = "aurora",
blur = 80,
speed = 1,
badge = "Gradient Mesh",
title = "Build something beautiful",
subtitle = "Pure CSS hero background — no WebGL, no canvas.",
children,
}) {
const themeData = THEMES[theme] ?? THEMES.aurora;
return (
<div
className="mesh-hero"
style={{ background: themeData.bg, "--mesh-blur": blur + "px" }}
>
<div className="mesh-bg">
{themeData.colors.map((color, i) => (
<div
key={i}
className={`mesh-orb mesh-orb-${i + 1}`}
style={{
...ORB_SIZES[i],
...ORB_POSITIONS[i],
background: `radial-gradient(circle at center, ${color} 0%, transparent 70%)`,
animationDuration: BASE_DURATIONS[i] / speed + "s",
animationDelay: ORB_DELAYS[i],
}}
/>
))}
</div>
<div className="mesh-card">
{badge && <span className="mesh-badge">{badge}</span>}
{title && <h2 className="mesh-title">{title}</h2>}
{subtitle && <p className="mesh-sub">{subtitle}</p>}
{children}
</div>
</div>
);
}
/* react/M-015.css */
.mesh-hero {
position: relative;
overflow: hidden;
border-radius: 20px;
height: clamp(280px, 45vw, 380px);
--mesh-blur: 80px;
}
.mesh-bg {
position: absolute;
inset: 0;
pointer-events: none;
}
.mesh-orb {
position: absolute;
border-radius: 50%;
filter: blur(var(--mesh-blur));
opacity: 0.85;
will-change: transform;
}
.mesh-orb-1 { animation: mesh-float-1 12s ease-in-out infinite; }
.mesh-orb-2 { animation: mesh-float-2 9s ease-in-out infinite; }
.mesh-orb-3 { animation: mesh-float-3 11s ease-in-out infinite; }
.mesh-orb-4 { animation: mesh-float-4 14s ease-in-out infinite; }
.mesh-orb-5 { animation: mesh-float-5 8s ease-in-out infinite; }
@keyframes mesh-float-1 {
0%, 100% { transform: translate(0%, 0%) scale(1); }
50% { transform: translate(18%, 22%) scale(1.08); }
}
@keyframes mesh-float-2 {
0%, 100% { transform: translate(0%, 0%) scale(1); }
33% { transform: translate(-22%, 18%) scale(1.05); }
66% { transform: translate(8%, -12%) scale(0.95); }
}
@keyframes mesh-float-3 {
0%, 100% { transform: translate(0%, 0%) scale(1); }
50% { transform: translate(-18%, -22%) scale(1.1); }
}
@keyframes mesh-float-4 {
0%, 100% { transform: translate(0%, 0%) scale(1); }
40% { transform: translate(22%, 12%) scale(1.06); }
80% { transform: translate(-12%, -18%) scale(0.94); }
}
@keyframes mesh-float-5 {
0%, 100% { transform: translate(0%, 0%) scale(1); }
50% { transform: translate(-28%, 18%) scale(1.12); }
}
@media (prefers-reduced-motion: reduce) {
.mesh-orb { animation: none; }
}
.mesh-card {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
padding: clamp(24px, 5vw, 44px);
pointer-events: none;
}
.mesh-badge {
display: inline-flex;
align-items: center;
gap: 7px;
border-radius: 999px;
padding: 6px 14px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.92);
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.22);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
margin-bottom: 16px;
}
.mesh-title {
margin: 0 0 10px;
font-size: clamp(26px, 5vw, 44px);
line-height: 1.1;
font-weight: 700;
color: #ffffff;
text-shadow: 0 2px 24px rgba(0, 0, 0, 0.45);
}
.mesh-sub {
margin: 0;
max-width: 38ch;
font-size: clamp(13px, 2vw, 15px);
line-height: 1.65;
color: rgba(255, 255, 255, 0.7);
}
AIへの指示テンプレート AI Prompt Template
以下のテンプレートをコピーしてAIアシスタントに貼り付けると、このパターンの実装を依頼できます。 Copy the template below and paste it into your AI assistant to request an implementation of this pattern.
注意とバリエーション Notes & Variations
- パフォーマンス: `filter: blur()` は GPU アクセラレーション対象ですが、orb が大きいほど描画負荷が上がります。低スペックデバイス向けには orb 数を 3 に減らすか、`blur` を 40〜60px に抑えてください。 Performance: `filter: blur()` is GPU-accelerated but cost scales with orb size. For low-end devices, reduce to 3 orbs or lower blur to 40–60px.
- コンテンツの可読性: テキストが見づらい場合は `.mesh-card` に `background: linear-gradient(to right, rgba(0,0,0,0.45), transparent)` などのオーバーレイを追加してコントラストを確保してください。 Readability: If text is hard to read, add an overlay like `background: linear-gradient(to right, rgba(0,0,0,0.45), transparent)` to `.mesh-card` to boost contrast.
- animation-delay の負値: 各 orb に負の `animation-delay` を設定することで、ページロード直後から途中フレームから再生され、不自然な「ぴょん」とした開始を防いでいます。 Negative animation-delay: Negative delays ensure each orb starts mid-cycle at page load, preventing the jarring "jump" from the 0% keyframe.
- ライトモード版: 背景を `#fafaff` に、orb の opacity を 0.4〜0.5 に下げると、パステルカラーのライトモード背景として機能します。 Light mode variant: Set background to `#fafaff` and lower orb opacity to 0.4–0.5 for a pastel light-mode hero.
- M-014 との使い分け: CSS メッシュは実装コストが低く 3〜4 色のブランドカラーで整然とした印象を出しやすいです。より有機的なノイズパターンや GPU ベースの細かい質感が必要な場合は M-014(WebGL Shader)を選択してください。 M-014 vs M-015: CSS mesh is lower-cost and works well with 3–4 brand colors. For organic noise textures or GPU-driven fine-grain detail, choose M-014 (WebGL Shader).