Status Text
betaStatus as colored text with no background, with an optional quickest fix and a peek behind it.
Preview
tone
icon
fix
detail
stream
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. Add the component.
npx shadcn@latest add @txbrendan/status-text
Use it for
- Drawing a status anywhere: rows, tables, receipts, files, and sync lines. Status is colored text, not a chip.
- Map your own statuses to a tone, like ok → success, warn → warning, bad → danger, info → brand, muted → neutral, overdue → danger. Overdue shares danger's red, so its word, Overdue, must always show.
- fix ends the status with its quickest fix as an icon in the same color, named in its tooltip: Couldn't save, then a retry arrow.
- Give fix a result, like { label: 'Reminded' }. The icon slips to a check and its tooltip says so. With onUndo, coming back shows Undo for 8 seconds, and ⌘Z undoes it too. Send the change in onCommit.
- Return a promise from onClick, and give the result failed words. The fix waits while it runs, and turns into a red Retry arrow if it fails.
- detail opens the story behind the status on hover, focus, or tap. Pass PeekCard props, or a node.
- Set stream when the words change in place, like Reply to Sent, so the new words slip in.
Avoid it for
- A chip or badge for a status. Use StatusText.
- An icon for a fix that does not say the action on sight. Leave fix out, and put the action in the row.
- Color alone. The words must say the status, since color is only a hint.
Props
- tone
Color role. neutral is muted.
"neutral" | "brand" | "danger" | "success" | "warning" · default "neutral"
- icon
An icon before the words, in their color.
ReactNode
- fix
The quickest fix: an icon after the words, named by label in its tooltip. The default icon is a turn arrow, for retry.
{ label: string; icon?: ReactNode; onClick?; result?: ActionResult }
- stream
When the words change, the new ones slip in. The first words just show.
boolean · default false
- detail
The story behind the status, in a peek on hover, focus, or tap. The words get a dotted underline.
PeekCardProps | ReactNode
- children
The words, like "Going" or "Couldn't save". Required, since tones share colors: Overdue and Couldn't save are both danger red.
ReactNode
- className
Extra classes on the status. Later classes win.
string
| Prop | Type | Default | What it does |
|---|---|---|---|
| tone | "neutral" | "brand" | "danger" | "success" | "warning" | "neutral" | Color role. neutral is muted. |
| icon | ReactNode | - | An icon before the words, in their color. |
| fix | { label: string; icon?: ReactNode; onClick?; result?: ActionResult } | - | The quickest fix: an icon after the words, named by label in its tooltip. The default icon is a turn arrow, for retry. |
| stream | boolean | false | When the words change, the new ones slip in. The first words just show. |
| detail | PeekCardProps | ReactNode | - | The story behind the status, in a peek on hover, focus, or tap. The words get a dotted underline. |
| children | ReactNode | - | The words, like "Going" or "Couldn't save". Required, since tones share colors: Overdue and Couldn't save are both danger red. |
| className | string | - | Extra classes on the status. Later classes win. |
Accessibility
- The words carry the status, so color is never the only signal.
- A fix is a 24px button named by its label, which shows in its tooltip. Once pressed, its name follows its state: the result, with "Press to undo." while a press undoes, then the record. A fix that failed is named "Retry:" and what failed.
- A fix says its result once, through the page's one polite live region, and its tooltip shows it for a moment after the press.
- With detail, the words are a button that opens the peek, named by its PeekCard title, or else by the words.
- stream only moves the words. To have a change read out, put role="status" on it.
Examples
Each tone, in words
<StatusText tone="success">Going</StatusText>
<StatusText tone="warning">Maybe</StatusText>
<StatusText>No reply</StatusText>With its quickest fix
<StatusText tone="danger" fix={{ label: "Retry", onClick: () => save() }}>
Couldn't save
</StatusText>With a peek behind it
<StatusText
tone="danger"
detail={{
title: "Overdue",
facts: [["Due", "Sat 17 Oct · INV-0042"]],
}}
>
Overdue
</StatusText>