Inline message
One short line next to the control it is about: a field, or a table cell. The smallest of three levels, under the section message and the banner.
Playground
Change the controls to try each prop. The code under the preview updates as you go and lists only the props you changed; copy it into your page.
import { InlineMessage } from '@/ui/inline-message';<InlineMessage> Add the domain, like sara@halcyon.com</InlineMessage>Under a field
An error shows on blur or submit, never while typing, and also turns the field's edge red. A confirmation shows only after a server check, such as a free workspace URL, and leaves the edge alone. Point the field at the message with aria-describedby.
import { InlineMessage } from '@/ui/inline-message';
// The field here is a plain input; in a form it is the Form page's Field.
const input =
'h-7 w-full rounded-large bg-surface px-100 text-body text-text shadow-[inset_0_0_0_1px_var(--color-border-input)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid focus-visible:outline-border-focused aria-invalid:shadow-[inset_0_0_0_1px_var(--color-danger)]';
export default function InlineMessageField() {
return (
<div className="flex flex-wrap gap-400">
<div className="flex w-70 flex-col gap-075">
<label htmlFor="client-email" className="text-body-small leading-body-small font-medium">
Client email
</label>
<input id="client-email" className={input} defaultValue="sara@halcyon" aria-invalid="true" aria-describedby="client-email-message" />
<InlineMessage id="client-email-message">Add the domain, like sara@halcyon.com</InlineMessage>
</div>
<div className="flex w-70 flex-col gap-075">
<label htmlFor="workspace-url" className="text-body-small leading-body-small font-medium">
Workspace URL
</label>
<input id="workspace-url" className={input} defaultValue="northbridge" aria-describedby="workspace-url-message" />
<InlineMessage id="workspace-url-message" appearance="confirmation">
Available
</InlineMessage>
</div>
</div>
);
}Under a table cell
A cell has no room below it, so the message floats as a small card 4px under the cell, which keeps its 2px red edge. Near the bottom of the table, pass placement="above".
import { InlineMessage } from '@/ui/inline-message';
// A table cell has no room below it, so the message floats 4px under the cell.
export default function InlineMessageCell() {
return (
<div className="pb-14">
<div className="relative flex h-9 w-60 items-center px-150 text-body font-medium shadow-[inset_0_0_0_2px_var(--color-danger)]">
maya.chen@
<InlineMessage placement="below">Enter a full email address</InlineMessage>
</div>
</div>
);
}Icon button
InlineMessageButton puts a short explanation behind a 24px icon next to a column name or heading. Hover shows the title; a click or Enter opens the detail in a popup, and Escape closes it.
import { InlineMessageButton } from '@/ui/inline-message';
export default function InlineMessageButtonExample() {
return (
<div className="flex items-center gap-300">
<div className="flex items-center gap-050 text-body font-medium text-text-subtle">
Match
<InlineMessageButton
title="How Match is worked out"
secondaryText="Skills, seniority and location against the Job's must-haves. 100 means every must-have is met."
/>
</div>
<div className="flex items-center gap-050 text-body font-medium text-text-subtle">
Last contacted
<InlineMessageButton
appearance="warning"
title="Some dates are missing"
secondaryText="Calls logged before you connected your calendar have no date."
/>
</div>
</div>
);
}Error boundary
What shows when a page can't: a crash, a missing page, no access, offline, signed out or the database out of reach. The failure stays inside the smallest part that broke, keeps a way out, and is reported on its own.
Progress bar
A thin bar for work that takes longer than a moment and runs while you keep working: reading a mailbox, finding people, importing a file.