【Next.js】タイムラインの作り方を解説|縦線とステップを Tailwind で

公開日: 2026年9月12日 · 読了目安 9 分

Next.js で導入の流れや沿革を縦のタイムラインで見せる方法です。Tailwind の border-l で崩れない縦線を引き、Framer Motion(motion)でステップを順番に出し、スクロールで線を伸ばすところまでコード付きで解説します。

読まれるタイムラインはどう作ればよいか

Next.js の LP に「導入までの流れ」や会社の沿革を載せようとすると、ステップの数も説明も増えがちです。

読まれるタイムラインにする答えは、次の3つです。

  • 縦に1列で並べる

  • ステップは4つまでにする

  • 各ステップは見出しと1文にまとめる

線は各ステップの左ボーダーで引き、動きは画面に入ったときの短い入場だけにします。線がスクロールに合わせて伸びる演出を足すなら、1ページに1か所までです。

  • 読みにくいタイムラインになる原因

  • Tailwind の border-l で、高さが変わっても崩れない縦線を引く方法

  • Motion(旧 Framer Motion)でステップを順番に出すコード

  • スクロールに合わせて線を伸ばす応用と、モバイルの注意点

4ステップ・1列のタイムラインが読みやすい理由

タイムラインは「順番」を伝える部品です。情報を増やすほど順番が見えなくなり、ただの長いリストになります。

左右交互のタイムラインは目線が往復する

中央の線をはさんで左右にステップを置くレイアウトは、見た目は華やかです。ただ、読者は左右に目を動かし続けることになり、モバイルではどのみち1列に戻ります。

最初から1列で設計すると、PC とスマホで同じ順番に読めます。

1ステップに段落が多いと、流れが切れる

各ステップに3段落の説明があると、次のステップまでのスクロールが長くなり、全体の流れが見えません。見出しと1文に絞り、詳しい説明は別のセクションか記事に分けます。

線や年号が本文より目立つ

線は読む順番を示すガイドです。本文より濃い色や太さにすると、線ばかりが目に入ります。

年号も大きくしすぎると見出しが負けるので、本文と同じくらいか、少し小さめの文字にします。

Tailwind で崩れない縦線のタイムラインを作る

縦線は、番号付きのリスト(ol)の左の枠線(border-l)として引きます。

絶対配置で長い線を1本置く方法は、ステップの高さが変わるたびに計算がずれ、線が足りなかったりはみ出したりしやすくなります。左の枠線なら、中身の高さに合わせて自然に伸びます。

各ステップの丸い点は、ステップ(li)を位置の基準にして(relative)、左の線の上に重ねて置きます(absolute)。

順番に意味があるので、ただのリストではなく番号付きのリストを使います。

tsx
// components/Timeline.tsx
  const steps = [
    { label: "STEP 1", title: "無料で相談する", body: "フォームから現在の課題を送ります。2営業日以内に返信します。" },
    { label: "STEP 2", title: "画面を見ながら打ち合わせ", body: "30分のオンラインで、必要な機能と期間をすり合わせます。" },
    { label: "STEP 3", title: "試用環境で確かめる", body: "実際のデータで2週間試し、社内の確認を進めます。" },
    { label: "STEP 4", title: "本番の利用を始める", body: "設定を引き継ぎ、そのまま本番で使い始めます。" },
  ];

  export function Timeline() {
    return (
      <section className="mx-auto max-w-2xl px-6 py-24">
        <h2 className="text-3xl font-semibold tracking-tight">導入までの流れ</h2>
        <ol className="mt-12 border-l border-zinc-200">
          {steps.map((step) => (
            <li key={step.label} className="relative pb-10 pl-8 last:pb-0">
              <span
                aria-hidden="true"
                className="absolute -left-[5px] top-1.5 h-2.5 w-2.5 rounded-full bg-zinc-900"
              />
              <p className="text-xs font-medium tracking-widest text-zinc-500">{step.label}</p>
              <h3 className="mt-1 text-lg font-semibold">{step.title}</h3>
              <p className="mt-2 text-sm leading-relaxed text-zinc-600">{step.body}</p>
            </li>
          ))}
        </ol>
      </section>
    );
  }

タイムラインの点の位置がずれるときの直し方

点の -left の値は「点の幅の半分+ボーダーの太さの半分」が目安です。幅 10px の点と 1px のボーダーなら、-left-[5px] でほぼ中央に乗ります。

ボーダーを border-l-2 に太くした場合は、-left-[6px] に変えてください。

Framer Motion でタイムラインのステップを順番に出す

全ステップが同時に現れると、タイムラインの「順番」が伝わりません。

各ステップは、画面に入ったときに1回だけ出します(whileInView)。出始めの時間(delay)は少しずつずらし、ずらす量は 0.08 秒ほどで十分です。

下のコードは、上のコードの各ステップを、動かせる部品(motion.li)に置き換えたものです。

読み込み元は motion パッケージの "motion/react" です。framer-motion を入れている場合は、"framer-motion" から同じ名前で使えます。

tsx
// components/AnimatedTimeline.tsx
  "use client";

  import { motion } from "motion/react";

  type Step = { label: string; title: string; body: string };

  export function AnimatedTimeline({ steps }: { steps: Step[] }) {
    return (
      <ol className="mt-12 border-l border-zinc-200">
        {steps.map((step, i) => (
          <motion.li
            key={step.label}
            className="relative pb-10 pl-8 last:pb-0"
            initial={{ opacity: 0, y: 12 }}
            whileInView={{ opacity: 1, y: 0 }}
            viewport={{ once: true, amount: 0.6 }}
            transition={{ duration: 0.4, delay: i * 0.08 }}
          >
            <span
              aria-hidden="true"
              className="absolute -left-[5px] top-1.5 h-2.5 w-2.5 rounded-full bg-zinc-900"
            />
            <p className="text-xs font-medium tracking-widest text-zinc-500">{step.label}</p>
            <h3 className="mt-1 text-lg font-semibold">{step.title}</h3>
            <p className="mt-2 text-sm leading-relaxed text-zinc-600">{step.body}</p>
          </motion.li>
        ))}
      </ol>
    );
  }

動きを減らす設定にタイムラインを合わせる

OS で動きを減らす設定をしている人には、移動をなくして表示だけにします。

そのためには、アプリ全体を MotionConfig で包み、reducedMotion="user" を指定します。上下の移動などが自動で止まります。

スクロールでタイムラインの縦線を伸ばす方法

沿革のように長いタイムラインでは、読み進めた分だけ線が伸びる演出が、現在地の目印になります。useScroll(スクロール量を読み取る仕組み)でセクション内の進み具合を取り、線の縦の伸び縮み(scaleY)に渡します。

灰色の線の上に、色の付いた線を重ねる形にします。origin-top を付けると、上から下へ伸びます。

tsx
"use client";

  import { motion, useScroll } from "motion/react";
  import { useRef, type ReactNode } from "react";

  export function ScrollLineTimeline({ children }: { children: ReactNode }) {
    const ref = useRef<HTMLDivElement>(null);
    const { scrollYProgress } = useScroll({
      target: ref,
      offset: ["start center", "end center"],
    });

    return (
      <div ref={ref} className="relative pl-8">
        <div aria-hidden="true" className="absolute left-0 top-0 h-full w-px bg-zinc-200" />
        <motion.div
          aria-hidden="true"
          className="absolute left-0 top-0 h-full w-px origin-top bg-zinc-900"
          style={{ scaleY: scrollYProgress }}
        />
        {children}
      </div>
    );
  }

線を伸ばす演出の注意点

  • ステップの入場と線の伸びを同時に強く動かすと、どちらを見ればよいか迷う。片方は控えめにする

  • 1ページに複数置くと、どこでも同じ動きが続いて単調になる。使うのは1か所まで

  • 線は aria-hidden にして、読み上げには ol の順番だけを渡す

タイムラインのモバイル対応と公開前の確認リスト

1列の設計なら、モバイルで崩れることはほとんどありません。それでも、公開前に次の点を確認してください。

  • スマホの幅で、左の余白(pl-8)が本文を狭くしすぎていない

  • 最後のステップの下に、線が余分に伸びていない(last:pb-0)

  • 年号やステップ番号が、見出しより目立っていない

  • 速くスクロールしても、ステップが出ないまま残らない(画面に入ったと判定する割合を下げると出やすい)

完成したタイムラインのセクションをコピーして使う方法

DesignLayer のコンポーネント一覧。左のカテゴリで用途ごとに絞り込める
DesignLayer のコンポーネント一覧。左のカテゴリで用途ごとに絞り込める

ここまでのコードで、読みやすいタイムラインは作れます。ただ、次の点まで整えるには、もう少し調整が必要です。

  • 年号のタイポグラフィ

  • 線と点のリズム

  • 入場の速さ

DesignLayer では、人が組んだモーション付きのセクションを、ブラウザでライブプレビューしてからコピーできます。https://design-layer.com/ja/components

ステップ名と期間だけ差し替えれば、そのまま LP に置けます。

ライブプレビューは無制限です。無料サンプル10個を無料アカウントで回数制限なくコピーでき、動画プロンプトは月1回です。Free では MCP に接続できません。カタログ全体は30日ビルドパス($19の1回払い、自動更新なし)、Lifetime Access(先着100名の$79の1回払い)、Plus(月額 $9、いつでも解約可)です。

Next.js のタイムラインについてよくある質問

  • 線と点だけなら、Tailwind のクラスで作れます。順番に出す動きや、スクロールで線を伸ばす演出を付けるときに、Motion を使うと短く書けます。

まとめ:Next.js でタイムラインを作る手順

  • タイムラインは縦1列、ステップは4つまで、各ステップは見出しと1文

  • 縦線はリストの左の枠線で引き、点はその上に重ねる

  • 画面に入ったときに、ステップを1回だけ順番に出す

  • スクロールで線を伸ばす演出は、1ページに1か所まで

スクロールで要素を出す方法は https://design-layer.com/ja/articles/nextjs-scroll-reveal-guide で解説しています。

機能紹介のセクションは https://design-layer.com/ja/articles/nextjs-features-section-guide にまとめています。

仕上がったセクションから始めたい場合は、カタログで見比べてください。https://design-layer.com/ja/components

関連ガイド