横タイムライン

横方向にスクロールする年表タイムライン。年ごとの節目を線でつなぎ、現在地のノードだけ光の輪でパルスさせて強調表示します。

#css#timeline

ライブデモ

コード

HTML
<!-- 横タイムライン: 横スクロールの年表。現在地のノードだけ強調表示 -->
<section class="tlh">
  <div class="tlh__head">
    <p class="tlh__eyebrow">BRAND HISTORY</p>
    <h2 class="tlh__title">ブランドヒストリー</h2>
  </div>

  <div class="tlh__scroll" tabindex="0" role="region" aria-label="年表(横にスクロールできます)">
    <ol class="tlh__track">
      <li class="tlh__node">
        <span class="tlh__year">2021</span>
        <span class="tlh__pin" aria-hidden="true"></span>
        <span class="tlh__label">創業</span>
        <span class="tlh__note">3人でサービス開発をスタート</span>
      </li>
      <li class="tlh__node">
        <span class="tlh__year">2022</span>
        <span class="tlh__pin" aria-hidden="true"></span>
        <span class="tlh__label">初期リリース</span>
        <span class="tlh__note">最初のβ版を公開</span>
      </li>
      <li class="tlh__node">
        <span class="tlh__year">2023</span>
        <span class="tlh__pin" aria-hidden="true"></span>
        <span class="tlh__label">資金調達</span>
        <span class="tlh__note">シリーズAを完了</span>
      </li>
      <li class="tlh__node">
        <span class="tlh__year">2024</span>
        <span class="tlh__pin" aria-hidden="true"></span>
        <span class="tlh__label">海外展開</span>
        <span class="tlh__note">アジア圏でサービス開始</span>
      </li>
      <li class="tlh__node tlh__node--now">
        <span class="tlh__year">2026</span>
        <span class="tlh__pin" aria-hidden="true"></span>
        <span class="tlh__label">現在地</span>
        <span class="tlh__note">月間利用者50万人を突破</span>
        <span class="tlh__now-badge">NOW</span>
      </li>
      <li class="tlh__node tlh__node--future">
        <span class="tlh__year">2027</span>
        <span class="tlh__pin" aria-hidden="true"></span>
        <span class="tlh__label">次のフェーズ(予定)</span>
        <span class="tlh__note">新プロダクトを開発中</span>
      </li>
    </ol>
  </div>
</section>
CSS
/* 横タイムライン: 横スクロールの年表。現在地のノードだけ光の輪でパルスする */
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 26px 0;
  font-family: "Segoe UI", "Hiragino Kaku Gothic ProN", "Yu Gothic UI", system-ui, sans-serif;
  background: radial-gradient(120% 140% at 50% -20%, #1c2350 0%, #0a0c18 60%);
  color: #eceffb;
  overflow-x: hidden;
}

/* ルート: 全幅フル */
.tlh { width: 100%; }

.tlh__head { text-align: center; padding: 0 20px; margin-bottom: 18px; }
.tlh__eyebrow {
  margin: 0 0 6px;
  font-size: 11px; font-weight: 700; letter-spacing: .24em;
  color: #7fb2ff;
}
.tlh__title {
  margin: 0;
  font-size: clamp(19px, 3vw, 24px);
  font-weight: 800;
  letter-spacing: -.01em;
}

.tlh__scroll {
  width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 6px 0 18px;
  cursor: grab;
  scrollbar-width: thin;
  scrollbar-color: rgba(127, 178, 255, .4) transparent;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 3%, #000 97%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 3%, #000 97%, transparent);
}
.tlh__scroll:active { cursor: grabbing; }
.tlh__scroll::-webkit-scrollbar { height: 6px; }
.tlh__scroll::-webkit-scrollbar-thumb { background: rgba(127, 178, 255, .35); border-radius: 999px; }

.tlh__track {
  position: relative;
  display: flex;
  list-style: none;
  margin: 0;
  padding: 30px 48px 4px;
  width: max-content;
  min-width: 100%;
}
/* 各ノードを貫く水平線 */
.tlh__track::before {
  content: "";
  position: absolute;
  left: 30px; right: 30px; top: 63px;
  height: 2px;
  background: linear-gradient(90deg, rgba(127, 178, 255, .12), #7fb2ff 12%, #7fb2ff 82%, rgba(127, 178, 255, .12));
}

.tlh__node {
  position: relative;
  flex: 0 0 auto;
  width: 188px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 0 14px;
  opacity: 0;
  transform: translateY(14px);
  animation: tlhIn .6s cubic-bezier(.22, 1, .36, 1) forwards;
}
.tlh__node:nth-child(1) { animation-delay: .05s; }
.tlh__node:nth-child(2) { animation-delay: .15s; }
.tlh__node:nth-child(3) { animation-delay: .25s; }
.tlh__node:nth-child(4) { animation-delay: .35s; }
.tlh__node:nth-child(5) { animation-delay: .45s; }
.tlh__node:nth-child(6) { animation-delay: .55s; }

.tlh__year {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .06em;
  color: #9aa6d6;
  margin-bottom: 10px;
}
.tlh__pin {
  position: relative;
  z-index: 1;
  width: 13px; height: 13px;
  border-radius: 50%;
  background: #7fb2ff;
  box-shadow: 0 0 0 5px #0a0c18;
  margin-bottom: 12px;
}
.tlh__label {
  font-size: 14px;
  font-weight: 800;
  color: #f2f4ff;
  margin-bottom: 4px;
}
.tlh__note {
  font-size: 11.5px;
  line-height: 1.55;
  color: #a6acc9;
}

/* 現在地: 大きめのパルスするノード */
.tlh__node--now .tlh__pin {
  width: 17px; height: 17px;
  background: linear-gradient(135deg, #6de0ff, #7c8bff);
  animation: tlhPulse 1.8s ease-out infinite;
}
.tlh__node--now .tlh__year,
.tlh__node--now .tlh__label { color: #8fe3ff; }
.tlh__now-badge {
  display: inline-block;
  margin-top: 8px;
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: .12em;
  color: #0a0c18;
  background: linear-gradient(120deg, #6de0ff, #7c8bff);
  padding: 3px 9px;
  border-radius: 999px;
}

/* 未来(予定): 破線で控えめに */
.tlh__node--future .tlh__pin {
  background: transparent;
  border: 2px dashed #6a7296;
  box-shadow: none;
}
.tlh__node--future .tlh__label,
.tlh__node--future .tlh__note { opacity: .65; }

@keyframes tlhIn {
  to { opacity: 1; transform: translateY(0); }
}
@keyframes tlhPulse {
  0%   { box-shadow: 0 0 0 5px #0a0c18, 0 0 0 5px rgba(109, 224, 255, .45); }
  70%  { box-shadow: 0 0 0 5px #0a0c18, 0 0 0 15px rgba(109, 224, 255, 0); }
  100% { box-shadow: 0 0 0 5px #0a0c18, 0 0 0 15px rgba(109, 224, 255, 0); }
}

@media (max-width: 480px) {
  .tlh__node { width: 156px; padding: 0 10px; }
  .tlh__track { padding: 26px 24px 4px; }
  .tlh__track::before { left: 16px; right: 16px; top: 59px; }
}

@media (prefers-reduced-motion: reduce) {
  .tlh__node { animation: none !important; opacity: 1; transform: none; }
  .tlh__node--now .tlh__pin { animation: none; }
}
JavaScript
// 横タイムライン: 初期表示で「現在地」ノードが見える位置へスクロールし、
// ドラッグでも横スクロールできるようにする(無くても表示・操作は成立する補助機能)
(() => {
  const scroller = document.querySelector('.tlh__scroll');
  const now = document.querySelector('.tlh__node--now');
  if (!scroller) return; // null安全

  // 現在地ノードをスクロール領域の中央付近に合わせる
  if (now) {
    const target = Math.max(0, now.offsetLeft - scroller.clientWidth / 2 + now.offsetWidth / 2);
    scroller.scrollLeft = target;
  }

  // ポインタドラッグで横スクロール
  let dragging = false, startX = 0, startScroll = 0;
  scroller.addEventListener('pointerdown', (e) => {
    dragging = true; startX = e.clientX; startScroll = scroller.scrollLeft;
    scroller.setPointerCapture(e.pointerId);
  });
  scroller.addEventListener('pointermove', (e) => {
    if (!dragging) return;
    scroller.scrollLeft = startScroll - (e.clientX - startX);
  });
  const endDrag = () => { dragging = false; };
  scroller.addEventListener('pointerup', endDrag);
  scroller.addEventListener('pointercancel', endDrag);
  scroller.addEventListener('pointerleave', endDrag);
})();

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

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

# 追加してほしい効果
横タイムライン(タイムライン & 404)
横方向にスクロールする年表タイムライン。年ごとの節目を線でつなぎ、現在地のノードだけ光の輪でパルスさせて強調表示します。

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

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 横タイムライン: 横スクロールの年表。現在地のノードだけ強調表示 -->
<section class="tlh">
  <div class="tlh__head">
    <p class="tlh__eyebrow">BRAND HISTORY</p>
    <h2 class="tlh__title">ブランドヒストリー</h2>
  </div>

  <div class="tlh__scroll" tabindex="0" role="region" aria-label="年表(横にスクロールできます)">
    <ol class="tlh__track">
      <li class="tlh__node">
        <span class="tlh__year">2021</span>
        <span class="tlh__pin" aria-hidden="true"></span>
        <span class="tlh__label">創業</span>
        <span class="tlh__note">3人でサービス開発をスタート</span>
      </li>
      <li class="tlh__node">
        <span class="tlh__year">2022</span>
        <span class="tlh__pin" aria-hidden="true"></span>
        <span class="tlh__label">初期リリース</span>
        <span class="tlh__note">最初のβ版を公開</span>
      </li>
      <li class="tlh__node">
        <span class="tlh__year">2023</span>
        <span class="tlh__pin" aria-hidden="true"></span>
        <span class="tlh__label">資金調達</span>
        <span class="tlh__note">シリーズAを完了</span>
      </li>
      <li class="tlh__node">
        <span class="tlh__year">2024</span>
        <span class="tlh__pin" aria-hidden="true"></span>
        <span class="tlh__label">海外展開</span>
        <span class="tlh__note">アジア圏でサービス開始</span>
      </li>
      <li class="tlh__node tlh__node--now">
        <span class="tlh__year">2026</span>
        <span class="tlh__pin" aria-hidden="true"></span>
        <span class="tlh__label">現在地</span>
        <span class="tlh__note">月間利用者50万人を突破</span>
        <span class="tlh__now-badge">NOW</span>
      </li>
      <li class="tlh__node tlh__node--future">
        <span class="tlh__year">2027</span>
        <span class="tlh__pin" aria-hidden="true"></span>
        <span class="tlh__label">次のフェーズ(予定)</span>
        <span class="tlh__note">新プロダクトを開発中</span>
      </li>
    </ol>
  </div>
</section>

【CSS】
/* 横タイムライン: 横スクロールの年表。現在地のノードだけ光の輪でパルスする */
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 26px 0;
  font-family: "Segoe UI", "Hiragino Kaku Gothic ProN", "Yu Gothic UI", system-ui, sans-serif;
  background: radial-gradient(120% 140% at 50% -20%, #1c2350 0%, #0a0c18 60%);
  color: #eceffb;
  overflow-x: hidden;
}

/* ルート: 全幅フル */
.tlh { width: 100%; }

.tlh__head { text-align: center; padding: 0 20px; margin-bottom: 18px; }
.tlh__eyebrow {
  margin: 0 0 6px;
  font-size: 11px; font-weight: 700; letter-spacing: .24em;
  color: #7fb2ff;
}
.tlh__title {
  margin: 0;
  font-size: clamp(19px, 3vw, 24px);
  font-weight: 800;
  letter-spacing: -.01em;
}

.tlh__scroll {
  width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 6px 0 18px;
  cursor: grab;
  scrollbar-width: thin;
  scrollbar-color: rgba(127, 178, 255, .4) transparent;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 3%, #000 97%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 3%, #000 97%, transparent);
}
.tlh__scroll:active { cursor: grabbing; }
.tlh__scroll::-webkit-scrollbar { height: 6px; }
.tlh__scroll::-webkit-scrollbar-thumb { background: rgba(127, 178, 255, .35); border-radius: 999px; }

.tlh__track {
  position: relative;
  display: flex;
  list-style: none;
  margin: 0;
  padding: 30px 48px 4px;
  width: max-content;
  min-width: 100%;
}
/* 各ノードを貫く水平線 */
.tlh__track::before {
  content: "";
  position: absolute;
  left: 30px; right: 30px; top: 63px;
  height: 2px;
  background: linear-gradient(90deg, rgba(127, 178, 255, .12), #7fb2ff 12%, #7fb2ff 82%, rgba(127, 178, 255, .12));
}

.tlh__node {
  position: relative;
  flex: 0 0 auto;
  width: 188px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 0 14px;
  opacity: 0;
  transform: translateY(14px);
  animation: tlhIn .6s cubic-bezier(.22, 1, .36, 1) forwards;
}
.tlh__node:nth-child(1) { animation-delay: .05s; }
.tlh__node:nth-child(2) { animation-delay: .15s; }
.tlh__node:nth-child(3) { animation-delay: .25s; }
.tlh__node:nth-child(4) { animation-delay: .35s; }
.tlh__node:nth-child(5) { animation-delay: .45s; }
.tlh__node:nth-child(6) { animation-delay: .55s; }

.tlh__year {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .06em;
  color: #9aa6d6;
  margin-bottom: 10px;
}
.tlh__pin {
  position: relative;
  z-index: 1;
  width: 13px; height: 13px;
  border-radius: 50%;
  background: #7fb2ff;
  box-shadow: 0 0 0 5px #0a0c18;
  margin-bottom: 12px;
}
.tlh__label {
  font-size: 14px;
  font-weight: 800;
  color: #f2f4ff;
  margin-bottom: 4px;
}
.tlh__note {
  font-size: 11.5px;
  line-height: 1.55;
  color: #a6acc9;
}

/* 現在地: 大きめのパルスするノード */
.tlh__node--now .tlh__pin {
  width: 17px; height: 17px;
  background: linear-gradient(135deg, #6de0ff, #7c8bff);
  animation: tlhPulse 1.8s ease-out infinite;
}
.tlh__node--now .tlh__year,
.tlh__node--now .tlh__label { color: #8fe3ff; }
.tlh__now-badge {
  display: inline-block;
  margin-top: 8px;
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: .12em;
  color: #0a0c18;
  background: linear-gradient(120deg, #6de0ff, #7c8bff);
  padding: 3px 9px;
  border-radius: 999px;
}

/* 未来(予定): 破線で控えめに */
.tlh__node--future .tlh__pin {
  background: transparent;
  border: 2px dashed #6a7296;
  box-shadow: none;
}
.tlh__node--future .tlh__label,
.tlh__node--future .tlh__note { opacity: .65; }

@keyframes tlhIn {
  to { opacity: 1; transform: translateY(0); }
}
@keyframes tlhPulse {
  0%   { box-shadow: 0 0 0 5px #0a0c18, 0 0 0 5px rgba(109, 224, 255, .45); }
  70%  { box-shadow: 0 0 0 5px #0a0c18, 0 0 0 15px rgba(109, 224, 255, 0); }
  100% { box-shadow: 0 0 0 5px #0a0c18, 0 0 0 15px rgba(109, 224, 255, 0); }
}

@media (max-width: 480px) {
  .tlh__node { width: 156px; padding: 0 10px; }
  .tlh__track { padding: 26px 24px 4px; }
  .tlh__track::before { left: 16px; right: 16px; top: 59px; }
}

@media (prefers-reduced-motion: reduce) {
  .tlh__node { animation: none !important; opacity: 1; transform: none; }
  .tlh__node--now .tlh__pin { animation: none; }
}

【JavaScript】
// 横タイムライン: 初期表示で「現在地」ノードが見える位置へスクロールし、
// ドラッグでも横スクロールできるようにする(無くても表示・操作は成立する補助機能)
(() => {
  const scroller = document.querySelector('.tlh__scroll');
  const now = document.querySelector('.tlh__node--now');
  if (!scroller) return; // null安全

  // 現在地ノードをスクロール領域の中央付近に合わせる
  if (now) {
    const target = Math.max(0, now.offsetLeft - scroller.clientWidth / 2 + now.offsetWidth / 2);
    scroller.scrollLeft = target;
  }

  // ポインタドラッグで横スクロール
  let dragging = false, startX = 0, startScroll = 0;
  scroller.addEventListener('pointerdown', (e) => {
    dragging = true; startX = e.clientX; startScroll = scroller.scrollLeft;
    scroller.setPointerCapture(e.pointerId);
  });
  scroller.addEventListener('pointermove', (e) => {
    if (!dragging) return;
    scroller.scrollLeft = startScroll - (e.clientX - startX);
  });
  const endDrag = () => { dragging = false; };
  scroller.addEventListener('pointerup', endDrag);
  scroller.addEventListener('pointercancel', endDrag);
  scroller.addEventListener('pointerleave', endDrag);
})();

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

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