mix-blend-mode テキスト

difference ブレンドで背景色を反転して切り抜く大型タイポ。マウス追従の光スポットで色が躍動します。LP のキービジュアルに。

#css#mix-blend-mode#typography#js

ライブデモ

使用例(お題: SaaS FlowDesk)

この技法を「SaaS FlowDesk」というテーマのダミーサイトで実際に使った例です。

HTML
<!-- FlowDesk:プロダクトLPのキービジュアル(difference 大型タイポ) -->
<section class="fd-blend">
  <!-- ナビ -->
  <nav class="fd-blend__nav">
    <span class="fd-blend__logo">▰ FlowDesk</span>
    <a class="fd-blend__login" href="#">ログイン</a>
  </nav>

  <!-- 動く背景(製品写真+ブランドグラデ) -->
  <div class="fd-blend__bg"></div>
  <!-- マウス追従の光スポット -->
  <div class="fd-blend__spot" aria-hidden="true"></div>

  <!-- difference で背景を反転して切り抜く大型コピー -->
  <h1 class="fd-blend__text">WORK<br>IN FLOW</h1>
  <p class="fd-blend__sub">チームの仕事を、ひとつの流れに。</p>
  <a class="fd-blend__cta" href="#">無料で試す →</a>
</section>
CSS
/* FlowDesk:difference 大型タイポのキービジュアル */
:root {
  --navy: #0f1b34;
  --blue: #4f7cff;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  height: 400px;
  font-family: "Segoe UI", "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
  overflow: hidden;
}

.fd-blend {
  position: relative;
  width: 100%;
  height: 400px;
  overflow: hidden;
  background: var(--navy);
  isolation: isolate;
}

/* ナビ */
.fd-blend__nav {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 30px;
}
.fd-blend__logo { font-size: 16px; font-weight: 800; letter-spacing: 0.04em; color: #fff; }
.fd-blend__login {
  font-size: 12px;
  color: #fff;
  text-decoration: none;
  opacity: 0.85;
}
.fd-blend__login:hover { opacity: 1; }

/* 背景:製品写真にブランドグラデを重ねて動かす */
.fd-blend__bg {
  position: absolute;
  inset: -10%;
  background:
    linear-gradient(120deg, rgba(15,27,52,0.85), rgba(79,124,255,0.6)),
    url("https://picsum.photos/900/600?random=71") center/cover no-repeat;
  animation: fdDrift 16s ease-in-out infinite alternate;
}
@keyframes fdDrift {
  0% { transform: scale(1.1) translate(-2%, -1%); }
  100% { transform: scale(1.18) translate(2%, 1%); }
}

/* マウス追従スポット(white でテキストの difference を躍動させる) */
.fd-blend__spot {
  position: absolute;
  width: 320px;
  height: 320px;
  left: var(--mx, 50%);
  top: var(--my, 45%);
  transform: translate(-50%, -50%);
  z-index: 2;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(120,170,255,0.95), rgba(120,170,255,0) 65%);
  pointer-events: none;
  mix-blend-mode: screen;
}

/* difference で背景を反転して切り抜く大型タイポ */
.fd-blend__text {
  position: absolute;
  left: 30px;
  top: 78px;
  z-index: 3;
  margin: 0;
  font-size: 92px;
  line-height: 0.92;
  font-weight: 900;
  letter-spacing: -0.02em;
  color: #fff;
  mix-blend-mode: difference;
  pointer-events: none;
}
.fd-blend__sub {
  position: absolute;
  left: 32px;
  bottom: 64px;
  z-index: 3;
  margin: 0;
  font-size: 14px;
  color: rgba(255,255,255,0.92);
}
.fd-blend__cta {
  position: absolute;
  left: 32px;
  bottom: 26px;
  z-index: 3;
  display: inline-block;
  padding: 11px 22px;
  border-radius: 10px;
  background: var(--blue);
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  box-shadow: 0 10px 24px rgba(79,124,255,0.5);
  transition: transform 0.2s ease;
}
.fd-blend__cta:hover { transform: translateY(-2px); }

@media (prefers-reduced-motion: reduce) {
  .fd-blend__bg { animation: none; }
  .fd-blend__cta { transition: none; }
}
JavaScript
// マウス位置を --mx/--my に渡し、光のスポットを追従させる
const blend = document.querySelector(".fd-blend");

if (blend) {
  const move = (clientX, clientY) => {
    const r = blend.getBoundingClientRect();
    if (!r.width || !r.height) return;
    blend.style.setProperty("--mx", `${clientX - r.left}px`);
    blend.style.setProperty("--my", `${clientY - r.top}px`);
  };
  blend.addEventListener("pointermove", (e) => move(e.clientX, e.clientY));

  // タッチ環境では円を自動巡回(操作なしでも動きを見せる)
  if (!window.matchMedia("(pointer: fine)").matches) {
    const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (!reduce) {
      let t = 0;
      setInterval(() => {
        t += 0.04;
        const r = blend.getBoundingClientRect();
        const x = r.width / 2 + Math.cos(t) * r.width * 0.3;
        const y = r.height / 2 + Math.sin(t * 1.3) * r.height * 0.3;
        blend.style.setProperty("--mx", `${x}px`);
        blend.style.setProperty("--my", `${y}px`);
      }, 40);
    }
  }
}

コード

HTML
<!-- mix-blend-mode で背景を反転表示する追従テキスト -->
<div class="blend">
  <!-- 動く背景(CSSグラデのみ) -->
  <div class="blend__bg"></div>
  <!-- マウスに追従する光の円 -->
  <div class="blend__spot" aria-hidden="true"></div>
  <!-- difference で背景色を反転して切り抜くテキスト -->
  <h1 class="blend__text">BLEND<br>MODE</h1>
</div>
CSS
* { box-sizing: border-box; }
body { margin: 0; }

.blend {
  position: relative;
  width: 100%;
  height: 360px;
  overflow: hidden;
  background: #0d0d12;
  font-family: "Arial Black", "Segoe UI", system-ui, sans-serif;
  isolation: isolate; /* blend をこの枠に閉じ込める */
}

/* グラデ背景がゆっくり流れる */
.blend__bg {
  position: absolute;
  inset: -20%;
  background: linear-gradient(115deg, #ff006e, #fb5607, #ffbe0b, #3a86ff, #8338ec);
  background-size: 300% 300%;
  animation: flow 12s ease infinite;
  filter: saturate(1.1);
}
@keyframes flow {
  0%   { background-position: 0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

/* マウス追従スポット(明るい円) */
.blend__spot {
  position: absolute;
  left: var(--mx, 50%);
  top: var(--my, 50%);
  width: 260px;
  height: 260px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, #fff, rgba(255, 255, 255, 0) 70%);
  mix-blend-mode: overlay;
  pointer-events: none;
  transition: transform .12s ease-out;
}

/* difference で背景を反転表示する文字 */
.blend__text {
  position: absolute;
  inset: 0;
  margin: 0;
  display: grid;
  place-content: center;
  text-align: center;
  line-height: .92;
  font-size: clamp(56px, 16vw, 130px);
  font-weight: 900;
  letter-spacing: -.02em;
  color: #fff;
  mix-blend-mode: difference; /* ここが肝 */
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .blend__bg { animation: none; }
  .blend__spot { transition: none; }
}
JavaScript
// マウス位置を --mx/--my に渡し、光のスポットを追従させる
const blend = document.querySelector(".blend");

if (blend) {
  const move = (clientX, clientY) => {
    const r = blend.getBoundingClientRect();
    blend.style.setProperty("--mx", `${clientX - r.left}px`);
    blend.style.setProperty("--my", `${clientY - r.top}px`);
  };

  blend.addEventListener("pointermove", (e) => move(e.clientX, e.clientY));

  // タッチ環境では自動で円を巡回させる(操作なしでも動きを見せる)
  if (!window.matchMedia("(pointer: fine)").matches) {
    let t = 0;
    const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (!reduce) {
      setInterval(() => {
        t += 0.04;
        const r = blend.getBoundingClientRect();
        const x = r.width / 2 + Math.cos(t) * r.width * 0.3;
        const y = r.height / 2 + Math.sin(t * 1.3) * r.height * 0.3;
        blend.style.setProperty("--mx", `${x}px`);
        blend.style.setProperty("--my", `${y}px`);
      }, 40);
    }
  }
}

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

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

# 追加してほしい効果
mix-blend-mode テキスト(画像エフェクト)
difference ブレンドで背景色を反転して切り抜く大型タイポ。マウス追従の光スポットで色が躍動します。LP のキービジュアルに。

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

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- mix-blend-mode で背景を反転表示する追従テキスト -->
<div class="blend">
  <!-- 動く背景(CSSグラデのみ) -->
  <div class="blend__bg"></div>
  <!-- マウスに追従する光の円 -->
  <div class="blend__spot" aria-hidden="true"></div>
  <!-- difference で背景色を反転して切り抜くテキスト -->
  <h1 class="blend__text">BLEND<br>MODE</h1>
</div>

【CSS】
* { box-sizing: border-box; }
body { margin: 0; }

.blend {
  position: relative;
  width: 100%;
  height: 360px;
  overflow: hidden;
  background: #0d0d12;
  font-family: "Arial Black", "Segoe UI", system-ui, sans-serif;
  isolation: isolate; /* blend をこの枠に閉じ込める */
}

/* グラデ背景がゆっくり流れる */
.blend__bg {
  position: absolute;
  inset: -20%;
  background: linear-gradient(115deg, #ff006e, #fb5607, #ffbe0b, #3a86ff, #8338ec);
  background-size: 300% 300%;
  animation: flow 12s ease infinite;
  filter: saturate(1.1);
}
@keyframes flow {
  0%   { background-position: 0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

/* マウス追従スポット(明るい円) */
.blend__spot {
  position: absolute;
  left: var(--mx, 50%);
  top: var(--my, 50%);
  width: 260px;
  height: 260px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, #fff, rgba(255, 255, 255, 0) 70%);
  mix-blend-mode: overlay;
  pointer-events: none;
  transition: transform .12s ease-out;
}

/* difference で背景を反転表示する文字 */
.blend__text {
  position: absolute;
  inset: 0;
  margin: 0;
  display: grid;
  place-content: center;
  text-align: center;
  line-height: .92;
  font-size: clamp(56px, 16vw, 130px);
  font-weight: 900;
  letter-spacing: -.02em;
  color: #fff;
  mix-blend-mode: difference; /* ここが肝 */
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .blend__bg { animation: none; }
  .blend__spot { transition: none; }
}

【JavaScript】
// マウス位置を --mx/--my に渡し、光のスポットを追従させる
const blend = document.querySelector(".blend");

if (blend) {
  const move = (clientX, clientY) => {
    const r = blend.getBoundingClientRect();
    blend.style.setProperty("--mx", `${clientX - r.left}px`);
    blend.style.setProperty("--my", `${clientY - r.top}px`);
  };

  blend.addEventListener("pointermove", (e) => move(e.clientX, e.clientY));

  // タッチ環境では自動で円を巡回させる(操作なしでも動きを見せる)
  if (!window.matchMedia("(pointer: fine)").matches) {
    let t = 0;
    const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (!reduce) {
      setInterval(() => {
        t += 0.04;
        const r = blend.getBoundingClientRect();
        const x = r.width / 2 + Math.cos(t) * r.width * 0.3;
        const y = r.height / 2 + Math.sin(t * 1.3) * r.height * 0.3;
        blend.style.setProperty("--mx", `${x}px`);
        blend.style.setProperty("--my", `${y}px`);
      }, 40);
    }
  }
}

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

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