GODRICH

ダッシュボード コンポーネント9選 — 画面ごとじゃなくこの部品だけ

ダッシュボード コンポーネントとは、完成した管理画面をまるごと組み込む代わりに、ヘッダーやフィード、カレンダーのように画面の一部品だけをそのまま自分のプロジェクトに移せるUI部品です。画面ごとではなくこの部品だけで、九つのうち一つは Ctrl(⌘)+K で開くコマンドパレットまで矢印キーで実際に動きます。

自動再生 · タイルを押すとその項目へ · すべて1つのzip

九つはよく使われる順ではなく、実際にダッシュボードを使う人がたどる順番で並べました。今どこにいるかを示すヘッダーから始まり、最近のアクティビティを確認し、予定(カレンダー)とタスク(カンバン)を管理してから目標の進捗を見て、協業(チーム・検索)と整理(タグ・ファイル)で締めくくります。完成した管理画面テンプレートではなくこの部品一つだけを取り出せるように、各デモは画面中央にウィジェット一つだけを置いています。

01パンくず + ページヘッダー

ケバブ(⋮)ボタンを押すと右上を起点に実際にメニューが拡大しながら開き、外側をクリックするか Esc を押すと実際に閉じます。完成した管理画面なしにパンくずとアクションメニューだけが必要なページに向いています。

transform-originclickaria-expanded
function close() {
  wrap.classList.remove('is-open');
  kebab.setAttribute('aria-expanded', 'false');
}
kebab.addEventListener('click', function (e) {
  document.querySelector('.ph').classList.remove('is-demo');
  e.stopPropagation();
  var open = wrap.classList.toggle('is-open');
  kebab.setAttribute('aria-expanded', String(open));
});
document.addEventListener('click', function (e) {
  if (!e.target.closest('.ph__menu-wrap')) close();
});

02アクティビティフィード タイムライン

「新しい活動を見る」を押すと隠れていた項目が実際に上へスライドして現れ、完了・コメント・アップロードの種類ごとに色の違う丸アイコンで区別します。

translateYopacityclick
.af__item--new {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity $duration $easing, transform $duration $easing;
}
.af__item--new.af__item--shown { opacity: 1; transform: translateY(0); }
.af__item--new.is-demo { animation: af-in 2s $easing infinite; }
@keyframes af-in {
  0%   { opacity: 0; transform: translateY(10px); }
  35%  { opacity: 1; transform: translateY(0); }
  80%  { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(10px); }
}

03月間カレンダーウィジェット

矢印を押すと実際に月が切り替わり、Date オブジェクトでその月のマスを再描画します。予定がある日は点で、今日は枠線で示されます。

Dateclickaria-pressed
document.querySelectorAll('.cal__nav').forEach(function (btn) {
  btn.addEventListener('click', function () {
    cur.m += Number(btn.dataset.dir);
    if (cur.m < 0) { cur.m = 11; cur.y--; }
    if (cur.m > 11) { cur.m = 0; cur.y++; }
    build();
  });
});

04ミニ カンバンボード ウィジェット

カードの矢印ボタンを押すとそのカードが実際に次の列のDOMへ移動し、最後の列に着くと矢印の代わりにチェックアイコンに変わります。ドラッグライブラリなしで進捗3列だけを見せたいときの部品です。

transformappendChildclick
@keyframes kb-move {
  0%   { transform: translateX(0) translateY(0); opacity: .9; }
  45%  { transform: translateX(96px) translateY(-3px); opacity: .9; }
  90%  { transform: translateX(192px) translateY(0); opacity: .9; }
  96%  { transform: translateX(192px) translateY(0); opacity: 0; }
  100% { transform: translateX(0) translateY(0); opacity: 0; }
}

05目標進捗ステップカード

「次のステップ」ボタンを押すとバーが実際にその分だけ埋まり、通過したステップにチェックアイコンが付きます。最後のステップに達するとボタンは消えます。

scaleXtransform-originclick
function apply() {
  steps.forEach(function (s, i) { s.classList.toggle('is-active', i <= idx); });
  fill.style.transform = 'scaleX(' + (Number(steps[idx].dataset.p) / 100) + ')';
  pct.textContent = (idx + 1) + ' / ' + steps.length;
  btn.hidden = idx === steps.length - 1;
}
btn.addEventListener('click', function () {
  document.querySelector('.gt').classList.remove('is-demo');
  idx = Math.min(idx + 1, steps.length - 1);
  apply();
});

06チームメンバーリスト(検索)

名前を入力するとその文字を含む行だけが実際に残り、残りは実際に隠れます。オンライン状態の点はずっと薄い輪となって広がっては消え続けます。

keyuphiddenscale
input.addEventListener('input', function () {
  var q = input.value.trim().toLowerCase();
  var shown = 0;
  rows.forEach(function (r) {
    var hit = r.dataset.name.toLowerCase().indexOf(q) !== -1;
    r.hidden = !hit;
    if (hit) shown++;
  });
  empty.hidden = shown !== 0;
});

07コマンドパレット(⌘K)

ボタンを押すか Ctrl(⌘)+K を押すとパネルが実際に画面中央で拡大しながら開き、入力した文字を含むコマンドだけが実際に残ります。方向キーと Enter で選べます。

keydownEscapescale
function filter() {
  var q = input.value.trim().toLowerCase();
  items.forEach(function (it) { it.hidden = q !== '' && it.dataset.kw.toLowerCase().indexOf(q) === -1; });
  var vis = visibleItems();
  empty.hidden = vis.length !== 0;
  setActive(0, vis);
}

08タグ管理入力ウィジェット

入力して追加を押すと(または Enter で)新しいタグチップが実際にリストの末尾に滑り込み、チップの×を押すとそのタグだけが実際に消えます。

translateXopacityclick
function addTag() {
  var v = input.value.trim();
  if (!v) return;
  var li = document.createElement('li');
  li.className = 'tg__chip';
  li.dataset.tag = v;
  li.textContent = v;
  var rm = document.createElement('button');
  rm.type = 'button'; rm.className = 'tg__remove'; rm.textContent = '×';
  rm.setAttribute('aria-label', v + ' を削除');
  li.appendChild(rm);
  wireRemove(li);
  list.appendChild(li);
  input.value = '';
  input.focus({ preventScroll: true });
}

09ファイルアップロード ドロップゾーン

領域にファイルをドラッグするかクリックして選ぶと進捗バーが実際に0から満タンまで伸び、完了するとチェックアイコンに変わります。

dragoverscaleXdrop
function addFile(name) {
  var li = document.createElement('li');
  li.className = 'fu__file';
  var nameEl = document.createElement('span'); nameEl.className = 'fu__file-name'; nameEl.textContent = name;
  var track = document.createElement('span'); track.className = 'fu__track';
  var fill = document.createElement('span'); fill.className = 'fu__fill';
  track.appendChild(fill);
  var rm = document.createElement('button'); rm.type = 'button'; rm.className = 'fu__remove';
  rm.setAttribute('aria-label', name + ' を削除'); rm.textContent = '×';
  rm.addEventListener('click', function () { li.remove(); });
  li.appendChild(nameEl); li.appendChild(track); li.appendChild(rm);
  list.appendChild(li);
  requestAnimationFrame(function () { fill.style.transform = 'scaleX(1)'; });
  setTimeout(function () { li.classList.add('fu__file--done'); }, 900);
}

どこで壊れるか — 落とし穴

九つを作っていて実際にはまった落とし穴は、ケバブメニューのように自分の箱の外へはみ出す要素でした。480×300のデスクトップ表示では問題なかったのに、320×200のスマホ幅で測り直すとドロップダウンが画面の半分をスクロール領域に変えてしまいました。overflow: hidden で見た目には隠れていても、scrollHeight はその下にある実際のレイアウトサイズをそのまま測ってしまうということで、メニューの項目数を減らして位置を調整してからようやく通りました。カンバンボードの幽霊カードも似た落とし穴でした——三つの列を瞬間移動させると、撮影区間のほとんどでアニメーションが止まって見え、計測スクリプトに「動いていない」と判定されました。三回の飛び移りではなく2秒かけて連続的に滑らせるよう変えてから、九つ全部が実際に動いていると計測されるようになりました。翻訳辞書にも小さな落とし穴がありました——カンバン(04)と目標カード(05)がどちらも「完了」という同じ文字を使っていて、辞書はこの一語を一度だけ登録すれば両方の項目が通ることに、九つ全部を訳し終えてから後から気づきました。カレンダー(03)の今日の輪とチームリスト(06)のオンライン表示の波紋は、コードは別々でも「薄い輪が広がって消える」という同じキーフレームの形をそれぞれ実装しています。この一件のおかげで、九つのデモ全体に散らばる残り七十語ほども項目ごとではなく文字列そのもの単位で整理し直し、抜けをずっと見つけやすくなりました。ダウンロード用パスワード usrg4zqs で開く zip の中身にはこれらの修正がすべて反映されています。

アクセシビリティ

九つとも prefers-reduced-motion: reduce では、ケバブメニューの自動オープン、フィード項目のスライドイン、今日の日付の脈打つ輪、幽霊カードの移動、ステップチェックの順次点灯、オンライン表示の波紋、パレットの開閉、新規タグチップ、アップロード進捗といった装飾用の自動ループをすべて止めますが、実際の状態値はそのまま残します。ケバブメニュー(01)とコマンドパレット(07)はそれぞれ role="menu"role="dialog"aria-expandedaria-modal を備え、外側クリックや Esc で閉じます。カレンダー(03)の日付ボタンは aria-pressed で選択状態を伝えます。チームリスト(06)の検索欄とタグ管理(08)の削除ボタンはそれぞれ aria-label で何を検索・削除するかをスクリーンリーダーに伝えます。上の落とし穴で触れた計算方法は MDN の Element.scrollHeight のページにまとまっています。

ほかのダッシュボード素材はダッシュボードカテゴリーにあり、このサイト全体が何を扱っているかは紹介ページにあります。

FAQ

完成した管理画面ではなくこの部品一つだけ使ってもいいですか?

はい、そのために九つに分けて作りました。各デモは管理画面全体ではなく部品一つだけを画面中央に置いており、zip の中にもその部品一つ分のHTML・SCSS・JSしか入っていないので、自分のプロジェクトのヘッダーやサイドバーの場所へそのまま移せます。

React版でも実際に状態は変わりますか?

はい。コマンドパレットの開閉、カレンダーの選択日、タグ一覧のように状態を持つものはすべて useState で管理し、durationeasingcolor という3つの変数は props として受け取り、CSSのカスタムプロパティとして渡しています。

ファイルアップロードや進捗バーは実際に動きますか、それとも見せかけですか?

実際に動きます。ファイルアップロード(09)は選んだファイル名を実際にリストへ追加し、進捗バーを0から満タンまで実際に埋めます。目標進捗(05)のボタンも押すたびに実際に次のステップの値までバーを埋めます。自動再生GIFで見える繰り返しの動きは、誰も操作していないときだけ回る装飾用ループで、クリックした瞬間に止まります。

解凍パスワードを入力してください

パスワードはこの記事の本文の中にあります。読み進めると出てきます。