3 Topics
Chapter•100 Minutes

Capstone: Build a Responsive TaskFlow Dashboard

Combine the course concepts into a complete dashboard with navigation, responsive cards, filters, task rows, states, and theme-aware styling.

Plan the dashboard structure

Build the dashboard in layers:

  1. Application shell: page background, navigation, content container.
  2. Page header: title, summary, primary action.
  3. Metric cards: consistent grid, number hierarchy, descriptive labels.
  4. Task controls: search, filter, sort, and status indicators.
  5. Task list: readable rows, due dates, owners, and completion state.
  6. Responsive behavior: stack on narrow screens; introduce sidebars or columns only when content has room.

Use semantic landmarks such as <header>, <nav>, <main>, and sections with meaningful headings. Use realistic data and test long task titles, empty results, and error states.

Dashboard component map
Key Takeaways
  • Build the shell before individual cards.
  • Use semantic landmarks and a logical heading hierarchy.
  • Test realistic content and empty/error states.

Implement the dashboard interface

The following is a compact capstone skeleton. Extend it with application state, real data, loading and error states, and your routing framework. It focuses on layout and visual hierarchy rather than pretending to implement a complete task-management backend.

Capstone scope

This skeleton demonstrates presentation. A production app still needs functional search and filters, task creation, loading/error/empty states, routing, data validation, and a working theme toggle.

TaskFlow dashboard skeleton
const metrics = [
  { label: "Open tasks", value: "18", note: "Across 4 projects" },
  { label: "Completed this week", value: "42", note: "+8 from last week" },
  { label: "Overdue", value: "3", note: "Needs attention" },
];

const tasks = [
  { id: 1, title: "Review responsive navigation", project: "Website refresh", status: "In progress" },
  { id: 2, title: "Document component variants", project: "Design system", status: "To do" },
  { id: 3, title: "Test keyboard focus states", project: "Accessibility", status: "Completed" },
];

export default function TaskFlowDashboard() {
  return (
    <div className="min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100">
      <header className="border-b border-slate-200 bg-white dark:border-slate-800 dark:bg-slate-900">
        <div className="mx-auto flex max-w-7xl items-center justify-between px-4 py-4 sm:px-6">
          <a href="/" className="text-lg font-bold tracking-tight">TaskFlow</a>
          <button className="rounded-lg bg-blue-600 px-4 py-2 text-sm font-semibold text-white hover:bg-blue-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600">
            Create task
          </button>
        </div>
      </header>

      <main className="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:py-10">
        <div className="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
          <div>
            <p className="text-sm font-semibold text-blue-700 dark:text-blue-300">Workspace overview</p>
            <h1 className="mt-2 text-3xl font-bold tracking-tight sm:text-4xl">Good morning, team.</h1>
            <p className="mt-2 text-slate-600 dark:text-slate-300">Here is what needs your attention today.</p>
          </div>
        </div>

        <section aria-label="Task metrics" className="mt-8 grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3">
          {metrics.map((metric) => (
            <article key={metric.label} className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
              <p className="text-sm text-slate-600 dark:text-slate-300">{metric.label}</p>
              <p className="mt-2 text-3xl font-bold">{metric.value}</p>
              <p className="mt-2 text-sm text-slate-500 dark:text-slate-400">{metric.note}</p>
            </article>
          ))}
        </section>

        <section className="mt-8 rounded-2xl border border-slate-200 bg-white dark:border-slate-800 dark:bg-slate-900">
          <div className="flex flex-col gap-3 border-b border-slate-200 p-5 sm:flex-row sm:items-center sm:justify-between dark:border-slate-800">
            <div>
              <h2 className="text-lg font-semibold">My tasks</h2>
              <p className="mt-1 text-sm text-slate-600 dark:text-slate-300">Priorities across your projects</p>
            </div>
            <label className="block">
              <span className="sr-only">Search tasks</span>
              <input type="search" placeholder="Search tasks..." className="w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm dark:border-slate-700 dark:bg-slate-950 sm:w-64" />
            </label>
          </div>
          <ul className="divide-y divide-slate-200 dark:divide-slate-800">
            {tasks.map((task) => (
              <li key={task.id} className="flex flex-col gap-2 p-5 sm:flex-row sm:items-center sm:justify-between">
                <div className="min-w-0">
                  <h3 className="break-words font-medium">{task.title}</h3>
                  <p className="mt-1 text-sm text-slate-600 dark:text-slate-300">{task.project}</p>
                </div>
                <span className="w-fit rounded-full bg-slate-100 px-3 py-1 text-xs font-semibold text-slate-700 dark:bg-slate-800 dark:text-slate-200">{task.status}</span>
              </li>
            ))}
          </ul>
        </section>
      </main>
    </div>
  );
}
đź’ˇ The dashboard composes a centered shell, responsive metric grid, semantic task list, search input, and dark-mode variants. The data is illustrative; connect the controls to real state and implement search, create-task behavior, and theme activation in your application. Confirm that your project has a dark-mode strategy configured.

Review checklist and extension challenges

Use this checklist to assess the project:

  • Does the page work at 320 px without horizontal page overflow?
  • Are grid columns introduced only when the content fits?
  • Do buttons and links use the correct native elements?
  • Is keyboard focus clearly visible?
  • Do labels and search inputs have accessible names?
  • Are status meanings communicated with text, not color alone?
  • Does dark mode preserve contrast for borders, text, inputs, and focus?
  • Does reduced-motion preference disable nonessential animation?
  • Are class candidates statically discoverable?
  • Does the production build render the same intended styles?

Extension challenges

  1. Add a filter toolbar that wraps on small screens.
  2. Add an empty state with a clear next action.
  3. Create Button, Badge, MetricCard, and TaskRow components with finite variants.
  4. Add a theme toggle with persistence and no hydration flash.
  5. Add a responsive sidebar that becomes a drawer on mobile.
  6. Audit contrast and keyboard flow with browser tools.
Key Takeaways
  • A capstone should prove responsive, accessible, and maintainable behavior—not only visual polish.
  • Treat empty, loading, and error states as first-class UI.
  • Use the checklist as a repeatable review before shipping.