Document the labels mechanism end to end
Patterns
Tasks & Projects
A Linear-style tracker, assembled from the package rather than re-drawn: a filterable task table, a detail view you can edit in place, a board, a sub-task checklist, cycle metrics and a roadmap.
Every demo below is wired to real state — sorting sorts, filters filter, selection selects, and renaming a task changes the row you renamed. Only two things are drawn by hand, because the package ships neither: the board's columns and the roadmap's quarter grid. Everything inside them is still made of StatusPill, AvatarGroup and RelativeTime.
Task list
The screen most task apps are mostly made of. SearchFilterBar and FilterChips above, DataTable in the middle, TablePagination below — plus a column menu, bulk actions behind a ConfirmDialog, and InlineEdit in the title cell so a task can be renamed without leaving the list. Chip counts come from the set filtered by everything except status, so each chip answers “how many will I get”.
| Assignees | Actions | |||||||
|---|---|---|---|---|---|---|---|---|
| DS-413 | In progress | Medium | Sep 3 | |||||
| DS-404 | In progress | High | Sep 1 | |||||
| DS-401 | In progress | Urgent | Aug 28 | |||||
| DS-402 | In review | High | Sep 2 | |||||
| DS-415 | In review | High | Aug 31 |
Task detail
Description on the left, properties on the right — and every property is a real control. InlineEdit renames the task (Enter saves, Esc reverts) and edits the description in multiline mode, where Enter adds a line and ⌘↵ saves. Status and priority run through Combobox, assignees through MultiCombobox, and the due date through DatePicker with an English locale and English labels. Each one sits in a FormField, which is what turns the caption into a real <label> and hands the control its id.
Kanban board
The one screen here with no package equivalent — columns and cards are the app's own layout. What the app should not re-invent is what goes inside a card: the priority chip is a StatusPill, the faces are an AvatarGroup, the timestamp is a RelativeTime, and an empty column is an EmptyState. Column dots take their colour from getStatusConfig rather than a local switch. Move a card with the arrows — counts update as it lands.
Todo
3Replace the hand-rolled tables in Patterns
Keyboard affordance for moving kanban cards
In progress
2Re-map muted-foreground so AA holds on cards
Pin the first DataTable column below md
In review
2Ship the async loading state for Combobox
Audit every focus ring against 1.4.11
Done
2Publish 0.9.1 with the StatusPill border fix
Give TablePagination English labels in the docs
Sub-task checklist
Real Checkbox controls with real <label> elements, and a real Progress — not a div with a computed width. The bar carries role="progressbar" and the track-to-bar contrast pair the package already measured, and it flips to the success tone at 100%.
Sub-tasks
2/5Sub-tasks roll up to the parent task's progress.
Cycle progress
Four StatCard tiles plus a completion bar. The numbers are derived from the same task array the list above starts from — hard-coded KPI literals go stale the first time the data moves. Note polarity on the overdue tile: rising overdue work is bad news no matter which way the arrow points, and the delta colour has to say so.
Scope
16
In flight
6
+2since Monday
Completed
3
+3since Monday
Overdue
2
+1since Monday
Cycle 12
At riskRoadmap timeline
Quarter-scale planning above the task level. The grid and the bar placement are the app's, but the bar itself is a Progress in a positioned wrapper, the health flag is a StatusPill with a shortLabel, and the crew is an AvatarGroup. The row scrolls inside its own container so a long horizon never scrolls the page.
Best Practices
Do
- Declare status and priority once as
StatusConfig[]and let every surface read from it — table, chips, board, detail. - Give every composite that takes
labelsan English set; the package defaults are Vietnamese, screen-reader strings included. - Reset to page 1 whenever a filter changes, or a stale page reads as “no data”.
- Count chips against everything except the filter they control, so each one predicts its own result.
- Write two empty states — nothing yet, and nothing matched — and branch on the unfiltered set.
- Give every task a short, stable key (DS-401) and keep it pinned when the table scrolls.
- Confirm bulk deletes and single deletes with the same dialog, so both read the same weight.
Don't
- Do not hand-roll a table.
DataTablealready has sorting, selection, pinning, column visibility, skeletons and an empty state. - Do not build class names from data (
bg-${tone}-300) — Tailwind scans statically and purges what it cannot see. - Do not colour text with a fill token.
text-destructivefails contrast;text-destructive-inkis the text step. - Do not nest a
ConfirmDialoginside aDropdownMenu; the menu unmounts before the dialog opens. - Do not encode status or priority with colour alone — every chip carries its label too.
- Do not caption a control with a styled
<p>.FormFieldemits a real<label htmlFor>; a paragraph is not a label, however much it looks like one. - Do not make moving a card drag-only, and do not lose scroll position when one lands.
- Do not put a control in a table cell without stopping its click from reaching the row.
Accessibility
- Board cards move by button as well as by drag, and each button names its destination in
aria-label. - Rows with
onRowClickjoin the tab order and answer Enter/Space — the package does that for you, but only if you pass the handler. - Checklist items use the package's
Checkbox— a Radix<button role="checkbox">carryingaria-checked, not an<input>— tied to a real<label htmlFor>, so clicking the text toggles it and a screen reader announces the state. - Progress bars carry an
aria-labelwith the percentage; thewarningtone adds hazard stripes because gold and yellow are indistinguishable on three of the four palettes. KeyboardHintisaria-hidden— the glyphs read as noise, so the button it decorates has to name its own shortcut.- Avatars in a group are focusable buttons carrying the member's name, so the group is not a row of silent colour swatches.
ComboboxandMultiComboboxdefault the clear button'ssr-onlylabel to Vietnamese (“Xóa lựa chọn” / “Xóa tất cả lựa chọn”); passclearLabel, added in 1.0.0, to name it in English —MultiComboboxneeds it most, sinceclearabledefaults to true there.- Announce bulk results and card drops through a live region, not only by re-rendering the list.
Built from
For the frame these screens sit in — sidebar, app header, workspace switcher, command palette — see the application pattern. For the same table shape against a different domain, see CRM and Commerce.