サポートチャット吹き出し

右下に常駐するチャットボタンをクリックすると、ミニチャット窓が開く問い合わせウィジェット。よくある質問や有人サポートへの入口として、コンバージョンや満足度の向上に使われます。

#sticky#chat#support#widget

ライブデモ

使用例(お題: カフェ MOON BREW)

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

HTML
<!-- MOON BREW:カフェのサポートチャット吹き出し -->
<div class="sch-frame">
  <div class="sch-stage">
    <h1>ご注文サポート</h1>
    <p>右下のボタンでチャット窓が開きます。</p>
  </div>

  <div class="sch-widget" id="schWidget">
    <div class="sch-panel" role="dialog" aria-label="サポートチャット">
      <div class="sch-head">
        <span class="sch-avatar" aria-hidden="true">☕</span>
        <div class="sch-who"><b>MOON BREW</b><small>通常5分以内に返信</small></div>
      </div>
      <div class="sch-msgs">
        <div class="sch-msg sch-msg--bot">こんにちは!ご注文はお決まりですか? ☕</div>
        <div class="sch-msg sch-msg--me">おすすめの浅煎りは?</div>
        <div class="sch-msg sch-msg--bot">今月はエチオピア・イルガチェフェが人気です。明るい酸味が魅力ですよ。</div>
      </div>
      <div class="sch-input">
        <input type="text" placeholder="メッセージを入力…" aria-label="メッセージ">
        <button class="sch-send" type="button" aria-label="送信">➤</button>
      </div>
    </div>
    <button class="sch-fab" id="schFab" type="button" aria-label="チャットを開く">
      <span class="sch-fab__chat" aria-hidden="true">💬</span>
      <span class="sch-fab__close" aria-hidden="true">✕</span>
      <span class="sch-badge" id="schBadge">1</span>
    </button>
  </div>
</div>
CSS
/* MOON BREW(カフェ):サポートチャット吹き出しの再スキン */
* { box-sizing: border-box; }
body { margin: 0; font-family: "Segoe UI", system-ui, -apple-system, sans-serif; }

.sch-frame { position: relative; width: 100%; height: 380px; overflow: hidden; background: linear-gradient(135deg, #f3ead9, #f8f4ec); }
.sch-stage { display: grid; place-content: center; height: 100%; text-align: center; color: #4a3320; padding: 0 24px; }
.sch-stage h1 { margin: 0; font-size: 26px; font-weight: 800; font-family: "Hiragino Mincho ProN", serif; }
.sch-stage p { margin: 12px 0 0; font-size: 13px; line-height: 1.8; color: #8a715a; }

.sch-widget { position: absolute; right: 20px; bottom: 20px; z-index: 10; }
.sch-panel { position: absolute; right: 0; bottom: 70px; width: 280px; background: #fffdf8; border-radius: 16px; overflow: hidden; box-shadow: 0 24px 50px rgba(90,60,30,.24); border: 1px solid #ece2d0; transform-origin: bottom right; transform: scale(.7) translateY(10px); opacity: 0; pointer-events: none; transition: transform .3s cubic-bezier(.2,.8,.2,1), opacity .25s ease; }
.sch-widget.is-open .sch-panel { transform: scale(1) translateY(0); opacity: 1; pointer-events: auto; }
.sch-head { display: flex; align-items: center; gap: 10px; padding: 13px 15px; background: linear-gradient(135deg, #7a4f2a, #b08c5e); color: #fff; }
.sch-avatar { width: 34px; height: 34px; border-radius: 50%; background: rgba(255,255,255,.22); display: grid; place-items: center; font-size: 16px; }
.sch-who b { display: block; font-size: 14px; }
.sch-who small { font-size: 11px; opacity: .85; }
.sch-msgs { padding: 14px; display: flex; flex-direction: column; gap: 9px; max-height: 170px; overflow-y: auto; background: #faf6ef; }
.sch-msg { max-width: 82%; font-size: 12.5px; line-height: 1.5; padding: 9px 12px; border-radius: 13px; }
.sch-msg--bot { align-self: flex-start; background: #fff; color: #4a3320; border: 1px solid #ece2d0; border-bottom-left-radius: 4px; }
.sch-msg--me { align-self: flex-end; background: #7a4f2a; color: #fff; border-bottom-right-radius: 4px; }
.sch-input { display: flex; gap: 8px; padding: 10px 12px; border-top: 1px solid #ece2d0; background: #fffdf8; }
.sch-input input { flex: 1; min-width: 0; font: inherit; font-size: 12.5px; border: 1px solid #e0d4bf; border-radius: 999px; padding: 8px 13px; outline: none; }
.sch-send { flex: 0 0 auto; width: 34px; height: 34px; border: none; border-radius: 50%; background: #7a4f2a; color: #fff; cursor: pointer; }
.sch-fab { position: relative; width: 58px; height: 58px; border: none; border-radius: 50%; cursor: pointer; background: linear-gradient(135deg, #7a4f2a, #b08c5e); color: #fff; box-shadow: 0 14px 30px rgba(122,79,42,.45); display: grid; place-items: center; transition: transform .2s ease; }
.sch-fab:hover { transform: scale(1.06); }
.sch-fab__chat, .sch-fab__close { grid-area: 1 / 1; font-size: 22px; transition: opacity .2s ease, transform .3s ease; }
.sch-fab__close { opacity: 0; transform: rotate(-90deg); font-size: 18px; }
.sch-widget.is-open .sch-fab__chat { opacity: 0; transform: rotate(90deg); }
.sch-widget.is-open .sch-fab__close { opacity: 1; transform: rotate(0); }
.sch-badge { position: absolute; top: -2px; right: -2px; min-width: 20px; height: 20px; border-radius: 999px; background: #d97742; color: #fff; font-size: 11px; font-weight: 800; display: grid; place-items: center; border: 2px solid #f3ead9; padding: 0 4px; }
.sch-widget.is-open .sch-badge { display: none; }

@media (prefers-reduced-motion: reduce) { .sch-panel, .sch-fab, .sch-fab__chat, .sch-fab__close { transition: none; } }
JavaScript
// (デモと同じフックを流用)チャット窓の開閉+自動実演
(() => {
  const widget = document.getElementById('schWidget');
  const fab = document.getElementById('schFab');
  if (!widget || !fab) return;
  const set = (open) => { widget.classList.toggle('is-open', open); fab.setAttribute('aria-label', open ? 'チャットを閉じる' : 'チャットを開く'); };
  let auto = !matchMedia('(prefers-reduced-motion: reduce)').matches;
  fab.addEventListener('click', () => { auto = false; set(!widget.classList.contains('is-open')); });
  if (auto) {
    let open = false;
    const tick = () => { if (!auto) return; open = !open; set(open); setTimeout(tick, open ? 3000 : 1700); };
    setTimeout(tick, 1300);
  }
})();

実装ガイド

使いどころ

問い合わせやサポートの入口として。右下に常駐するボタンを押すと、ミニチャット窓が開くウィジェットです。

実装時の注意点

チャット窓は transform-origin:bottom right の scale+opacity でポップに開閉。FABのアイコンは💬↔✕でモーフし、未読バッジは開くと消えます。JS は開閉とプレビュー用の自動開閉のみ。

対応ブラウザ

transform/opacity アニメーションは全モダンブラウザ対応。実運用のチャットSDK連携時も土台は同じです。

よくある失敗

他の固定要素(戻る↑等)と重ならない位置に。開いた状態で背面スクロールやフォーカスを適切に扱うこと。モバイルでは窓が画面幅に収まるよう調整します。常時アニメで気を散らさないよう未読時のみ強調を。

応用例

有人/ボット切替、よくある質問のクイック返信、画像添付、営業時間外の自動応答などに展開できます。

コード

HTML
<!-- 右下に常駐するチャット吹き出しウィジェット -->
<div class="sch-frame">
  <div class="sch-stage">
    <h1>サポートチャット</h1>
    <p>右下のボタンでチャット窓が開きます。<br>(自動でも開閉を実演します)</p>
  </div>

  <div class="sch-widget" id="schWidget">
    <div class="sch-panel" role="dialog" aria-label="サポートチャット">
      <div class="sch-head">
        <span class="sch-avatar" aria-hidden="true">◍</span>
        <div class="sch-who"><b>サポート</b><small>通常5分以内に返信</small></div>
      </div>
      <div class="sch-msgs">
        <div class="sch-msg sch-msg--bot">こんにちは!ご用件をどうぞ 👋</div>
        <div class="sch-msg sch-msg--me">料金プランを知りたいです</div>
        <div class="sch-msg sch-msg--bot">3つのプランがあります。用途を教えていただければ最適なものをご案内します。</div>
      </div>
      <div class="sch-input">
        <input type="text" placeholder="メッセージを入力…" aria-label="メッセージ">
        <button class="sch-send" type="button" aria-label="送信">➤</button>
      </div>
    </div>
    <button class="sch-fab" id="schFab" type="button" aria-label="チャットを開く">
      <span class="sch-fab__chat" aria-hidden="true">💬</span>
      <span class="sch-fab__close" aria-hidden="true">✕</span>
      <span class="sch-badge" id="schBadge">1</span>
    </button>
  </div>
</div>
CSS
* { box-sizing: border-box; }
body { margin: 0; font-family: "Segoe UI", system-ui, -apple-system, sans-serif; }

.sch-frame { position: relative; width: 100%; height: 380px; overflow: hidden; background: linear-gradient(135deg, #e8edff, #f6f7fc); }
.sch-stage { display: grid; place-content: center; height: 100%; text-align: center; color: #2a3050; padding: 0 24px; }
.sch-stage h1 { margin: 0; font-size: 26px; font-weight: 800; }
.sch-stage p { margin: 12px 0 0; font-size: 13px; line-height: 1.8; color: #6a7090; }

.sch-widget { position: absolute; right: 20px; bottom: 20px; z-index: 10; }

/* チャット窓 */
.sch-panel {
  position: absolute; right: 0; bottom: 70px; width: 280px;
  background: #fff; border-radius: 16px; overflow: hidden;
  box-shadow: 0 24px 50px rgba(20,24,50,.24); border: 1px solid #e6e9f2;
  transform-origin: bottom right; transform: scale(.7) translateY(10px); opacity: 0; pointer-events: none;
  transition: transform .3s cubic-bezier(.2,.8,.2,1), opacity .25s ease;
}
.sch-widget.is-open .sch-panel { transform: scale(1) translateY(0); opacity: 1; pointer-events: auto; }

.sch-head { display: flex; align-items: center; gap: 10px; padding: 13px 15px; background: linear-gradient(135deg, #4f46e5, #7c3aed); color: #fff; }
.sch-avatar { width: 34px; height: 34px; border-radius: 50%; background: rgba(255,255,255,.22); display: grid; place-items: center; font-size: 16px; }
.sch-who b { display: block; font-size: 14px; }
.sch-who small { font-size: 11px; opacity: .85; }

.sch-msgs { padding: 14px; display: flex; flex-direction: column; gap: 9px; max-height: 170px; overflow-y: auto; background: #f7f8fc; }
.sch-msg { max-width: 82%; font-size: 12.5px; line-height: 1.5; padding: 9px 12px; border-radius: 13px; }
.sch-msg--bot { align-self: flex-start; background: #fff; color: #2a3050; border: 1px solid #e8ebf3; border-bottom-left-radius: 4px; }
.sch-msg--me { align-self: flex-end; background: #4f46e5; color: #fff; border-bottom-right-radius: 4px; }

.sch-input { display: flex; gap: 8px; padding: 10px 12px; border-top: 1px solid #eceef5; background: #fff; }
.sch-input input { flex: 1; min-width: 0; font: inherit; font-size: 12.5px; border: 1px solid #e2e5ef; border-radius: 999px; padding: 8px 13px; outline: none; }
.sch-send { flex: 0 0 auto; width: 34px; height: 34px; border: none; border-radius: 50%; background: #4f46e5; color: #fff; cursor: pointer; }

/* FAB */
.sch-fab { position: relative; width: 58px; height: 58px; border: none; border-radius: 50%; cursor: pointer; background: linear-gradient(135deg, #4f46e5, #7c3aed); color: #fff; box-shadow: 0 14px 30px rgba(79,70,229,.45); display: grid; place-items: center; transition: transform .2s ease; }
.sch-fab:hover { transform: scale(1.06); }
.sch-fab__chat, .sch-fab__close { grid-area: 1 / 1; font-size: 22px; transition: opacity .2s ease, transform .3s ease; }
.sch-fab__close { opacity: 0; transform: rotate(-90deg); font-size: 18px; }
.sch-widget.is-open .sch-fab__chat { opacity: 0; transform: rotate(90deg); }
.sch-widget.is-open .sch-fab__close { opacity: 1; transform: rotate(0); }
.sch-badge { position: absolute; top: -2px; right: -2px; min-width: 20px; height: 20px; border-radius: 999px; background: #f43f5e; color: #fff; font-size: 11px; font-weight: 800; display: grid; place-items: center; border: 2px solid #eef1ff; padding: 0 4px; }
.sch-widget.is-open .sch-badge { display: none; }

@media (prefers-reduced-motion: reduce) { .sch-panel, .sch-fab, .sch-fab__chat, .sch-fab__close { transition: none; } }
JavaScript
// チャット窓の開閉。プレビューでは自動でも開閉を実演
(() => {
  const widget = document.getElementById('schWidget');
  const fab = document.getElementById('schFab');
  if (!widget || !fab) return;
  const set = (open) => { widget.classList.toggle('is-open', open); fab.setAttribute('aria-label', open ? 'チャットを閉じる' : 'チャットを開く'); };

  let auto = !matchMedia('(prefers-reduced-motion: reduce)').matches;
  fab.addEventListener('click', () => { auto = false; set(!widget.classList.contains('is-open')); });

  if (auto) {
    let open = false;
    const tick = () => { if (!auto) return; open = !open; set(open); setTimeout(tick, open ? 3000 : 1700); };
    setTimeout(tick, 1300);
  }
})();

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

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

# 追加してほしい効果
サポートチャット吹き出し(追従ウィジェット)
右下に常駐するチャットボタンをクリックすると、ミニチャット窓が開く問い合わせウィジェット。よくある質問や有人サポートへの入口として、コンバージョンや満足度の向上に使われます。

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

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 右下に常駐するチャット吹き出しウィジェット -->
<div class="sch-frame">
  <div class="sch-stage">
    <h1>サポートチャット</h1>
    <p>右下のボタンでチャット窓が開きます。<br>(自動でも開閉を実演します)</p>
  </div>

  <div class="sch-widget" id="schWidget">
    <div class="sch-panel" role="dialog" aria-label="サポートチャット">
      <div class="sch-head">
        <span class="sch-avatar" aria-hidden="true">◍</span>
        <div class="sch-who"><b>サポート</b><small>通常5分以内に返信</small></div>
      </div>
      <div class="sch-msgs">
        <div class="sch-msg sch-msg--bot">こんにちは!ご用件をどうぞ 👋</div>
        <div class="sch-msg sch-msg--me">料金プランを知りたいです</div>
        <div class="sch-msg sch-msg--bot">3つのプランがあります。用途を教えていただければ最適なものをご案内します。</div>
      </div>
      <div class="sch-input">
        <input type="text" placeholder="メッセージを入力…" aria-label="メッセージ">
        <button class="sch-send" type="button" aria-label="送信">➤</button>
      </div>
    </div>
    <button class="sch-fab" id="schFab" type="button" aria-label="チャットを開く">
      <span class="sch-fab__chat" aria-hidden="true">💬</span>
      <span class="sch-fab__close" aria-hidden="true">✕</span>
      <span class="sch-badge" id="schBadge">1</span>
    </button>
  </div>
</div>

【CSS】
* { box-sizing: border-box; }
body { margin: 0; font-family: "Segoe UI", system-ui, -apple-system, sans-serif; }

.sch-frame { position: relative; width: 100%; height: 380px; overflow: hidden; background: linear-gradient(135deg, #e8edff, #f6f7fc); }
.sch-stage { display: grid; place-content: center; height: 100%; text-align: center; color: #2a3050; padding: 0 24px; }
.sch-stage h1 { margin: 0; font-size: 26px; font-weight: 800; }
.sch-stage p { margin: 12px 0 0; font-size: 13px; line-height: 1.8; color: #6a7090; }

.sch-widget { position: absolute; right: 20px; bottom: 20px; z-index: 10; }

/* チャット窓 */
.sch-panel {
  position: absolute; right: 0; bottom: 70px; width: 280px;
  background: #fff; border-radius: 16px; overflow: hidden;
  box-shadow: 0 24px 50px rgba(20,24,50,.24); border: 1px solid #e6e9f2;
  transform-origin: bottom right; transform: scale(.7) translateY(10px); opacity: 0; pointer-events: none;
  transition: transform .3s cubic-bezier(.2,.8,.2,1), opacity .25s ease;
}
.sch-widget.is-open .sch-panel { transform: scale(1) translateY(0); opacity: 1; pointer-events: auto; }

.sch-head { display: flex; align-items: center; gap: 10px; padding: 13px 15px; background: linear-gradient(135deg, #4f46e5, #7c3aed); color: #fff; }
.sch-avatar { width: 34px; height: 34px; border-radius: 50%; background: rgba(255,255,255,.22); display: grid; place-items: center; font-size: 16px; }
.sch-who b { display: block; font-size: 14px; }
.sch-who small { font-size: 11px; opacity: .85; }

.sch-msgs { padding: 14px; display: flex; flex-direction: column; gap: 9px; max-height: 170px; overflow-y: auto; background: #f7f8fc; }
.sch-msg { max-width: 82%; font-size: 12.5px; line-height: 1.5; padding: 9px 12px; border-radius: 13px; }
.sch-msg--bot { align-self: flex-start; background: #fff; color: #2a3050; border: 1px solid #e8ebf3; border-bottom-left-radius: 4px; }
.sch-msg--me { align-self: flex-end; background: #4f46e5; color: #fff; border-bottom-right-radius: 4px; }

.sch-input { display: flex; gap: 8px; padding: 10px 12px; border-top: 1px solid #eceef5; background: #fff; }
.sch-input input { flex: 1; min-width: 0; font: inherit; font-size: 12.5px; border: 1px solid #e2e5ef; border-radius: 999px; padding: 8px 13px; outline: none; }
.sch-send { flex: 0 0 auto; width: 34px; height: 34px; border: none; border-radius: 50%; background: #4f46e5; color: #fff; cursor: pointer; }

/* FAB */
.sch-fab { position: relative; width: 58px; height: 58px; border: none; border-radius: 50%; cursor: pointer; background: linear-gradient(135deg, #4f46e5, #7c3aed); color: #fff; box-shadow: 0 14px 30px rgba(79,70,229,.45); display: grid; place-items: center; transition: transform .2s ease; }
.sch-fab:hover { transform: scale(1.06); }
.sch-fab__chat, .sch-fab__close { grid-area: 1 / 1; font-size: 22px; transition: opacity .2s ease, transform .3s ease; }
.sch-fab__close { opacity: 0; transform: rotate(-90deg); font-size: 18px; }
.sch-widget.is-open .sch-fab__chat { opacity: 0; transform: rotate(90deg); }
.sch-widget.is-open .sch-fab__close { opacity: 1; transform: rotate(0); }
.sch-badge { position: absolute; top: -2px; right: -2px; min-width: 20px; height: 20px; border-radius: 999px; background: #f43f5e; color: #fff; font-size: 11px; font-weight: 800; display: grid; place-items: center; border: 2px solid #eef1ff; padding: 0 4px; }
.sch-widget.is-open .sch-badge { display: none; }

@media (prefers-reduced-motion: reduce) { .sch-panel, .sch-fab, .sch-fab__chat, .sch-fab__close { transition: none; } }

【JavaScript】
// チャット窓の開閉。プレビューでは自動でも開閉を実演
(() => {
  const widget = document.getElementById('schWidget');
  const fab = document.getElementById('schFab');
  if (!widget || !fab) return;
  const set = (open) => { widget.classList.toggle('is-open', open); fab.setAttribute('aria-label', open ? 'チャットを閉じる' : 'チャットを開く'); };

  let auto = !matchMedia('(prefers-reduced-motion: reduce)').matches;
  fab.addEventListener('click', () => { auto = false; set(!widget.classList.contains('is-open')); });

  if (auto) {
    let open = false;
    const tick = () => { if (!auto) return; open = !open; set(open); setTimeout(tick, open ? 3000 : 1700); };
    setTimeout(tick, 1300);
  }
})();

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

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