ライブデモ 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.

Gradient Mesh

Build something beautiful

Pure CSS hero background — no WebGL, no canvas,
just blurred radial gradients in motion.

1.0×
80px

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

実装 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