Components · Tasks
TaskCompletionControl
The round check that leads a task row or card and marks the task Done in one press, or reopens a done one.
import { TaskCompletionControl } from "@botiverse/raft-ui/wip";TaskCompletionControl is how a list of tasks is worked through as a to-do list: one press marks the task Done, and a press on a done task reopens it. It is a round check at the start of every task row and card, beside a conversation, under a message, on a board and in a list. When it shows and what a press does to the task are in Completing a task.
When to use
Anatomy
TaskCompletionControl: a nativebuttonwith checkbox semantics, holding a roundCheckboxIndicator(shape="circle"), 20px. Required on every task row and card. The row or card around it, the title it names and its tooltip are the caller's.
States
- Unchecked: an empty circle with an ink edge, on every status but Done: Backlog and Closed included.
- Hover: while unchecked, a faint check shows when the pointer is over the control itself. A hover elsewhere on the row is a hover on the row and previews nothing, even when the row or a column around it is a Tailwind
group. - Checked: an ink circle with the check knocked out, on a Done task only.
- Disabled: for a person who can't change the task: the disabled look,
aria-disabledand no preview. It doesn't set the nativedisabled, which would take the button out of the keyboard's reach: focus moving through a list of tasks couldn't stop on it, a screen reader would never hear that it is locked, and its tooltip couldn't open. It keeps its focus, and a press or Space does nothing.
Behavior
- Pass
checkedfrom the task's status and change the task inonCheckedChange: it is called withtrueto complete the task andfalseto reopen it. The control keeps no state of its own; what completing does (the Toast, the pause before the task moves to Done, Undo) is the caller's (Completing a task). - A press stops at the control: its click, Space and Enter never reach the row or card it sits on, so it never also opens the task, and on a TaskBoard card Space completes the task instead of picking the card up. Arrow keys still reach the list, for moving between rows.
- An
onClickthat callsevent.preventDefault()keepsonCheckedChangefrom being called. - Its hit area reaches 8px past the 20px circle with a mouse (36px) and 12px on a touch screen (44px) (Accessibility). With a mouse it reaches less far sideways than a Checkbox's 12px, which would cover the task number beside it.
Content
- Name it by the task: its number, then its title, "#25 Fix onboarding copy".
- Put the action in its tooltip if the product shows one: Complete, Reopen on a done one (Tooltip).
Accessibility
TaskCompletionControl renders a native button with role="checkbox" and wraps no Base UI primitive. aria-checked is true only on a done task. It isn't Base UI's Checkbox because it needs none of that checkbox's hidden input (a task's state isn't a form value), Enter must never toggle it, and a click must stay on this button rather than reach the row or card under it.
- Give it
aria-label(oraria-labelledby) with the task; the type requires one. Keep the same name when the state changes: the checked state already says whether the task is done, so a name that says what a press does ("Reopen #25") would be read as "Reopen #25, checkbox, checked". - Space toggles it. Enter doesn't, as on any checkbox, so it isn't taken for the row's Enter, which opens the task.
- It shows its own focus ring, apart from the row's.
CheckboxIndicatorinside it isaria-hidden; the button carries the role, the state and the name.
Rules
- Keep it round. A square box at the start of a row reads as "select this row", which is what a Checkbox there means; the circle reads as "done", as in a to-do list. Round is otherwise only for switches, radios and grab handles; this control is one of the two exceptions, with the Android pull-to-refresh disc (Shape).
- Use it only for completing a task. Don't use a round
CheckboxIndicatoror a round Checkbox for anything else (Checkbox). - Show it checked only on a Done task. A press on any other status, Backlog and Closed included, completes the task.
- Make it the first thing on a task row or card, and its own button on top of the row or card: pressing the rest still opens the task.
API
TaskCompletionControl
| Prop | Type | Default |
|---|---|---|
| The task, by number and title: "#25 Fix onboarding copy". It stays the same when the task is completed or reopened. | string | |
| The id of the element that names the task, such as its title. | string | |
| Whether the task is done. Only a Done task is checked; every other status, Backlog and Closed included, is unchecked. | boolean | |
| For a person who can't change the task. Sets `aria-disabled`, not the native `disabled`: the control stays focusable and a press does nothing. | boolean | false |
| Called with the state a press asks for: `true` to complete the task, `false` to reopen it. The caller changes the task. | (checked: boolean, event: MouseEvent<HTMLButtonElement>) => void |
Also takes the attributes of its element.