無限マーキー

トラックを複製しtranslateX(-50%)で継ぎ目のない横スクロールループを実現。お知らせ帯やロゴ流しに使えます。

#css#javascript#marquee#loop

ライブデモ

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

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

HTML
<!-- MOON BREW:カフェのお知らせ帯を無限マーキーで流す -->
<section class="mb-stage">
  <header class="mb-nav">
    <div class="mb-brand"><span class="mb-cup">☕</span> MOON BREW</div>
    <nav class="mb-menu"><span>MENU</span><span>STORE</span><span>NEWS</span></nav>
  </header>

  <!-- お知らせ帯:トラックを複製して継ぎ目なくループ -->
  <div class="mb-marquee" id="mbMarquee" aria-label="お知らせ">
    <div class="mb-track" id="mbTrack">
      <span class="mb-item"><b>NEW</b> 季節限定「焙煎ハニーラテ」新登場</span>
      <span class="mb-dot">●</span>
      <span class="mb-item">本日 18時まで 豆20%OFF</span>
      <span class="mb-dot">●</span>
      <span class="mb-item">月見テラス席 予約受付中</span>
      <span class="mb-dot">●</span>
      <span class="mb-item">朝7時オープン モーニング全品+クッキー</span>
      <span class="mb-dot">●</span>
    </div>
  </div>

  <div class="mb-hero">
    <img class="mb-photo" src="https://picsum.photos/360/200?random=41" alt="本日のコーヒー">
    <div class="mb-card">
      <p class="mb-tag">本日のおすすめ</p>
      <h2 class="mb-h">焙煎ハニーラテ</h2>
      <p class="mb-desc">深煎り豆の余韻に、信州産はちみつの甘み。<br>月明かりのような、まろやかな一杯。</p>
      <div class="mb-price">¥620 <button class="mb-order" type="button">注文する</button></div>
    </div>
  </div>
</section>
CSS
/* MOON BREW:お知らせ帯の無限マーキー */
:root {
  --cream: #f5ede1;
  --brown: #2b1d12;
  --amber: #c98a3b;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  height: 400px;
  font-family: "Hiragino Mincho ProN", "Yu Mincho", "Segoe UI", serif;
  background: var(--cream);
  color: var(--brown);
}

.mb-stage { height: 400px; display: flex; flex-direction: column; }

.mb-nav {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 20px;
  background: var(--cream);
}
.mb-brand { font-size: 16px; font-weight: 800; letter-spacing: 0.08em; }
.mb-cup { font-size: 15px; }
.mb-menu { display: flex; gap: 16px; font-size: 11px; letter-spacing: 0.12em; color: #6b5640; }

/* マーキー帯 */
.mb-marquee {
  overflow: hidden;
  white-space: nowrap;
  background: var(--brown);
  color: var(--cream);
  padding: 9px 0;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
/* トラックを translateX(-50%) で流す(複製で継ぎ目なし) */
.mb-track {
  display: inline-flex;
  align-items: center;
  gap: 22px;
  padding-left: 22px;
  animation: mb-scroll 18s linear infinite;
  will-change: transform;
}
.mb-marquee:hover .mb-track { animation-play-state: paused; }
@keyframes mb-scroll {
  to { transform: translateX(-50%); }
}
.mb-item { font-size: 12px; letter-spacing: 0.05em; }
.mb-item b { color: var(--amber); margin-right: 4px; font-weight: 800; }
.mb-dot { font-size: 6px; color: var(--amber); }

/* ヒーロー */
.mb-hero { flex: 1; display: flex; gap: 16px; padding: 18px 20px; align-items: center; }
.mb-photo {
  width: 180px; height: 200px;
  object-fit: cover;
  border-radius: 14px;
  box-shadow: 0 16px 34px -14px rgba(43, 29, 18, 0.5);
}
.mb-card { flex: 1; }
.mb-tag {
  display: inline-block;
  font-size: 10px; letter-spacing: 0.14em;
  padding: 3px 10px; border-radius: 999px;
  background: rgba(201, 138, 59, 0.16); color: #9a6420;
  margin: 0 0 10px;
}
.mb-h { margin: 0 0 10px; font-size: 24px; font-weight: 800; }
.mb-desc { margin: 0 0 16px; font-size: 13px; line-height: 1.8; color: #5e4a36; }
.mb-price { display: flex; align-items: center; gap: 14px; font-size: 20px; font-weight: 800; color: var(--amber); }
.mb-order {
  font-family: inherit; font-size: 12px; font-weight: 700;
  padding: 9px 18px; border: none; border-radius: 999px; cursor: pointer;
  color: var(--cream); background: var(--brown);
  transition: transform 0.1s ease, background 0.2s ease;
}
.mb-order:hover { background: #44301f; }
.mb-order:active { transform: scale(0.97); }

@media (prefers-reduced-motion: reduce) {
  .mb-track { animation: none; }
}
JavaScript
// MOON BREW:トラック内の要素群を複製し、translateX(-50%) で継ぎ目なくループ
(() => {
  const track = document.getElementById("mbTrack");
  if (!track) return; // null安全

  // 現在の子要素をまるごと複製して後ろに連結
  // → トラック幅が2倍になり、-50%移動で元の並びと完全に重なる
  const items = Array.from(track.children);
  items.forEach((node) => {
    const clone = node.cloneNode(true);
    clone.setAttribute("aria-hidden", "true");
    track.appendChild(clone);
  });
})();

コード

HTML
<!-- 無限マーキー:途切れないループ流れ。ホバーで一時停止できる -->
<div class="marq-stage">
  <div class="marq-headline">
    <span class="marq-badge">LIVE</span>
    Trending Now
  </div>

  <!-- 上段:左へ流れる -->
  <div class="marquee" data-marquee>
    <div class="marquee-track">
      <span class="chip">★ デザイン</span><span class="chip">⚡ アニメーション</span>
      <span class="chip">◆ CSS</span><span class="chip">✦ UI/UX</span>
      <span class="chip">● トランジション</span><span class="chip">▲ モーション</span>
    </div>
  </div>

  <!-- 下段:右へ流れる(速度ちがい) -->
  <div class="marquee marquee--reverse marquee--slow" data-marquee>
    <div class="marquee-track">
      <span class="chip alt">JavaScript</span><span class="chip alt">SVG</span>
      <span class="chip alt">Canvas</span><span class="chip alt">WebGL</span>
      <span class="chip alt">Easing</span><span class="chip alt">Keyframes</span>
    </div>
  </div>
</div>
CSS
/* 暗い背景に発光チップが流れる */
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 360px;
  display: grid;
  align-content: center;
  gap: 18px;
  font-family: "Segoe UI", "Hiragino Sans", "Yu Gothic UI", system-ui, sans-serif;
  background: linear-gradient(160deg, #0e1020 0%, #181a33 100%);
  color: #eef0ff;
  overflow: hidden;
}
.marq-stage { display: grid; gap: 16px; }
.marq-headline {
  text-align: center;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: .04em;
  display: flex; align-items: center; justify-content: center; gap: 10px;
}
.marq-badge {
  font-size: 11px; font-weight: 800; letter-spacing: .12em;
  padding: 3px 8px; border-radius: 6px;
  background: #ff4d6d; color: #fff;
  box-shadow: 0 0 14px rgba(255, 77, 109, .6);
}

/* マーキー枠:はみ出しを隠し、左右をフェード */
.marquee {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
/* トラックを2倍幅にして同内容を複製→-50%で継ぎ目なしループ */
.marquee-track {
  display: flex;
  width: max-content;
  gap: 14px;
  padding: 4px 7px;
  animation: marqScroll 18s linear infinite;
}
.marquee--slow .marquee-track { animation-duration: 26s; }
.marquee--reverse .marquee-track { animation-direction: reverse; }

@keyframes marqScroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* ホバーで全マーキー停止 */
.marquee:hover .marquee-track { animation-play-state: paused; }

.chip {
  flex: none;
  padding: 10px 18px;
  border-radius: 999px;
  font-size: 14px; font-weight: 600;
  white-space: nowrap;
  background: rgba(124, 139, 255, 0.16);
  border: 1px solid rgba(124, 139, 255, 0.32);
  color: #dfe3ff;
}
.chip.alt {
  background: rgba(109, 240, 194, 0.12);
  border-color: rgba(109, 240, 194, 0.34);
  color: #c9ffe9;
}

@media (prefers-reduced-motion: reduce) {
  .marquee-track { animation: none; }
}
JavaScript
// 無限マーキー:トラック内容を複製し、-50%移動で継ぎ目のないループにする
(() => {
  const marquees = document.querySelectorAll('[data-marquee]');
  if (!marquees.length) return; // null安全

  marquees.forEach((marq) => {
    const track = marq.querySelector('.marquee-track');
    if (!track) return;
    // 元の子要素を丸ごと複製して連結(合計幅が2倍→-50%で元位置に戻る)
    const clone = track.cloneNode(true);
    clone.setAttribute('aria-hidden', 'true');
    track.append(...Array.from(clone.children));
  });
})();

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

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

# 追加してほしい効果
無限マーキー(アニメーション & トランジション)
トラックを複製しtranslateX(-50%)で継ぎ目のない横スクロールループを実現。お知らせ帯やロゴ流しに使えます。

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

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 無限マーキー:途切れないループ流れ。ホバーで一時停止できる -->
<div class="marq-stage">
  <div class="marq-headline">
    <span class="marq-badge">LIVE</span>
    Trending Now
  </div>

  <!-- 上段:左へ流れる -->
  <div class="marquee" data-marquee>
    <div class="marquee-track">
      <span class="chip">★ デザイン</span><span class="chip">⚡ アニメーション</span>
      <span class="chip">◆ CSS</span><span class="chip">✦ UI/UX</span>
      <span class="chip">● トランジション</span><span class="chip">▲ モーション</span>
    </div>
  </div>

  <!-- 下段:右へ流れる(速度ちがい) -->
  <div class="marquee marquee--reverse marquee--slow" data-marquee>
    <div class="marquee-track">
      <span class="chip alt">JavaScript</span><span class="chip alt">SVG</span>
      <span class="chip alt">Canvas</span><span class="chip alt">WebGL</span>
      <span class="chip alt">Easing</span><span class="chip alt">Keyframes</span>
    </div>
  </div>
</div>

【CSS】
/* 暗い背景に発光チップが流れる */
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 360px;
  display: grid;
  align-content: center;
  gap: 18px;
  font-family: "Segoe UI", "Hiragino Sans", "Yu Gothic UI", system-ui, sans-serif;
  background: linear-gradient(160deg, #0e1020 0%, #181a33 100%);
  color: #eef0ff;
  overflow: hidden;
}
.marq-stage { display: grid; gap: 16px; }
.marq-headline {
  text-align: center;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: .04em;
  display: flex; align-items: center; justify-content: center; gap: 10px;
}
.marq-badge {
  font-size: 11px; font-weight: 800; letter-spacing: .12em;
  padding: 3px 8px; border-radius: 6px;
  background: #ff4d6d; color: #fff;
  box-shadow: 0 0 14px rgba(255, 77, 109, .6);
}

/* マーキー枠:はみ出しを隠し、左右をフェード */
.marquee {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
/* トラックを2倍幅にして同内容を複製→-50%で継ぎ目なしループ */
.marquee-track {
  display: flex;
  width: max-content;
  gap: 14px;
  padding: 4px 7px;
  animation: marqScroll 18s linear infinite;
}
.marquee--slow .marquee-track { animation-duration: 26s; }
.marquee--reverse .marquee-track { animation-direction: reverse; }

@keyframes marqScroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* ホバーで全マーキー停止 */
.marquee:hover .marquee-track { animation-play-state: paused; }

.chip {
  flex: none;
  padding: 10px 18px;
  border-radius: 999px;
  font-size: 14px; font-weight: 600;
  white-space: nowrap;
  background: rgba(124, 139, 255, 0.16);
  border: 1px solid rgba(124, 139, 255, 0.32);
  color: #dfe3ff;
}
.chip.alt {
  background: rgba(109, 240, 194, 0.12);
  border-color: rgba(109, 240, 194, 0.34);
  color: #c9ffe9;
}

@media (prefers-reduced-motion: reduce) {
  .marquee-track { animation: none; }
}

【JavaScript】
// 無限マーキー:トラック内容を複製し、-50%移動で継ぎ目のないループにする
(() => {
  const marquees = document.querySelectorAll('[data-marquee]');
  if (!marquees.length) return; // null安全

  marquees.forEach((marq) => {
    const track = marq.querySelector('.marquee-track');
    if (!track) return;
    // 元の子要素を丸ごと複製して連結(合計幅が2倍→-50%で元位置に戻る)
    const clone = track.cloneNode(true);
    clone.setAttribute('aria-hidden', 'true');
    track.append(...Array.from(clone.children));
  });
})();

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

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