Notice
betaOne quiet line under the thing a problem or note is about, with a tone-colored icon and no box.
Preview
tone
size
in place
October invoices
icon
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/notice
Use it for
- Saying a problem in one line, right under the field, row, or card it is about.
- Put the few words that say what happened in NoticeStatus, like 'Declined'. The reason after them stays in the text color.
- Use tone to say what kind of note it is: danger for a problem, warning for a risk, success for done, neutral or brand for a plain note.
- Use size='sm' under a field, and the default 'md' under a card or a row.
- Add role='alert' only for an urgent problem that appears after the page loads, like a failed save.
Avoid it for
- A message about a whole page or section, like planned downtime. Use Alert.
- A choice people must make before they go on. Use a dialog.
- A tinted box or a banner around the line. The point of a notice is that it stays quiet.
- role='alert' on a notice that is there when the page loads. Screen readers would interrupt people for news that is not new.
- More than one notice under the same thing. Say the most important problem, and its fix.
Props
- tone
What kind of note it is. Colors the icon and NoticeStatus.
"neutral" | "brand" | "danger" | "success" | "warning" · default "neutral"
- size
Text and icon size. sm reads at the meta size for a note under a field.
"sm" | "md" · default "md"
- icon
Replaces the tone's icon. Pass null for no icon. Hidden from screen readers either way.
ReactNode | null
- role
Unset by default. Set it only for notices that appear or change after load. TypeScript rejects other roles.
"alert" | "status"
- className
Extra classes for the line. Later classes win.
string
- NoticeStatus
Part. The few words that say what happened. They take the tone's text color.
ComponentProps<"span">
| Prop | Type | Default | What it does |
|---|---|---|---|
| tone | "neutral" | "brand" | "danger" | "success" | "warning" | "neutral" | What kind of note it is. Colors the icon and NoticeStatus. |
| size | "sm" | "md" | "md" | Text and icon size. sm reads at the meta size for a note under a field. |
| icon | ReactNode | null | - | Replaces the tone's icon. Pass null for no icon. Hidden from screen readers either way. |
| role | "alert" | "status" | - | Unset by default. Set it only for notices that appear or change after load. TypeScript rejects other roles. |
| className | string | - | Extra classes for the line. Later classes win. |
| NoticeStatus | ComponentProps<"span"> | - | Part. The few words that say what happened. They take the tone's text color. |
Accessibility
- Renders a <div> with no role, so a notice that is there at load is read in order, like the rest of the page.
- role="alert" reads the notice at once. role="status" waits until the screen reader is quiet. Use either only for notices added or changed after load.
- The icon is aria-hidden, and color is not read out. The words must say what kind of note it is, like "Declined" or "Not saved".
- The icon and NoticeStatus use the tone's soft foreground. The theme tests hold it to 4.5:1 on the page, on cards, and in popovers.
- Under a field, point the field's aria-describedby at the notice's id, so the reason is read with the field.
Examples
A problem under a row
<div className="grid gap-1">
<p className="text-sm font-medium">October invoices</p>
<Notice tone="danger">
<NoticeStatus>Declined.</NoticeStatus> The card on file has expired.
</Notice>
</div>Under a field, read with it
<div className="grid gap-1.5">
<label htmlFor="team-email">Team email</label>
<input id="team-email" aria-describedby="team-email-note" aria-invalid defaultValue="sam@" />
<Notice id="team-email-note" tone="danger" size="sm">
Add the part after the @, like sam@example.com.
</Notice>
</div>A consequence, with a link to the fix
<Notice tone="warning">
<NoticeStatus>Due at 14:40.</NoticeStatus> Later bookings in Room 3 move with it.{" "}
<a href="/schedule">Open the schedule</a>
</Notice>A save that failed after load
function SaveNote({ error }: { error: string | null }) {
if (!error) return null;
return (
<Notice role="alert" tone="danger">
<NoticeStatus>Not saved.</NoticeStatus> {error}
</Notice>
);
}Your own icon, or none
import { Clock } from "lucide-react";
<div className="grid gap-2">
<Notice icon={<Clock />}>Replies close on Friday at 18:00.</Notice>
<Notice icon={null} size="sm">
Only admins can see this.
</Notice>
</div>