ブレンドモードカーソル

mix-blend-mode:difference でカーソル下の色を反転させる円。カラフルな作品集やポートフォリオの印象的なアクセントになります。

#css#mix-blend-mode#js#interaction

ライブデモ

使用例(お題: アイドルグループ Sakura)

この技法を「アイドルグループ Sakura」というテーマのダミーサイトで実際に使った例です。

HTML
<!-- Sakura: 楽曲MVギャラリー1画面。差分ブレンドの反転カーソルを主役に -->
<div class="sk" data-blend-root>
  <header class="sk__bar">
    <span class="sk__logo">🌸 Sakura</span>
    <span class="sk__sub">MUSIC VIDEO</span>
  </header>

  <section class="sk__gallery">
    <h1 class="sk__title">最新MV「桜色サイダー」</h1>
    <div class="sk__grid">
      <div class="sk__cell sk__cell--a"><span>桜色サイダー</span></div>
      <div class="sk__cell sk__cell--b"><span>ナイトブルーム</span></div>
      <div class="sk__cell sk__cell--c"><span>はなびら Letter</span></div>
      <div class="sk__cell sk__cell--d"><span>春陽メロディ</span></div>
    </div>
    <p class="sk__hint">カーソルを重ねると色が反転します</p>
  </section>

  <!-- 主役: 差分ブレンドの反転円 -->
  <div class="blend-cursor" data-blend></div>
</div>
CSS
/* Sakura MVギャラリー: 桜ピンク + カラフルなセル(差分反転が映える) */
* { box-sizing: border-box; }
body {
  margin: 0;
  font-family: "Hiragino Sans", "Yu Gothic", system-ui, sans-serif;
  color: #3a2b30;
  overflow: hidden;
}

.sk {
  position: relative;
  height: 400px;
  background: linear-gradient(160deg, #fff 0%, #fdf4f7 100%);
  cursor: none;
  overflow: hidden;
}

/* ヘッダー */
.sk__bar {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 14px 26px;
}
.sk__logo {
  font-weight: 800;
  font-size: 17px;
  letter-spacing: .06em;
  color: #e06a92;
}
.sk__sub {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .24em;
  color: #b39aa2;
}

/* ギャラリー */
.sk__gallery {
  padding: 0 26px 18px;
}
.sk__title {
  margin: 0 0 12px;
  font-size: 18px;
  font-weight: 800;
}
.sk__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
}
.sk__cell {
  height: 210px;
  border-radius: 14px;
  display: flex;
  align-items: flex-end;
  padding: 12px;
  color: #fff;
  font-weight: 700;
  font-size: 13px;
  text-shadow: 0 1px 6px rgba(0,0,0,.3);
}
/* 反転が映えるカラフルなカバー */
.sk__cell--a { background: linear-gradient(150deg, #ff9bbb, #e0489a); }
.sk__cell--b { background: linear-gradient(150deg, #6a8bff, #3b2b7a); }
.sk__cell--c { background: linear-gradient(150deg, #ffd166, #ff7b54); }
.sk__cell--d { background: linear-gradient(150deg, #7be0c2, #2e9e8a); }

.sk__hint {
  margin: 12px 0 0;
  font-size: 11px;
  letter-spacing: .04em;
  color: #b39aa2;
}

/* 主役: 差分ブレンドの反転円 */
.blend-cursor {
  position: fixed;
  top: 0;
  left: 0;
  width: 64px;
  height: 64px;
  margin: -32px 0 0 -32px;
  border-radius: 50%;
  background: #fff;
  mix-blend-mode: difference; /* 下の色を反転 */
  pointer-events: none;
  z-index: 50;
  opacity: 0;
  transition: opacity .3s ease, width .25s ease, height .25s ease, margin .25s ease;
}
.sk.is-active .blend-cursor { opacity: 1; }
.blend-cursor.is-big {
  width: 110px;
  height: 110px;
  margin: -55px 0 0 -55px;
}
JavaScript
// Sakura: 差分ブレンドの反転カーソル。待機中はグリッド上を自動で巡回、操作で本物に追従
(() => {
  const root = document.querySelector('[data-blend-root]');
  const blend = document.querySelector('[data-blend]');
  if (!root || !blend) return; // null安全

  let px = 0, py = 0;
  let usePointer = false;
  let lastMove = 0;
  const IDLE = 1500;

  root.classList.add('is-active');

  root.addEventListener('pointermove', (e) => {
    usePointer = true;
    lastMove = performance.now();
    px = e.clientX; py = e.clientY;
  });
  // 押下中は拡大して反転範囲を強調
  root.addEventListener('pointerdown', () => blend.classList.add('is-big'));
  root.addEventListener('pointerup', () => blend.classList.remove('is-big'));
  root.addEventListener('pointerleave', () => {
    blend.classList.remove('is-big');
    usePointer = false;
  });

  // 仮想カーソルの自動経路: カラフルなセル帯を左右に横切る
  const autoPos = (t) => {
    const r = root.getBoundingClientRect();
    const cx = r.left + r.width / 2;
    const cy = r.top + r.height * 0.62; // セル帯の高さ付近
    return {
      x: cx + Math.sin(t * 0.0008) * r.width * 0.38,
      y: cy + Math.sin(t * 0.0013 + 0.6) * r.height * 0.1,
    };
  };

  const loop = (now) => {
    if (usePointer && now - lastMove > IDLE) usePointer = false;
    if (!usePointer) {
      const p = autoPos(now);
      px = p.x; py = p.y;
      // 自動時は一定周期で“ふくらむ”脈動を加える
      const big = Math.sin(now * 0.0025) > 0.6;
      blend.classList.toggle('is-big', big);
    }
    blend.style.transform = `translate(${px}px, ${py}px)`;
    requestAnimationFrame(loop);
  };
  requestAnimationFrame(loop);
})();

コード

HTML
<!-- ブレンドモードカーソル:mix-blend-mode:difference で背景色を反転する円 -->
<div class="stage" data-blend-root>
  <div class="bands">
    <span class="band b1"></span>
    <span class="band b2"></span>
    <span class="band b3"></span>
    <span class="band b4"></span>
  </div>

  <div class="content">
    <h1 class="title">BLEND MODE</h1>
    <p class="lead">カーソルが触れた色を反転させる difference ブレンド。</p>
  </div>

  <!-- ブレンドする円 -->
  <div class="blend-cursor" data-blend></div>
</div>
CSS
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }

.stage {
  position: relative;
  height: 360px;
  overflow: hidden;
  display: grid;
  place-items: center;
  background: #f4f1ea;
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
  cursor: none;
}

/* 背景に色帯を敷いてブレンドの効果を見せる */
.bands { position: absolute; inset: 0; display: flex; }
.band { flex: 1; height: 100%; }
.b1 { background: #ff5d73; }
.b2 { background: #ffd23f; }
.b3 { background: #1b9aaa; }
.b4 { background: #3d348b; }

.content {
  position: relative;
  z-index: 1;
  text-align: center;
  padding: 24px;
  pointer-events: none;
  /* 文字も difference で白く反転させ視認性を確保 */
  color: #fff;
  mix-blend-mode: difference;
}
.title {
  margin: 0 0 10px;
  font-size: clamp(34px, 8vw, 64px);
  font-weight: 900;
  letter-spacing: .08em;
}
.lead { margin: 0; font-size: 14px; font-weight: 600; }

/* ブレンドカーソル本体 */
.blend-cursor {
  position: fixed;
  top: 0; left: 0;
  width: 70px; height: 70px;
  border-radius: 50%;
  background: #fff;
  mix-blend-mode: difference; /* 下の色を反転 */
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 9999;
  opacity: 0; /* 初回移動まで非表示(隅の反転アーティファクトを防ぐ) */
  transition: width .25s ease, height .25s ease, opacity .3s ease;
  will-change: transform;
}
[data-blend-root].is-active .blend-cursor { opacity: 1; }
.blend-cursor.is-big { width: 130px; height: 130px; }
JavaScript
// ブレンドモードカーソル:円を追従させ、押下中は拡大
(() => {
  const root = document.querySelector('[data-blend-root]');
  const blend = document.querySelector('[data-blend]');
  if (!root || !blend) return; // null安全

  // 円を即時追従(transformのみ更新でGPU合成)
  root.addEventListener('pointermove', (e) => {
    blend.style.transform = `translate(${e.clientX}px, ${e.clientY}px) translate(-50%, -50%)`;
    if (!root.classList.contains('is-active')) root.classList.add('is-active');
  });

  // 押している間は拡大して反転範囲を強調
  root.addEventListener('pointerdown', () => blend.classList.add('is-big'));
  root.addEventListener('pointerup', () => blend.classList.remove('is-big'));
  root.addEventListener('pointerleave', () => {
    blend.classList.remove('is-big');
    root.classList.remove('is-active');
  });
})();

🤖 AIエージェント用プロンプト

このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「ブレンドモードカーソル」の効果を追加してください。

# 追加してほしい効果
ブレンドモードカーソル(カスタムカーソル)
mix-blend-mode:difference でカーソル下の色を反転させる円。カラフルな作品集やポートフォリオの印象的なアクセントになります。

# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- ブレンドモードカーソル:mix-blend-mode:difference で背景色を反転する円 -->
<div class="stage" data-blend-root>
  <div class="bands">
    <span class="band b1"></span>
    <span class="band b2"></span>
    <span class="band b3"></span>
    <span class="band b4"></span>
  </div>

  <div class="content">
    <h1 class="title">BLEND MODE</h1>
    <p class="lead">カーソルが触れた色を反転させる difference ブレンド。</p>
  </div>

  <!-- ブレンドする円 -->
  <div class="blend-cursor" data-blend></div>
</div>

【CSS】
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }

.stage {
  position: relative;
  height: 360px;
  overflow: hidden;
  display: grid;
  place-items: center;
  background: #f4f1ea;
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
  cursor: none;
}

/* 背景に色帯を敷いてブレンドの効果を見せる */
.bands { position: absolute; inset: 0; display: flex; }
.band { flex: 1; height: 100%; }
.b1 { background: #ff5d73; }
.b2 { background: #ffd23f; }
.b3 { background: #1b9aaa; }
.b4 { background: #3d348b; }

.content {
  position: relative;
  z-index: 1;
  text-align: center;
  padding: 24px;
  pointer-events: none;
  /* 文字も difference で白く反転させ視認性を確保 */
  color: #fff;
  mix-blend-mode: difference;
}
.title {
  margin: 0 0 10px;
  font-size: clamp(34px, 8vw, 64px);
  font-weight: 900;
  letter-spacing: .08em;
}
.lead { margin: 0; font-size: 14px; font-weight: 600; }

/* ブレンドカーソル本体 */
.blend-cursor {
  position: fixed;
  top: 0; left: 0;
  width: 70px; height: 70px;
  border-radius: 50%;
  background: #fff;
  mix-blend-mode: difference; /* 下の色を反転 */
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 9999;
  opacity: 0; /* 初回移動まで非表示(隅の反転アーティファクトを防ぐ) */
  transition: width .25s ease, height .25s ease, opacity .3s ease;
  will-change: transform;
}
[data-blend-root].is-active .blend-cursor { opacity: 1; }
.blend-cursor.is-big { width: 130px; height: 130px; }

【JavaScript】
// ブレンドモードカーソル:円を追従させ、押下中は拡大
(() => {
  const root = document.querySelector('[data-blend-root]');
  const blend = document.querySelector('[data-blend]');
  if (!root || !blend) return; // null安全

  // 円を即時追従(transformのみ更新でGPU合成)
  root.addEventListener('pointermove', (e) => {
    blend.style.transform = `translate(${e.clientX}px, ${e.clientY}px) translate(-50%, -50%)`;
    if (!root.classList.contains('is-active')) root.classList.add('is-active');
  });

  // 押している間は拡大して反転範囲を強調
  root.addEventListener('pointerdown', () => blend.classList.add('is-big'));
  root.addEventListener('pointerup', () => blend.classList.remove('is-big'));
  root.addEventListener('pointerleave', () => {
    blend.classList.remove('is-big');
    root.classList.remove('is-active');
  });
})();

# 外部ライブラリ
なし(追加ライブラリ不要)

# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。