Getting Started
Writing
Clear, consistent copy is as much a part of the design as color and spacing. These conventions govern all text that appears in the interface: labels, buttons, error messages, status indicators, and empty states.
Voice
The interface speaks directly and informatively. It does not apologize unnecessarily, use filler phrases, or adopt a chatty tone. Every word should earn its place.
| Avoid | Prefer |
|---|---|
| Oops! Something went wrong. | Could not save changes. Check your connection and try again. |
| Hey there! Ready to get started? | No tasks yet. Create your first task to get started. |
| Please be advised that this action cannot be undone. | This cannot be undone. |
| You have successfully saved your changes! | Changes saved. |
| Click here to learn more. | Learn more about task statuses. |
Sentence case
Use sentence case everywhere in the UI: navigation labels, page headings, button labels, table column headers, and form field labels. Title case is reserved for product names and proper nouns only.
| Incorrect | Correct |
|---|---|
| Create New Task | Create new task |
| Project Overview | Project overview |
| Due Date | Due date |
| Assigned To | Assigned to |
| Activity Log | Activity log |
Action verbs for CTAs
Button labels are verbs that describe what the action does, not vague calls to action. Lead with the verb. Keep labels short — one or two words is ideal.
| Avoid | Prefer |
|---|---|
| Click here | Save changes |
| Submit | Create task |
| OK | Confirm |
| Yes, delete it | Delete task |
| Close | Cancel (when dismissing a form) or Close (when closing info) |
// Confirmation dialog — clear, specific actions
<Dialog>
<DialogHeader>
<DialogTitle>Delete task</DialogTitle>
<DialogDescription>
This task and all associated comments will be permanently deleted.
This cannot be undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline">Cancel</Button>
<Button variant="destructive">Delete task</Button>
</DialogFooter>
</Dialog>Error messages
State the problem clearly, then suggest what the user can do about it. Avoid technical jargon. Never blame the user.
| Avoid | Prefer |
|---|---|
| Error 422: Unprocessable Entity | Title is required. |
| Something went wrong | Could not load tasks. Refresh to try again. |
| Invalid input | Due date must be today or later. |
| Network error occurred | Could not connect. Check your internet connection. |
Inline field errors appear below the input. They should be short — one sentence — and avoid restating the field name if it is already visible.
// Field error — direct and specific
<div className="space-y-1.5">
<label htmlFor="title" className="text-sm text-foreground">
Title
</label>
<input
id="title"
className="border-destructive ring-destructive/20"
aria-describedby="title-error"
aria-invalid="true"
/>
<p id="title-error" className="text-xs text-destructive">
Title is required.
</p>
</div>Status labels
Status values are always capitalized as proper nouns. Use consistent terminology across the admin app and any client-facing surfaces.
| Status | Display label | Usage |
|---|---|---|
todo | To do | Task not yet started |
in_progress | In progress | Actively being worked on |
in_review | In review | Awaiting approval or feedback |
done | Done | Completed successfully |
blocked | Blocked | Cannot proceed — dependency or impediment |
cancelled | Cancelled | Deliberately stopped, not failed |
Empty states
Empty states should tell the user why the area is empty and what they can do about it. A good empty state has four elements: an icon, a short heading, a one-sentence description, and a primary action.
// Empty state pattern
<div className="flex flex-col items-center gap-3 py-16 text-center">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-surface-200">
<ClipboardList size={18} className="text-foreground-muted" aria-hidden="true" />
</div>
<div className="space-y-1">
<p className="text-sm font-medium text-foreground">No tasks yet</p>
<p className="text-sm text-foreground-lighter">
Create your first task to start tracking work.
</p>
</div>
<Button size="md" onClick={onCreateTask}>
<Plus size={14} aria-hidden="true" />
New task
</Button>
</div>Numeric formatting
Use consistent number formatting across the interface. Never abbreviate numbers in a context where precision matters (a task count, a record ID). Abbreviate only in dashboards and summary tiles where space is constrained.
| Context | Format | Example |
|---|---|---|
| Record count in a table | Full number | 1,284 tasks |
| Dashboard summary tile | Abbreviated | 1.3k |
| Dates (absolute) | Day Mon Year | 12 Jan 2025 |
| Dates (relative, recent) | Relative phrase | 2 hours ago, yesterday |
| Percentages | No decimal unless significant | 72%, not 72.3% |