# TaskCard

> The card that shows one task — its number, title and a status chip that changes the status — on a task board and in a task list.

Source: https://raft-design.botiverse.build/components/task-card. Generated from the current release; where it differs from what you remember, this page is right.

A TaskCard shows one task: its number, its title and a status chip people can change. A board and a list use the same card. How tasks are shown, filtered and opened is in [Tasks](https://raft-design.botiverse.build/patterns/tasks.md).

```tsx
import { useState } from "react";
import type { RefObject } from "react";
import { EllipsisVertical } from "lucide-react";
import {
  Button,
  TaskCard,
  TaskCardBody,
  TaskCardDescription,
  TaskCardLegacy,
  TaskCardMeta,
  TaskCardNumber,
  TaskCardRow,
  TaskCardStatus,
  TaskCardTitle,
  TaskStatusIcon,
} from "@botiverse/raft-ui";

import { getTaskStatusLabel } from "../task-card.fixtures";
import type { TaskStatus } from "../task-card.fixtures";
import { TaskStatusSelect } from "./task-status-select.composition";

export function TaskCardStateMatrixComposition({
  portalRef,
}: {
  portalRef?: RefObject<HTMLElement | null>;
}) {
  const [defaultStatus, setDefaultStatus] = useState<TaskStatus>("todo");
  const [longStatus, setLongStatus] = useState<TaskStatus>("todo");
  const [legacyStatus, setLegacyStatus] = useState<TaskStatus>("in-progress");
  const [menuStatus, setMenuStatus] = useState<TaskStatus>("in-review");

  return (
    <>
      <TaskCard>
        <TaskCardRow>
          <TaskCardBody>
            <TaskCardMeta>
              <TaskStatusSelect
                value={defaultStatus}
                onValueChange={setDefaultStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <TaskCardStatus
                  status={defaultStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #142"
                >
                  <TaskStatusIcon status={defaultStatus} />
                </TaskCardStatus>
              </TaskStatusSelect>
              <TaskCardNumber>#142</TaskCardNumber>
              <TaskStatusSelect
                value={defaultStatus}
                onValueChange={setDefaultStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <Button
                  variant="ghost"
                  size="icon-md"
                  disabled={false}
                  aria-label="Edit status for task #142"
                  className="ml-auto"
                >
                  <EllipsisVertical />
                </Button>
              </TaskStatusSelect>
              <TaskStatusSelect
                value={defaultStatus}
                onValueChange={setDefaultStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <TaskCardStatus
                  status={defaultStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #142"
                >
                  <span data-slot="task-card-status-label">
                    <TaskStatusIcon status={defaultStatus} />
                    {getTaskStatusLabel(defaultStatus)}
                  </span>
                </TaskCardStatus>
              </TaskStatusSelect>
            </TaskCardMeta>
            <TaskCardTitle>Match the Tasks tab toolbar and board spacing</TaskCardTitle>
            <TaskCardDescription>
              Preserve the source hierarchy before connecting product state.
            </TaskCardDescription>
          </TaskCardBody>
        </TaskCardRow>
      </TaskCard>

      <TaskCard>
        <TaskCardRow>
          <TaskCardBody>
            <TaskCardMeta>
              <TaskStatusSelect
                value={longStatus}
                onValueChange={setLongStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <TaskCardStatus
                  status={longStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #139"
                >
                  <TaskStatusIcon status={longStatus} />
                </TaskCardStatus>
              </TaskStatusSelect>
              <TaskCardNumber>#139</TaskCardNumber>
              <TaskStatusSelect
                value={longStatus}
                onValueChange={setLongStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <Button
                  variant="ghost"
                  size="icon-md"
                  disabled={false}
                  aria-label="Edit status for task #139"
                  className="ml-auto"
                >
                  <EllipsisVertical />
                </Button>
              </TaskStatusSelect>
              <TaskStatusSelect
                value={longStatus}
                onValueChange={setLongStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <TaskCardStatus
                  status={longStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #139"
                >
                  <span data-slot="task-card-status-label">
                    <TaskStatusIcon status={longStatus} />
                    {getTaskStatusLabel(longStatus)}
                  </span>
                </TaskCardStatus>
              </TaskStatusSelect>
            </TaskCardMeta>
            <TaskCardTitle>
              Verify a deliberately long task title wraps to three lines without pushing the status
              control outside the card
            </TaskCardTitle>
            <TaskCardDescription>
              The description also wraps and clamps so dense board columns remain scannable at the
              source width.
            </TaskCardDescription>
          </TaskCardBody>
        </TaskCardRow>
      </TaskCard>

      <TaskCard>
        <TaskCardRow>
          <TaskCardBody>
            <TaskCardMeta>
              <TaskStatusSelect
                value={legacyStatus}
                onValueChange={setLegacyStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <TaskCardStatus
                  status={legacyStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #126"
                >
                  <TaskStatusIcon status={legacyStatus} />
                </TaskCardStatus>
              </TaskStatusSelect>
              <TaskCardNumber>#126</TaskCardNumber>
              <TaskCardLegacy>LEGACY</TaskCardLegacy>
              <TaskStatusSelect
                value={legacyStatus}
                onValueChange={setLegacyStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <Button
                  variant="ghost"
                  size="icon-md"
                  disabled={false}
                  aria-label="Edit status for task #126"
                  className="ml-auto"
                >
                  <EllipsisVertical />
                </Button>
              </TaskStatusSelect>
              <TaskStatusSelect
                value={legacyStatus}
                onValueChange={setLegacyStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <TaskCardStatus
                  status={legacyStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #126"
                >
                  <span data-slot="task-card-status-label">
                    <TaskStatusIcon status={legacyStatus} />
                    {getTaskStatusLabel(legacyStatus)}
                  </span>
                </TaskCardStatus>
              </TaskStatusSelect>
            </TaskCardMeta>
            <TaskCardTitle>Retire the legacy task inspector</TaskCardTitle>
          </TaskCardBody>
        </TaskCardRow>
      </TaskCard>

      <TaskCard disabled>
        <TaskCardRow>
          <TaskCardBody>
            <TaskCardMeta>
              <TaskStatusSelect value={defaultStatus} disabled title="Change task status">
                <TaskCardStatus
                  status={defaultStatus}
                  busy={false}
                  disabled
                  aria-label="Change status for task #142"
                >
                  <TaskStatusIcon status={defaultStatus} />
                </TaskCardStatus>
              </TaskStatusSelect>
              <TaskCardNumber>#142</TaskCardNumber>
              <TaskStatusSelect value={defaultStatus} disabled title="Change task status">
                <Button
                  variant="ghost"
                  size="icon-md"
                  disabled
                  aria-label="Edit status for task #142"
                  className="ml-auto"
                >
                  <EllipsisVertical />
                </Button>
              </TaskStatusSelect>
              <TaskStatusSelect value={defaultStatus} disabled title="Change task status">
                <TaskCardStatus
                  status={defaultStatus}
                  busy={false}
                  disabled
                  aria-label="Change status for task #142"
                >
                  <span data-slot="task-card-status-label">
                    <TaskStatusIcon status={defaultStatus} />
                    {getTaskStatusLabel(defaultStatus)}
                  </span>
                </TaskCardStatus>
              </TaskStatusSelect>
            </TaskCardMeta>
            <TaskCardTitle>Match the Tasks tab toolbar and board spacing</TaskCardTitle>
            <TaskCardDescription>
              Preserve the source hierarchy before connecting product state.
            </TaskCardDescription>
          </TaskCardBody>
        </TaskCardRow>
      </TaskCard>

      <TaskCard>
        <TaskCardRow>
          <TaskCardBody>
            <TaskCardMeta>
              <TaskStatusSelect value="in-progress" disabled title="Change task status">
                <TaskCardStatus
                  status="in-progress"
                  busy
                  disabled
                  aria-label="Change status for task #137"
                >
                  <TaskStatusIcon status="in-progress" />
                </TaskCardStatus>
              </TaskStatusSelect>
              <TaskCardNumber>#137</TaskCardNumber>
              <TaskStatusSelect value="in-progress" disabled title="Change task status">
                <Button
                  variant="ghost"
                  size="icon-md"
                  disabled
                  aria-label="Edit status for task #137"
                  className="ml-auto"
                >
                  <EllipsisVertical />
                </Button>
              </TaskStatusSelect>
              <TaskStatusSelect value="in-progress" disabled title="Change task status">
                <TaskCardStatus
                  status="in-progress"
                  busy
                  disabled
                  aria-label="Change status for task #137"
                >
                  <span data-slot="task-card-status-label">
                    <TaskStatusIcon status="in-progress" />
                    In Progress
                  </span>
                </TaskCardStatus>
              </TaskStatusSelect>
            </TaskCardMeta>
            <TaskCardTitle>Connect keyboard drag feedback</TaskCardTitle>
            <TaskCardDescription>Announce allowed and blocked status targets.</TaskCardDescription>
          </TaskCardBody>
        </TaskCardRow>
      </TaskCard>

      <TaskCard>
        <TaskCardRow>
          <TaskCardBody>
            <TaskCardMeta>
              <TaskStatusSelect
                value={menuStatus}
                onValueChange={setMenuStatus}
                portalRef={portalRef}
                defaultOpen
                title="Change task status"
              >
                <TaskCardStatus
                  status={menuStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #121"
                >
                  <TaskStatusIcon status={menuStatus} />
                </TaskCardStatus>
              </TaskStatusSelect>
              <TaskCardNumber>#121</TaskCardNumber>
              <TaskStatusSelect
                value={menuStatus}
                onValueChange={setMenuStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <Button
                  variant="ghost"
                  size="icon-md"
                  disabled={false}
                  aria-label="Edit status for task #121"
                  className="ml-auto"
                >
                  <EllipsisVertical />
                </Button>
              </TaskStatusSelect>
              <TaskStatusSelect
                value={menuStatus}
                onValueChange={setMenuStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <TaskCardStatus
                  status={menuStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #121"
                >
                  <span data-slot="task-card-status-label">
                    <TaskStatusIcon status={menuStatus} />
                    {getTaskStatusLabel(menuStatus)}
                  </span>
                </TaskCardStatus>
              </TaskStatusSelect>
            </TaskCardMeta>
            <TaskCardTitle>Review task filter empty states</TaskCardTitle>
            <TaskCardDescription>
              Compare empty, filtered empty, loading, and failure structures.
            </TaskCardDescription>
          </TaskCardBody>
        </TaskCardRow>
      </TaskCard>
    </>
  );
}
```

## When to use

| Situation                                                     | Use                                                                                                                    |
| ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| A task on a board or in a list                                | TaskCard, inside a [TaskBoard](https://raft-design.botiverse.build/components/task-board.md) column or a [TaskList](https://raft-design.botiverse.build/components/task-list.md) group    |
| A task in a conversation                                      | Not a card: the message is the task, and a task marker hangs under it ([MessageItem](https://raft-design.botiverse.build/components/message-item.md)) |
| A task's status or number in running text, a row or a heading | `TaskChip` or `TaskStatusBadge` ([Tasks components](https://raft-design.botiverse.build/components/tasks.md))                                                |
| The task's details                                            | A centered dialog; see [Tasks](https://raft-design.botiverse.build/patterns/tasks.md#structure)                                      |

## Anatomy

- `TaskCard`: the card. Pass `onClick` to make it open the task's details.
- `TaskCardRow` and `TaskCardBody`: the layout around the text.
- `TaskCardMeta`: the first line.
  - `TaskCardChannel`: the place the task belongs to (#launch-week). Optional; see [Content](#content).
  - `TaskCardStatus`: the status chip. Required.
  - `TaskCardNumber`: the task number (#25). Required.
  - `TaskCardLegacy`: a marker for a task from the old task system. Optional.
- `TaskCardTitle`: the task's title, at most 3 lines. Required.
- `TaskCardDescription`: a short description, at most 2 lines. Optional.

## States

| State                                    | Use for                                                                                                            |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `busy` on `TaskCard` or `TaskCardStatus` | A status change is being saved; the chip can't be pressed until it finishes                                        |
| `disabled`                               | A task that can't be opened or changed: a `fill-muted` card with `foreground-disabled` text                        |
| Dragging                                 | On a board, the card being dragged is the only card with `shadow-float` ([TaskBoard](https://raft-design.botiverse.build/components/task-board.md)) |

```tsx
import { useState } from "react";
import type { RefObject } from "react";
import { EllipsisVertical } from "lucide-react";
import {
  Button,
  TaskCard,
  TaskCardBody,
  TaskCardDescription,
  TaskCardLegacy,
  TaskCardMeta,
  TaskCardNumber,
  TaskCardRow,
  TaskCardStatus,
  TaskCardTitle,
  TaskStatusIcon,
} from "@botiverse/raft-ui";

import { getTaskStatusLabel } from "../task-card.fixtures";
import type { TaskStatus } from "../task-card.fixtures";
import { TaskStatusSelect } from "./task-status-select.composition";

export function TaskCardStateMatrixComposition({
  portalRef,
}: {
  portalRef?: RefObject<HTMLElement | null>;
}) {
  const [defaultStatus, setDefaultStatus] = useState<TaskStatus>("todo");
  const [longStatus, setLongStatus] = useState<TaskStatus>("todo");
  const [legacyStatus, setLegacyStatus] = useState<TaskStatus>("in-progress");
  const [menuStatus, setMenuStatus] = useState<TaskStatus>("in-review");

  return (
    <>
      <TaskCard>
        <TaskCardRow>
          <TaskCardBody>
            <TaskCardMeta>
              <TaskStatusSelect
                value={defaultStatus}
                onValueChange={setDefaultStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <TaskCardStatus
                  status={defaultStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #142"
                >
                  <TaskStatusIcon status={defaultStatus} />
                </TaskCardStatus>
              </TaskStatusSelect>
              <TaskCardNumber>#142</TaskCardNumber>
              <TaskStatusSelect
                value={defaultStatus}
                onValueChange={setDefaultStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <Button
                  variant="ghost"
                  size="icon-md"
                  disabled={false}
                  aria-label="Edit status for task #142"
                  className="ml-auto"
                >
                  <EllipsisVertical />
                </Button>
              </TaskStatusSelect>
              <TaskStatusSelect
                value={defaultStatus}
                onValueChange={setDefaultStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <TaskCardStatus
                  status={defaultStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #142"
                >
                  <span data-slot="task-card-status-label">
                    <TaskStatusIcon status={defaultStatus} />
                    {getTaskStatusLabel(defaultStatus)}
                  </span>
                </TaskCardStatus>
              </TaskStatusSelect>
            </TaskCardMeta>
            <TaskCardTitle>Match the Tasks tab toolbar and board spacing</TaskCardTitle>
            <TaskCardDescription>
              Preserve the source hierarchy before connecting product state.
            </TaskCardDescription>
          </TaskCardBody>
        </TaskCardRow>
      </TaskCard>

      <TaskCard>
        <TaskCardRow>
          <TaskCardBody>
            <TaskCardMeta>
              <TaskStatusSelect
                value={longStatus}
                onValueChange={setLongStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <TaskCardStatus
                  status={longStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #139"
                >
                  <TaskStatusIcon status={longStatus} />
                </TaskCardStatus>
              </TaskStatusSelect>
              <TaskCardNumber>#139</TaskCardNumber>
              <TaskStatusSelect
                value={longStatus}
                onValueChange={setLongStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <Button
                  variant="ghost"
                  size="icon-md"
                  disabled={false}
                  aria-label="Edit status for task #139"
                  className="ml-auto"
                >
                  <EllipsisVertical />
                </Button>
              </TaskStatusSelect>
              <TaskStatusSelect
                value={longStatus}
                onValueChange={setLongStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <TaskCardStatus
                  status={longStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #139"
                >
                  <span data-slot="task-card-status-label">
                    <TaskStatusIcon status={longStatus} />
                    {getTaskStatusLabel(longStatus)}
                  </span>
                </TaskCardStatus>
              </TaskStatusSelect>
            </TaskCardMeta>
            <TaskCardTitle>
              Verify a deliberately long task title wraps to three lines without pushing the status
              control outside the card
            </TaskCardTitle>
            <TaskCardDescription>
              The description also wraps and clamps so dense board columns remain scannable at the
              source width.
            </TaskCardDescription>
          </TaskCardBody>
        </TaskCardRow>
      </TaskCard>

      <TaskCard>
        <TaskCardRow>
          <TaskCardBody>
            <TaskCardMeta>
              <TaskStatusSelect
                value={legacyStatus}
                onValueChange={setLegacyStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <TaskCardStatus
                  status={legacyStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #126"
                >
                  <TaskStatusIcon status={legacyStatus} />
                </TaskCardStatus>
              </TaskStatusSelect>
              <TaskCardNumber>#126</TaskCardNumber>
              <TaskCardLegacy>LEGACY</TaskCardLegacy>
              <TaskStatusSelect
                value={legacyStatus}
                onValueChange={setLegacyStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <Button
                  variant="ghost"
                  size="icon-md"
                  disabled={false}
                  aria-label="Edit status for task #126"
                  className="ml-auto"
                >
                  <EllipsisVertical />
                </Button>
              </TaskStatusSelect>
              <TaskStatusSelect
                value={legacyStatus}
                onValueChange={setLegacyStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <TaskCardStatus
                  status={legacyStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #126"
                >
                  <span data-slot="task-card-status-label">
                    <TaskStatusIcon status={legacyStatus} />
                    {getTaskStatusLabel(legacyStatus)}
                  </span>
                </TaskCardStatus>
              </TaskStatusSelect>
            </TaskCardMeta>
            <TaskCardTitle>Retire the legacy task inspector</TaskCardTitle>
          </TaskCardBody>
        </TaskCardRow>
      </TaskCard>

      <TaskCard disabled>
        <TaskCardRow>
          <TaskCardBody>
            <TaskCardMeta>
              <TaskStatusSelect value={defaultStatus} disabled title="Change task status">
                <TaskCardStatus
                  status={defaultStatus}
                  busy={false}
                  disabled
                  aria-label="Change status for task #142"
                >
                  <TaskStatusIcon status={defaultStatus} />
                </TaskCardStatus>
              </TaskStatusSelect>
              <TaskCardNumber>#142</TaskCardNumber>
              <TaskStatusSelect value={defaultStatus} disabled title="Change task status">
                <Button
                  variant="ghost"
                  size="icon-md"
                  disabled
                  aria-label="Edit status for task #142"
                  className="ml-auto"
                >
                  <EllipsisVertical />
                </Button>
              </TaskStatusSelect>
              <TaskStatusSelect value={defaultStatus} disabled title="Change task status">
                <TaskCardStatus
                  status={defaultStatus}
                  busy={false}
                  disabled
                  aria-label="Change status for task #142"
                >
                  <span data-slot="task-card-status-label">
                    <TaskStatusIcon status={defaultStatus} />
                    {getTaskStatusLabel(defaultStatus)}
                  </span>
                </TaskCardStatus>
              </TaskStatusSelect>
            </TaskCardMeta>
            <TaskCardTitle>Match the Tasks tab toolbar and board spacing</TaskCardTitle>
            <TaskCardDescription>
              Preserve the source hierarchy before connecting product state.
            </TaskCardDescription>
          </TaskCardBody>
        </TaskCardRow>
      </TaskCard>

      <TaskCard>
        <TaskCardRow>
          <TaskCardBody>
            <TaskCardMeta>
              <TaskStatusSelect value="in-progress" disabled title="Change task status">
                <TaskCardStatus
                  status="in-progress"
                  busy
                  disabled
                  aria-label="Change status for task #137"
                >
                  <TaskStatusIcon status="in-progress" />
                </TaskCardStatus>
              </TaskStatusSelect>
              <TaskCardNumber>#137</TaskCardNumber>
              <TaskStatusSelect value="in-progress" disabled title="Change task status">
                <Button
                  variant="ghost"
                  size="icon-md"
                  disabled
                  aria-label="Edit status for task #137"
                  className="ml-auto"
                >
                  <EllipsisVertical />
                </Button>
              </TaskStatusSelect>
              <TaskStatusSelect value="in-progress" disabled title="Change task status">
                <TaskCardStatus
                  status="in-progress"
                  busy
                  disabled
                  aria-label="Change status for task #137"
                >
                  <span data-slot="task-card-status-label">
                    <TaskStatusIcon status="in-progress" />
                    In Progress
                  </span>
                </TaskCardStatus>
              </TaskStatusSelect>
            </TaskCardMeta>
            <TaskCardTitle>Connect keyboard drag feedback</TaskCardTitle>
            <TaskCardDescription>Announce allowed and blocked status targets.</TaskCardDescription>
          </TaskCardBody>
        </TaskCardRow>
      </TaskCard>

      <TaskCard>
        <TaskCardRow>
          <TaskCardBody>
            <TaskCardMeta>
              <TaskStatusSelect
                value={menuStatus}
                onValueChange={setMenuStatus}
                portalRef={portalRef}
                defaultOpen
                title="Change task status"
              >
                <TaskCardStatus
                  status={menuStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #121"
                >
                  <TaskStatusIcon status={menuStatus} />
                </TaskCardStatus>
              </TaskStatusSelect>
              <TaskCardNumber>#121</TaskCardNumber>
              <TaskStatusSelect
                value={menuStatus}
                onValueChange={setMenuStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <Button
                  variant="ghost"
                  size="icon-md"
                  disabled={false}
                  aria-label="Edit status for task #121"
                  className="ml-auto"
                >
                  <EllipsisVertical />
                </Button>
              </TaskStatusSelect>
              <TaskStatusSelect
                value={menuStatus}
                onValueChange={setMenuStatus}
                portalRef={portalRef}
                title="Change task status"
              >
                <TaskCardStatus
                  status={menuStatus}
                  busy={false}
                  disabled={false}
                  aria-label="Change status for task #121"
                >
                  <span data-slot="task-card-status-label">
                    <TaskStatusIcon status={menuStatus} />
                    {getTaskStatusLabel(menuStatus)}
                  </span>
                </TaskCardStatus>
              </TaskStatusSelect>
            </TaskCardMeta>
            <TaskCardTitle>Review task filter empty states</TaskCardTitle>
            <TaskCardDescription>
              Compare empty, filtered empty, loading, and failure structures.
            </TaskCardDescription>
          </TaskCardBody>
        </TaskCardRow>
      </TaskCard>
    </>
  );
}
```

## Behavior

- A card with an `onClick` hovers with the subtle tier, like the other cards people read, and presses with the medium tier; its status chip hovers medium ([Hover and press](https://raft-design.botiverse.build/foundations/states.md#hover-and-press)).
- Pressing the status chip opens a list of the five statuses, with a check on the current one ([Select](https://raft-design.botiverse.build/components/select.md)). Choosing one changes the status at once.
- Pressing the chip doesn't open the card: stop the click from reaching `TaskCard`.

```tsx
import { useCallback, useMemo } from "react";
import type { MouseEvent, ReactElement, RefObject } from "react";
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectGroupLabel,
  SelectItem,
  SelectItemIndicator,
  SelectItemText,
  SelectList,
  SelectTrigger,
  TaskStatusIcon,
} from "@botiverse/raft-ui";

import { TASK_STATUSES } from "../task-card.fixtures";
import type { TaskStatus } from "../task-card.fixtures";

const TASK_STATUS_SELECT_ITEMS = TASK_STATUSES.map(({ id, label }) => ({ value: id, label }));

function stopPropagation(event: MouseEvent) {
  event.stopPropagation();
}

export function TaskStatusSelect({
  value,
  children,
  disabled = false,
  defaultOpen,
  onValueChange,
  portalRef,
  title = "Task status",
}: {
  value: TaskStatus;
  children: ReactElement;
  disabled?: boolean;
  defaultOpen?: boolean;
  onValueChange?: (status: TaskStatus) => void;
  portalRef?: RefObject<HTMLElement | null>;
  title?: string;
}) {
  const handleValueChange = useCallback(
    (nextValue: TaskStatus | null) => {
      if (nextValue) onValueChange?.(nextValue);
    },
    [onValueChange],
  );
  const portalProps = useMemo(
    () => (portalRef ? { container: portalRef } : undefined),
    [portalRef],
  );

  return (
    <Select<TaskStatus>
      value={value}
      items={TASK_STATUS_SELECT_ITEMS}
      disabled={disabled}
      defaultOpen={defaultOpen}
      modal={false}
      onValueChange={handleValueChange}
    >
      <SelectTrigger render={children} onClick={stopPropagation} />
      <SelectContent align="start" className="w-[220px] min-w-[220px]" portalProps={portalProps}>
        <SelectList>
          <SelectGroup>
            <SelectGroupLabel>{title}</SelectGroupLabel>
            {TASK_STATUSES.map((status) => (
              <SelectItem key={status.id} value={status.id}>
                <TaskStatusIcon status={status.id} />
                <SelectItemText>{status.label}</SelectItemText>
                <SelectItemIndicator />
              </SelectItem>
            ))}
          </SelectGroup>
        </SelectList>
      </SelectContent>
    </Select>
  );
}
```

## Content

- Write the title in sentence case, as a verb and its object: "Polish the onboarding flow".
- Show the channel only where tasks from several places are listed together, such as a product's task page. In a place's own Tasks view every task belongs to that place, so leave it out.
- Set the channel and the number in Space Mono; the parts do this.
- Spell the statuses Todo, In Progress, In Review, Done and Closed ([Writing](https://raft-design.botiverse.build/content/writing.md#voice)).

## Accessibility

- `TaskCard` uses Base UI's `useRender` and renders an `article`. With `onClick` it is clickable but not a button: give the keyboard a way to open the task, such as a link or button on the title.
- `TaskCardStatus` is a native `button` and the trigger of the status [Select](https://raft-design.botiverse.build/components/select.md). Give it an accessible name that includes the task: `aria-label="Change status for task #25"`.
- `busy` sets `aria-busy`; `disabled` sets `aria-disabled` on the card.

## Rules

- Make the status chip the only thing that can be changed on the card. Assignee and creator are changed in the task's details.
- Don't add a shadow, a colored band or an accent border to the card: it is a white card with a hairline ([Shape](https://raft-design.botiverse.build/foundations/shape.md#nothing-on-the-edge-of-a-rounded-container)).

## API

### TaskCard

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `busy` | `boolean` | `false` |  |
| `disabled` | `boolean` | `false` |  |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### TaskCardBody

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### TaskCardChannel

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### TaskCardDescription

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### TaskCardLegacy

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### TaskCardMeta

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### TaskCardNumber

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### TaskCardRow

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### TaskCardStatus

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `busy` | `boolean` | `false` |  |
| `status` (required) | `TaskStatusId` |  |  |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### TaskCardTitle

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.
