affectUI › Components › Cards

Project Card

A two-line project summary that quietly unfolds on hover – a progress ring with one segment per subtask, a people filter and a status picker, all linked by hover.

Part of affectUI Pro. Install: npx shadcn@latest add https://www.affectui.com/r/project-card.json · Import from @/components/ui/project-card

When to use

  • Boards, lists and sidebars where many projects or tasks sit next to each other and the details should be one hover away
  • Anything with a short summary and a longer truth behind it – a project, a ticket, a deal, an agent run
  • When opening a separate page or drawer would be too heavy for a quick look and a small edit

When not to use

  • Long content or forms – open a drawer or a page instead; this card is for a glance and a tick
  • Dense tables – a row that grows on hover pushes everything below it around
  • When the details must stay visible – show the open card as a static layout instead

Usage

import { ProjectCard } from "@/components/ui/project-card";

export function OnboardingCard() {
  return (
    <ProjectCard
      title="Onboarding flow"
      tasks={[
        { id: "welcome", label: "Welcome screen", done: true, assignee: "Mara" },
        { id: "workspace", label: "Workspace setup", done: true, assignee: "Jonas" },
        { id: "invite", label: "Invite teammates", assignee: "Mara" },
      ]}
      status="progress"
      due="Oct 24"
      members={[{ name: "Mara" }, { name: "Jonas" }]}
      action={{ label: "Open", onClick: () => router.push("/projects/onboarding") }}
    />
  );
}

Props

NameTypeDefaultDescription
titlestringName of the project
tasksProjectTask[]{ id, label, done?, assignee? } – one ring segment and one row each; assignee is a member's name
onTasksChange(tasks: ProjectTask[]) => voidCalled with the full list after a tick. With it the card shows exactly what you pass; without it the ticks live inside the card
statusstringfirst optionId of the chosen status
statusesProjectOption[]To do, In progress, In review, Done{ id, label, tone? } – tone is red, amber, green, blue, violet or gray and also picks the icon; icon replaces it
onStatusChange(id: string) => voidCalled with the picked status
duestringReady-to-show due date, e.g. "Oct 24" – shown as "Due Oct 24"
membersProjectMember[]{ name, avatar? } – the avatars in the head; hovering one shows only that person's subtasks. Initials on a soft color without an image
action{ label: string; onClick: () => void }A button on the tray of the open card, e.g. Open
hintReactNode"2 left" / "All done"The line on the tray while nothing is hovered
trigger"hover" | "click""hover"Hover opens under the mouse; taps and the keyboard always toggle
openbooleanControlled open state
defaultOpenbooleanfalseStarts open when uncontrolled
onOpenChange(open: boolean) => voidCalled when the card opens or closes
classNamestringExtra class on the root

Keyboard

  • Enter / Space: On the card: opens or closes it
  • Tab: Through the status, the people, the subtasks and the action
  • Space: On a subtask: ticks or unticks it. On a person: keeps or releases their filter. On the status: unfolds the options
  • Esc: Closes the status options first, then the card

Accessibility

  • The head is one button with aria-expanded whose name carries the summary: title, progress, status and due date
  • Subtasks are checkboxes, the people are toggle buttons, the status options are a radio group
  • While the card is closed, everything inside is inert – nothing hidden can be focused or read
  • Focusing a subtask or a person does what hovering does, and the line on the tray is announced politely

Responsive

  • Fills its container up to 320px. Below 290px the date drops its word ("Oct 24") and the avatars move closer; a long title is cut with an ellipsis and nothing runs under the avatars
  • Touch screens: a tap opens, a tap outside closes, rows and options are taller and the assignee avatars are always visible
  • Dark mode built in: switches when an ancestor (usually <html>) has the class dark or data-theme="dark"

Requirements

  • React 19

Related

  • Progress Tracker – Onboarding checklist with an animated progress bar, ticks that draw themselves and auto play
  • Agent Plan – An agent's step-by-step plan to review before it runs – rename, drag to reorder, remove or add steps, then watch each one run with live timers and retry from a failed step
  • Compact Breakdown – A total split into its parts in one small card – numbers roll, the bar glides and the ranked rows trade places when the period changes