タイムライン(Timeline)— 作業ログ型

データ表示 初級

このコンポーネントについて

タイムラインはプロジェクトの作業履歴や操作ログを時系列で見せるUIです。 ダッシュボードの「最近の活動」欄や、チケット管理ツールの更新履歴として広く使われています。

このページでは、タスクの完了・進行中・保留をステータスで色分けし、担当者名とともに縦1列で表示する作業ログ型タイムラインを紹介します。 JavaScriptの配列データから動的にDOMを生成するため、データの差し替えが容易です。

  • ステータス色分け — 完了(緑)・進行中(青)・保留(黄)を data-status 属性とCSSの属性セレクタで切り替える
  • 縦ライン+ドット::before 疑似要素で縦線を描画し、.tl-dot でステータスカラーの円を表示する
  • 進行中アニメーションprogress ステータスのドットに pulse アニメーションを付与して視覚的に強調
  • 動的レンダリング — タスク配列から createElement + appendChild でDOMを生成。innerHTML に変数を渡さない
  • 担当者+ステータスバッジ — 各タスクに担当者名とステータスラベルをセットで表示し、一覧の読み取りを助ける

実装のポイント・注意点

縦ラインは .tl コンテナに ::before 疑似要素で1本描画します。各 .tl-item::before で描くと最後のアイテムの下にまでラインが伸びてしまいますが、コンテナ側に1本引く方法ならその問題が起きません。

ステータスの色分けは data-status 属性とCSSの属性セレクタで実現します。JavaScriptでスタイルを直接操作するより、属性を付けてCSSに任せる方が変更しやすくコードもシンプルになります。この属性を .tl-item(アイテム全体)と .tl-badge(バッジ)の両方に設定することで、ドットとバッジを同じ仕組みで色分けできます。

進行中ドットのアニメーションは transform: scale()opacity の組み合わせで実装します。box-shadow の拡大で実装する方法もありますが、transform は GPU アクセラレーションが効きやすくパフォーマンス面で有利です。

テキストのDOM生成はすべて textContent を使い、innerHTML に変数を直接代入しません。外部から受け取ったデータを innerHTML に入れると XSS(クロスサイトスクリプティング)の脆弱性を生む恐れがあるため、コピペして使う場面でも安全な書き方を採用しています。

HTML・CSS・バニラJavaScriptのみで実装しており、フレームワーク不要でコピペすぐに動きます。

デモ

サンプルソース

3つのファイルを同じフォルダに保存し、index.html をブラウザで開くとすぐに動作確認できます。
ファイル名:index.html / style.css / script.js — 保存時の文字コードは UTF-8 を指定してください(Shift-JISだと日本語が文字化けします)。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>タイムライン サンプル</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>

<div class="tl" id="js-tl"></div>

<script src="./script.js"></script>
</body>
</html>
/* タイムライン(作業ログ型)— style.css */
*, *::before, *::after { box-sizing: border-box; }

:root {
  --tl-done:     #22C55E;
  --tl-progress: #3B82F6;
  --tl-hold:     #F59E0B;
  --tl-line:     #E2E8F0;
  --tl-hover:    #F8FAFC;
}

body {
  font-family: sans-serif;
  padding: 24px;
  max-width: 520px;
  margin: 0 auto;
  background: #fff;
  color: #1A2533;
}

/* ---- コンテナ ---- */
.tl {
  position: relative;
  padding-left: 28px;
}

/* 縦ライン(コンテナ側で1本描画) */
.tl::before {
  content: '';
  position: absolute;
  left: 6px;
  top: 14px;
  bottom: 4px;
  width: 2px;
  background: var(--tl-line);
  border-radius: 1px;
}

/* ---- 各アイテム ---- */
.tl-item {
  position: relative;
  padding: 4px 8px 24px 16px;
  border-radius: 6px;
  transition: background 0.1s;
}

.tl-item:last-child { padding-bottom: 0; }

.tl-item:hover { background: var(--tl-hover); }

/* ---- ステータスドット ---- */
.tl-dot {
  position: absolute;
  left: -27px;
  top: 8px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #CBD5E1;
}

.tl-item[data-status="done"]     .tl-dot { background: var(--tl-done); }
.tl-item[data-status="progress"] .tl-dot {
  background: var(--tl-progress);
  animation: tl-pulse 1.5s ease-in-out infinite;
}
.tl-item[data-status="hold"]     .tl-dot { background: var(--tl-hold); }

@keyframes tl-pulse {
  0%, 100% { transform: scale(1);    opacity: 1;   }
  50%       { transform: scale(1.4); opacity: 0.6; }
}

/* ---- 日時 ---- */
.tl-date {
  display: block;
  font-size: 12px;
  color: #94A3B8;
  margin-bottom: 4px;
}

/* ---- タスク名 ---- */
.tl-title {
  font-weight: 700;
  font-size: 15px;
  margin: 0 0 6px;
  color: #1A2533;
}

/* ---- 担当者+バッジ ---- */
.tl-meta {
  display: flex;
  align-items: center;
  gap: 8px;
}

.tl-assignee {
  font-size: 13px;
  color: #64748B;
}

/* ---- ステータスバッジ ---- */
.tl-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 600;
  color: #fff;
  background: #CBD5E1;
}

.tl-badge[data-status="done"]     { background: var(--tl-done); }
.tl-badge[data-status="progress"] { background: var(--tl-progress); }
.tl-badge[data-status="hold"]     { background: var(--tl-hold); }
// ============================================================
// タイムライン(作業ログ型)— script.js
// ============================================================

var tasks = [
  { date: '2026-06-06 14:30', title: 'リリース前動作確認',       assignee: '山田 太郎', status: 'done'     },
  { date: '2026-06-05 11:00', title: 'コードレビュー対応',         assignee: '佐藤 花子', status: 'done'     },
  { date: '2026-06-04 16:45', title: 'APIエラーハンドリング実装', assignee: '山田 太郎', status: 'progress' },
  { date: '2026-06-04 10:00', title: 'テストケース作成',           assignee: '鈴木 一郎', status: 'hold'     },
  { date: '2026-06-03 15:30', title: 'DB設計レビュー',             assignee: '佐藤 花子', status: 'done'     },
  { date: '2026-06-02 09:00', title: 'ワイヤーフレーム確認',       assignee: '山田 太郎', status: 'done'     },
  { date: '2026-06-01 14:00', title: '要件定義ミーティング',       assignee: '鈴木 一郎', status: 'done'     }
];

// ステータス値→表示ラベルのマッピング
var statusLabel = { done: '完了', progress: '進行中', hold: '保留' };

// タイムラインを描画する
function renderTimeline(list) {
  var container = document.getElementById('js-tl');
  // 再描画のため既存の内容を消去
  container.innerHTML = '';

  list.forEach(function(task) {
    var item = document.createElement('div');
    item.className = 'tl-item';
    item.setAttribute('data-status', task.status);

    // ステータスドット
    var dot = document.createElement('span');
    dot.className = 'tl-dot';
    item.appendChild(dot);

    // 日時
    var date = document.createElement('time');
    date.className = 'tl-date';
    date.textContent = task.date;
    item.appendChild(date);

    // タスク名
    var title = document.createElement('p');
    title.className = 'tl-title';
    title.textContent = task.title;
    item.appendChild(title);

    // 担当者+ステータスバッジ
    var meta = document.createElement('div');
    meta.className = 'tl-meta';

    var assignee = document.createElement('span');
    assignee.className = 'tl-assignee';
    assignee.textContent = task.assignee;
    meta.appendChild(assignee);

    var badge = document.createElement('span');
    badge.className = 'tl-badge';
    badge.setAttribute('data-status', task.status);
    badge.textContent = statusLabel[task.status] || task.status;
    meta.appendChild(badge);

    item.appendChild(meta);
    container.appendChild(item);
  });
}

// 初期描画
renderTimeline(tasks);

AI用プロンプト

以下のプロンプトをコピーしてAIに渡すと、同様のコンポーネントを生成できます。

ChatGPTやClaudeにこのプロンプトを渡すと、同様のコンポーネントをゼロから生成・カスタマイズできます。ライブラリ指定や項目変更など、要件を追記して使うのがおすすめです。

※ このプロンプトを使ってもデモとまったく同じ動作にならない場合があります。AIの解釈や生成タイミングによって差が出ることをご了承ください。

💡 jQuery・Vue・React など特定のライブラリで実装したい場合は、プロンプトの末尾に「〇〇を使って実装してください」と追記してください。

# タイムライン(作業ログ型)作成依頼

## 概要
プロジェクトのタスク履歴を時系列で表示する縦1列タイムラインを実装してください。
タスクの完了・進行中・保留をステータスで色分けし、日時・タスク名・担当者名とともに一覧表示します。

## 要件
- タスクデータの配列から JavaScript の DOM 操作でリストを動的生成する
- 各アイテムに日時・タスク名(太字)・担当者名・ステータスバッジを表示する
- ステータスは done(完了)/ progress(進行中)/ hold(保留)の3種類
- 左端に縦ラインを引き、各アイテムの横にステータスカラーの円(ドット)を表示する
- ステータスで色分けする(完了:緑、進行中:青、保留:黄)
- 進行中ドットには pulse アニメーション(スケール+透明度)を付与する
- アイテムのホバーで背景色を薄く変化させる

## 技術仕様
- HTML / CSS / バニラJavaScript で実装
- 外部ライブラリ:なし
- レスポンシブ対応:必要

## 動作詳細
tasks 配列は { date, title, assignee, status } のプロパティを持つオブジェクトの配列とする(新しい順に並べておく)。
renderTimeline(tasks) 関数が #js-tl 要素に .tl-item を createElement + appendChild で追加する。
data-status 属性を .tl-item と .tl-badge の両方に付与し、CSS の属性セレクタで色を切り替える。
ステータスラベルは { done: '完了', progress: '進行中', hold: '保留' } のマッピングオブジェクトで変換する。
動的データのDOM挿入は textContent を使い innerHTML に変数を直接渡さない。

## 出力形式
HTML・CSS・JavaScriptを分けて出力してください。
各ファイルは単独でコピー&ペーストして使えるよう記述してください。