Task Card
Kanban task card: round done checkbox with bouncy tick, pastel label, title (struck when done), subtask progress, due chip that turns red when overdue, comment/attachment counts and avatar dots.
soft-flat/ui/task-cardSource
"use client";
import { useState } from "react";
import { CalendarDays, Check, MessageCircle, Paperclip } from "lucide-react";
import { cn } from "@/lib/utils";
export interface TaskCardProps {
title?: string;
label?: { text: string; tone: "peach" | "mint" | "lavender" | "butter" };
due?: string;
overdue?: boolean;
assignees?: string[];
comments?: number;
attachments?: number;
subtasks?: { done: number; total: number };
defaultDone?: boolean;
onDoneChange?: (done: boolean) => void;
className?: string;
}
const TONES = {
peach: "bg-da-accent text-da-accent-fg",
mint: "bg-da-secondary text-da-secondary-fg",
lavender: "bg-da-primary/12 text-da-fg",
butter: "bg-da-warning/30 text-da-fg",
} as const;
const AV = ["bg-da-accent", "bg-da-secondary", "bg-da-primary/35", "bg-da-warning/60"];
/** Kanban task card: round done checkbox (bouncy tick), pastel label, title, subtask progress, due date chip (red when overdue), meta counts and avatar dots. */
export function TaskCard({
title = "Write launch newsletter",
label = { text: "Marketing", tone: "peach" },
due = "Thu, Oct 9",
overdue = false,
assignees = ["Ana Souza", "Tom Becker"],
comments = 4,
attachments = 2,
subtasks = { done: 2, total: 5 },
defaultDone = false,
onDoneChange,
className,
}: TaskCardProps) {
const [done, setDone] = useState(defaultDone);
return (
<article className={cn("rounded-da-lg bg-da-surface p-4 shadow-da-sm transition-shadow duration-(--da-duration) hover:shadow-da-md", className)}>
<div className="flex items-start gap-3">
<button
type="button"
role="checkbox"
aria-checked={done}
aria-label={`Mark “${title}” as ${done ? "not done" : "done"}`}
onClick={() => {
setDone(!done);
onDoneChange?.(!done);
}}
className={cn(
"da-focus mt-0.5 grid size-6 shrink-0 place-items-center rounded-full border-2 transition-all duration-(--da-duration) ease-da-emphasized",
done ? "scale-110 border-da-secondary-fg bg-da-secondary-fg text-da-secondary" : "border-da-border-strong hover:border-da-primary",
)}
>
{done && <Check aria-hidden className="size-3.5" strokeWidth={3} />}
</button>
<div className="min-w-0 flex-1">
<span className={cn("inline-block rounded-da-pill px-2.5 py-0.5 text-xs font-semibold", TONES[label.tone])}>{label.text}</span>
<h3 className={cn("mt-2 font-da-display text-lg leading-snug font-bold", done && "text-da-muted-fg line-through")}>{title}</h3>
{subtasks.total > 0 && (
<div className="mt-3">
<div
className="h-1.5 overflow-hidden rounded-full bg-da-muted"
role="progressbar"
aria-valuenow={subtasks.done}
aria-valuemax={subtasks.total}
aria-label="Subtasks"
>
<div className="h-full rounded-full bg-da-primary" style={{ width: `${(subtasks.done / subtasks.total) * 100}%` }} />
</div>
<p className="mt-1 text-xs text-da-muted-fg">
{subtasks.done}/{subtasks.total} subtasks
</p>
</div>
)}
</div>
</div>
<footer className="mt-4 flex items-center gap-3 text-xs text-da-muted-fg">
<span
className={cn(
"inline-flex items-center gap-1 rounded-da-pill px-2 py-1 font-medium",
overdue && !done ? "bg-da-danger/12 text-da-danger" : "bg-da-surface-2",
)}
>
<CalendarDays aria-hidden className="size-3.5" />
{overdue && !done && <span className="sr-only">Overdue: </span>}
{due}
</span>
<span className="inline-flex items-center gap-1">
<MessageCircle aria-hidden className="size-3.5" />
{comments}
<span className="sr-only"> comments</span>
</span>
<span className="inline-flex items-center gap-1">
<Paperclip aria-hidden className="size-3.5" />
{attachments}
<span className="sr-only"> attachments</span>
</span>
<span className="ml-auto flex -space-x-1.5" role="img" aria-label={`Assigned to ${assignees.join(", ")}`}>
{assignees.map((a, i) => (
<span
key={a}
className={cn("grid size-7 place-items-center rounded-full text-[10px] font-bold text-da-fg ring-2 ring-da-surface", AV[i % AV.length])}
>
{a
.split(" ")
.map((w) => w[0])
.join("")}
</span>
))}
</span>
</footer>
</article>
);
}
export default TaskCard;
modules/soft-flat/ui/task-card/index.tsx
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title / due | string | — | Basics. |
| label | { text, tone } | — | Pastel label. |
| overdue / defaultDone | boolean | — | States. |
| assignees | string[] | — | Names. |
| comments / attachments | number | — | Counts. |
| subtasks | { done, total } | — | Progress. |
| onDoneChange | (done: boolean) => void | — | Handler. |
Other card variants in Soft Flat
Profile Card
Rounded teammate card: pastel header band, big emoji avatar in a white circle, name, role, status pill, three stats in soft wells, skill pills and a “Say hi” button.
Project Card
Pastel project card: white emoji tile, name link covering the card, description, chunky rounded progress bar with percentage, tasks-left and due chips, member dots.
Stat Card
Friendly KPI tile: emoji in a white circle, change pill, label, big rounded number and a row of rounded mini bars (last one highlighted); five tones.
Card in other art directions
Card
Composable card (Card, CardHeader, CardKicker, CardTitle, CardDescription, CardContent, CardFooter) with 5 solid tones, 3 hard-shadow depths and an interactive press-on-hover mode.
Profile Card
User/team member card: striped blue banner with status chip, overlapping yellow initials square, uppercase name and mono role, bio, ruled stat strip and a split action bar.
Release Card
Changelog entry card: yellow version/date column (top bar on mobile), uppercase title (optional link), summary and a list of changes tagged new / improved / fixed / breaking with fixed-width colored labels.
Stat Card
KPI card: mono label, icon square, giant value, green/red delta chip with arrow, and an outlined mini bar chart whose last bar is filled. Four solid tones.
Card
Composable frosted card (Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter) with frosted / tinted-gradient / solid variants, specular top edge and interactive lift.
Meeting Card
Meeting recap glass card: title, time / duration / platform meta, gradient participant stack with +N, AI summary panel, action items with checkboxes and owners, and a transcript link.
Profile Card
Teammate glass card: gradient cover, large initials avatar with presence dot, role and timezone, status pill, three stats, expertise tags and Message / Schedule actions.
Stat Ring Card
KPI glass card with a primary→accent gradient progress ring (SVG) showing the percentage, the value over its max and a caption; the ring draws in on mount.
Card
Composable hairline card (Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter) with default / raised / ghost variants and an interactive hover lift.
Incident Card
Incident summary card: severity chip, id and duration, linked title, service chips, a 4-step status progress bar, commander avatar, stacked responder avatars and update count.
Integration Card
Integration tile: logo/monogram, name, category, description, a Radix Switch to enable it, live status meta and a Configure link. Controlled or uncontrolled.
Metric Card
KPI card: muted label, big mono value, inline SVG sparkline with area fill and end dot, and a colored delta chip vs. the previous period (lowerIsBetter flips the colors).
Profile Card
Studio/person profile card in the Read.cv spirit: square gray portrait with initials, name, role and city in mono, availability dot in signal color, short bio and a list of links with ↗.
Project Card
Portfolio project card: neutral image frame that darkens slightly on hover with a “View case ↗” overlay, then a mono meta row (index, client, year) and the title with discipline tags.
Stat Card
Analytics stat: mono label, large display value, change in mono, and a row of thin vertical bars (last bar in ink, others gray) — no chart chrome.
Template Card
Template tile: a wireframe preview drawn with hairlines (nav, giant title, image grid) in a bordered frame, then name, author, category and usage count, with a “Use template” button revealed on hover/focus.
Account Card
Bank account card: institution + masked number header, big balance with available amount, sync timestamp, and three recent inflow/outflow rows.
Invoice Card
Invoice summary card: number + status, bill-to and dates grid, line items table with mono amounts, subtotal/tax/total, and download/send actions.
Kpi Card
Metric card: label, big mono value, colored delta vs. period and an area sparkline in the primary color along the bottom.
Plan Card
Billing/subscription card: current plan + price + renewal date, usage meters (progressbar semantics, turning amber at 80% and red at 95%), manage and upgrade actions.
Action Card
Reduction action card: serif title, description, saving and cost figures, leaf-rated impact/effort, and an “Add to plan” pill that toggles to a sage “In your plan” state.
Article Card
Journal article card: an organic shaped “cover” (arch, blob or circle) with a hand-drawn leaf, category and read time, serif title that underlines on hover, excerpt; whole card is one link.
Metric Card
Emissions metric card: label, big serif value with mono unit, a change chip (down = good, sage; up = clay) and a human equivalence line with a small leaf.
Supplier Card
Supplier card: arch-shaped initials avatar, name, category and country, status pill, share of your footprint and a rounded data-completeness bar with a reminder action.