スポットライト見出しヒーロー
暗い画面の上を光のスポットが移動し、通過した部分だけ見出しが色づくドラマチックなヒーロー。マウスにも追従します。プロダクトのローンチやポートフォリオの表紙に映えます。
ライブデモ
使用例(お題: SaaS FlowDesk)
この技法を「SaaS FlowDesk」というテーマのダミーサイトで実際に使った例です。
HTML
<!-- FlowDesk:新機能ローンチのスポットライト見出し -->
<section class="tsp" id="tsp">
<div class="tsp-glow" aria-hidden="true"></div>
<div class="tsp-inner">
<p class="tsp-kicker">NEW · AI ASSIST</p>
<h1 class="tsp-title">
<span class="tsp-dim" aria-hidden="true">BUILT<br>FOR FLOW</span>
<span class="tsp-lit">BUILT<br>FOR FLOW</span>
</h1>
<p class="tsp-sub">下書きから1ページ。光が通った部分だけ色づきます(マウスでも操作可)。</p>
</div>
</section>
CSS
/* FlowDesk(SaaS):スポットライト見出しヒーローの再スキン */
* { box-sizing: border-box; }
body { margin: 0; font-family: "Segoe UI", system-ui, -apple-system, sans-serif; }
.tsp {
position: relative; width: 100%; min-height: 380px;
display: grid; place-content: center; text-align: center;
padding: 40px 26px; overflow: hidden; background: #070a16;
--mx: 50%; --my: 42%;
}
.tsp-glow { position: absolute; inset: 0; z-index: 1; pointer-events: none; background: radial-gradient(240px circle at var(--mx) var(--my), rgba(79,107,255,.45), rgba(109,40,217,.14) 40%, transparent 62%); transition: background .08s linear; }
.tsp-inner { position: relative; z-index: 2; }
.tsp-kicker { margin: 0 0 18px; font-size: 11px; letter-spacing: .3em; color: #6b78a6; font-weight: 700; }
.tsp-title { position: relative; margin: 0 auto; display: inline-block; font-size: 50px; font-weight: 900; line-height: 1.04; letter-spacing: -.01em; }
.tsp-dim { color: #1b2138; }
.tsp-lit {
position: absolute; inset: 0;
background: linear-gradient(120deg, #8aa0ff, #a78bfa, #67e8f9);
-webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: transparent;
-webkit-mask-image: radial-gradient(170px circle at var(--mx) var(--my), #000 0%, rgba(0,0,0,.55) 42%, transparent 66%);
mask-image: radial-gradient(170px circle at var(--mx) var(--my), #000 0%, rgba(0,0,0,.55) 42%, transparent 66%);
}
.tsp-sub { margin: 24px auto 0; position: relative; z-index: 2; font-size: 13px; color: #828aae; max-width: 360px; line-height: 1.7; }
@media (prefers-reduced-motion: reduce) { .tsp-glow { transition: none; } }
JavaScript
// (デモと同じフックを流用)スポットライトを自動で動かし、マウスにも追従
(() => {
const el = document.getElementById('tsp');
if (!el) return;
const set = (x, y) => { el.style.setProperty('--mx', x + '%'); el.style.setProperty('--my', y + '%'); };
let auto = !matchMedia('(prefers-reduced-motion: reduce)').matches;
el.addEventListener('pointermove', (e) => {
auto = false;
const r = el.getBoundingClientRect();
set(((e.clientX - r.left) / r.width) * 100, ((e.clientY - r.top) / r.height) * 100);
});
if (auto) {
let t = 0;
const loop = () => { if (!auto) return; t += 0.018; set((50 + Math.sin(t) * 34).toFixed(1), (44 + Math.sin(t * 1.7) * 20).toFixed(1)); requestAnimationFrame(loop); };
loop();
}
})();
実装ガイド
使いどころ
プロダクトのローンチや、ポートフォリオ・新機能告知の表紙に。暗い画面上を光のスポットが移動し、通過した部分だけ見出しが色づくドラマチックなヒーローです。
実装時の注意点
見出しを暗いアウトライン層と明るいグラデ層の2枚重ねにし、明るい層に radial-gradient の mask を当てて、スポット位置だけ見せています。座標は CSS 変数 --mx/--my で、JS が自動で8の字に動かしつつ pointermove で追従。別レイヤーの発光(glow)も同じ座標を共有します。
対応ブラウザ
mask-image(-webkit- 併記)・background-clip:text・CSS 変数は全モダンブラウザで対応します。mask は Safari で -webkit-mask-image の併記が必要で、サンプルも両方記述しています。
よくある失敗
mask の半径とグラデ層のコントラストが弱いと、効果が見えません。暗背景が前提(明背景では発光が映えない)。テキストが多いとスポットが小さく感じるので、語数は絞ること。タッチ端末では追従が効かないため、自動アニメで必ず動くようにしています。
応用例
スポットを複数にする、色を時間で変える、ホバーした単語にスポットを吸着させる、スクロールでスポットを移動させるなどの拡張ができます。
コード
HTML
<!-- 光のスポットが見出しを照らすヒーロー -->
<section class="tsp" id="tsp">
<div class="tsp-glow" aria-hidden="true"></div>
<div class="tsp-inner">
<p class="tsp-kicker">IN THE SPOTLIGHT</p>
<h1 class="tsp-title">
<span class="tsp-dim" aria-hidden="true">CREATE<br>IN THE DARK</span>
<span class="tsp-lit">CREATE<br>IN THE DARK</span>
</h1>
<p class="tsp-sub">光が通り過ぎたところだけ、文字が色づきます。マウスでも操作できます。</p>
</div>
</section>
CSS
* { box-sizing: border-box; }
body { margin: 0; font-family: "Segoe UI", system-ui, -apple-system, sans-serif; }
.tsp {
position: relative;
width: 100%; min-height: 380px;
display: grid; place-content: center; text-align: center;
padding: 40px 26px; overflow: hidden;
background: #08080c;
--mx: 50%; --my: 42%;
}
/* スポットの光 */
.tsp-glow {
position: absolute; inset: 0; z-index: 1; pointer-events: none;
background: radial-gradient(240px circle at var(--mx) var(--my), rgba(124,58,237,.42), rgba(236,72,153,.12) 40%, transparent 62%);
transition: background .08s linear;
}
.tsp-inner { position: relative; z-index: 2; }
.tsp-kicker { margin: 0 0 18px; font-size: 11px; letter-spacing: .34em; color: #6b6f86; font-weight: 700; }
.tsp-title { position: relative; margin: 0 auto; display: inline-block; font-size: 50px; font-weight: 900; line-height: 1.04; letter-spacing: -.01em; }
.tsp-dim { color: #24262f; }
.tsp-lit {
position: absolute; inset: 0;
background: linear-gradient(120deg, #a78bfa, #f0abfc, #fcd34d);
-webkit-background-clip: text; background-clip: text;
-webkit-text-fill-color: transparent; color: transparent;
-webkit-mask-image: radial-gradient(170px circle at var(--mx) var(--my), #000 0%, rgba(0,0,0,.55) 42%, transparent 66%);
mask-image: radial-gradient(170px circle at var(--mx) var(--my), #000 0%, rgba(0,0,0,.55) 42%, transparent 66%);
}
.tsp-sub { margin: 24px auto 0; position: relative; z-index: 2; font-size: 13px; color: #8a8ea3; max-width: 360px; line-height: 1.7; }
@media (prefers-reduced-motion: reduce) { .tsp-glow { transition: none; } }
JavaScript
// スポットライトを自動で動かし、マウスにも追従させる
(() => {
const el = document.getElementById('tsp');
if (!el) return;
const set = (x, y) => {
el.style.setProperty('--mx', x + '%');
el.style.setProperty('--my', y + '%');
};
// マウス追従
let auto = !matchMedia('(prefers-reduced-motion: reduce)').matches;
el.addEventListener('pointermove', (e) => {
auto = false;
const r = el.getBoundingClientRect();
set(((e.clientX - r.left) / r.width) * 100, ((e.clientY - r.top) / r.height) * 100);
});
// 自動移動(8の字に近い往復)
if (auto) {
let t = 0;
const loop = () => {
if (!auto) return;
t += 0.018;
const x = 50 + Math.sin(t) * 34;
const y = 44 + Math.sin(t * 1.7) * 20;
set(x.toFixed(1), y.toFixed(1));
requestAnimationFrame(loop);
};
loop();
}
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「スポットライト見出しヒーロー」の効果を追加してください。
# 追加してほしい効果
スポットライト見出しヒーロー(タイトル周り)
暗い画面の上を光のスポットが移動し、通過した部分だけ見出しが色づくドラマチックなヒーロー。マウスにも追従します。プロダクトのローンチやポートフォリオの表紙に映えます。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 光のスポットが見出しを照らすヒーロー -->
<section class="tsp" id="tsp">
<div class="tsp-glow" aria-hidden="true"></div>
<div class="tsp-inner">
<p class="tsp-kicker">IN THE SPOTLIGHT</p>
<h1 class="tsp-title">
<span class="tsp-dim" aria-hidden="true">CREATE<br>IN THE DARK</span>
<span class="tsp-lit">CREATE<br>IN THE DARK</span>
</h1>
<p class="tsp-sub">光が通り過ぎたところだけ、文字が色づきます。マウスでも操作できます。</p>
</div>
</section>
【CSS】
* { box-sizing: border-box; }
body { margin: 0; font-family: "Segoe UI", system-ui, -apple-system, sans-serif; }
.tsp {
position: relative;
width: 100%; min-height: 380px;
display: grid; place-content: center; text-align: center;
padding: 40px 26px; overflow: hidden;
background: #08080c;
--mx: 50%; --my: 42%;
}
/* スポットの光 */
.tsp-glow {
position: absolute; inset: 0; z-index: 1; pointer-events: none;
background: radial-gradient(240px circle at var(--mx) var(--my), rgba(124,58,237,.42), rgba(236,72,153,.12) 40%, transparent 62%);
transition: background .08s linear;
}
.tsp-inner { position: relative; z-index: 2; }
.tsp-kicker { margin: 0 0 18px; font-size: 11px; letter-spacing: .34em; color: #6b6f86; font-weight: 700; }
.tsp-title { position: relative; margin: 0 auto; display: inline-block; font-size: 50px; font-weight: 900; line-height: 1.04; letter-spacing: -.01em; }
.tsp-dim { color: #24262f; }
.tsp-lit {
position: absolute; inset: 0;
background: linear-gradient(120deg, #a78bfa, #f0abfc, #fcd34d);
-webkit-background-clip: text; background-clip: text;
-webkit-text-fill-color: transparent; color: transparent;
-webkit-mask-image: radial-gradient(170px circle at var(--mx) var(--my), #000 0%, rgba(0,0,0,.55) 42%, transparent 66%);
mask-image: radial-gradient(170px circle at var(--mx) var(--my), #000 0%, rgba(0,0,0,.55) 42%, transparent 66%);
}
.tsp-sub { margin: 24px auto 0; position: relative; z-index: 2; font-size: 13px; color: #8a8ea3; max-width: 360px; line-height: 1.7; }
@media (prefers-reduced-motion: reduce) { .tsp-glow { transition: none; } }
【JavaScript】
// スポットライトを自動で動かし、マウスにも追従させる
(() => {
const el = document.getElementById('tsp');
if (!el) return;
const set = (x, y) => {
el.style.setProperty('--mx', x + '%');
el.style.setProperty('--my', y + '%');
};
// マウス追従
let auto = !matchMedia('(prefers-reduced-motion: reduce)').matches;
el.addEventListener('pointermove', (e) => {
auto = false;
const r = el.getBoundingClientRect();
set(((e.clientX - r.left) / r.width) * 100, ((e.clientY - r.top) / r.height) * 100);
});
// 自動移動(8の字に近い往復)
if (auto) {
let t = 0;
const loop = () => {
if (!auto) return;
t += 0.018;
const x = 50 + Math.sin(t) * 34;
const y = 44 + Math.sin(t * 1.7) * 20;
set(x.toFixed(1), y.toFixed(1));
requestAnimationFrame(loop);
};
loop();
}
})();
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。