【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)。
順番に意味があるので、ただのリストではなく番号付きのリストを使います。
// 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" から同じ名前で使えます。
// 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 を付けると、上から下へ伸びます。
"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 では、人が組んだモーション付きのセクションを、ブラウザでライブプレビューしてからコピーできます。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


