[§] /docs/rules/copy
Copy
Buttons say what they do, errors say how to fix it, and one spelling, one date format and one case style run through the whole product.
TL;DR
- Buttons start with a verb: "Save changes", not "OK".
- Errors say what went wrong and how to fix it, next to the field.
- Every empty state has one clear action.
- Numbers and dates go through
Intl. - One spelling (British here), sentence case, short sentences.
Microcopy is interface. The label on a button decides whether people press it, and an error message decides whether they recover or leave. These conventions keep copy short, specific and consistent, so the words stop being the thing people notice.
Buttons and links
Buttons and links: table (6 rows)
| Do | Don't | Rule |
|---|---|---|
| Save changes | OK | Verb plus object. |
| Delete project | Yes | The confirm button repeats the action. |
| Create account | Submit | Say what happens, not what the form does. |
| Rename… | Rename | An ellipsis (…) means "asks for more input first". |
| Saving… | Loading | Name the action in progress. |
| Read the media rules | Click here | Link text makes sense on its own. |
- MUST start every button label with a verb.
- MUST use the same verb on the trigger, the dialog title and the confirm button: "Delete project?" then "Delete project".
- MUST label the escape route "Cancel", and make it do nothing destructive.
- SHOULD keep button labels to 1 to 3 words.
- SHOULD use the single ellipsis character
…, not three full stops. - MUST NOT use "Yes" and "No" as dialog buttons. People skim the question.
Sentence case
Use sentence case everywhere: page titles, headings, buttons, menu items, tabs. Capitalise only the first word and proper nouns. Material and Apple's HIG both use sentence case for most UI text; pick it and apply it everywhere.
Sentence case: table (3 rows)
| Do | Don't |
|---|---|
| Create new project | Create New Project |
| Sign in with GitHub | Sign In With Github |
| Rules & conventions | Rules & Conventions |
- MUST keep product names and brands in their own casing: GitHub, iPhone, Next.js.
- MUST NOT set whole labels in capitals in the source text. If a label should look uppercase, use
text-transform: uppercaseso screen readers do not spell it out.
Error messages
[01]
What happened
in plain words, no codes first
[02]
Why
only if it helps them act
[03]
How to fix it
one concrete next step
✕ vague
Error 422: Invalid input.
✓ specific
That email is already registered. Sign in instead, or use a different address.
Error messages: table (6 rows)
| Situation | Message |
|---|---|
| Empty required field | Enter your email address |
| Wrong format | Enter an email address like name@example.com |
| Too long | Project name must be 40 characters or fewer |
| Network failure | Could not save. Check your connection and try again. |
| Permission | Only owners can delete projects. Ask an owner to do it. |
| Unknown server error | Something went wrong on our side. Try again in a minute. |
- MUST show the error next to the field that caused it, and repeat a summary at the top for long forms.
- MUST keep what the user typed. Never clear a form because one field failed.
- MUST NOT blame the user ("You entered an invalid…") or joke about failures.
- MUST NOT lead with a code. If support needs one, put it last: "…try again. (Ref: 8F2A)".
- SHOULD validate on blur or submit, not on every keystroke.
Empty states
An empty state is the first screen many people see. Treat it as onboarding, not as an absence.
Empty states: table (3 rows)
| Part | Example |
|---|---|
| What this place is | No projects yet |
| Why it is empty, or what goes here | Projects hold your pages, tokens and assets. |
| One action | Create project |
- MUST give every list, table and search a designed empty state.
- MUST offer exactly one primary action. A second one can be a text link.
- SHOULD make "no results" different from "nothing yet": "No projects match 'riso'. Clear the filter."
Numbers, dates and units
const gb = 'en-GB';
new Intl.NumberFormat(gb).format(12500); // "12,500"
new Intl.NumberFormat(gb, { style: 'currency', currency: 'GBP' }).format(9.5); // "£9.50"
new Intl.NumberFormat(gb, { notation: 'compact' }).format(12500); // "13K"
new Intl.DateTimeFormat(gb, { dateStyle: 'medium' }).format(date); // "29 Sept 2026"
new Intl.RelativeTimeFormat(gb, { numeric: 'auto' }).format(-1, 'day'); // "yesterday"Numbers, dates and units: table (8 rows)
| Thing | Convention | Example |
|---|---|---|
| Dates in UI | Day, short month, year, from Intl | 29 Sept 2026 |
| Dates in data, URLs, files | ISO 8601 | 2026-09-29 |
| Recent times | Relative, up to 7 days, with the full date on hover | 3 hours ago |
| Times | One clock across the product | 14:30 or 2:30pm, never both |
| Large numbers | Thousands separator | 12,500 |
| Numbers with units | Non-breaking space between them | 10 MB, 250 ms |
| Ranges | "to", or an en dash with no spaces | 150 to 400ms |
| Counts | Words for zero to nine in prose, digits in UI | "three steps", "3 items" |
- MUST NOT write all-number dates such as 09/10/2026. Half the world reads it as September, half as October.
- MUST format numbers and dates with
Intl, not string concatenation, so the locale does the work. - MUST handle plurals properly: "1 item", "2 items", "No items".
Intl.PluralRuleshelps. - SHOULD use
font-variant-numeric: tabular-numswhere numbers line up or change.
One spelling
Choose British or US English once, write it in the README, and apply it everywhere users read. This guide uses British English.
One spelling: table (6 rows)
| British | US |
|---|---|
| colour | color |
| organise | organize |
| centre | center |
| licence (noun) | license |
| cancelled | canceled |
| grey | gray |
- MUST set the matching
langon<html>:en-GBoren-US. - MUST keep code in its own spelling. CSS says
colorandcenter; your prose says colour and centre. Do not "fix" either. - SHOULD name tokens in the code's spelling (
--color-accent) and describe them in the prose spelling. - SHOULD add a spell-check dictionary set to the chosen variant in the editor and CI.
Voice rules
- MUST write short sentences. One idea each.
- MUST address people as "you", and the product as "we" only when it is a person acting (support, a team).
- MUST use one word per concept: pick "Sign in" or "Log in" and never mix them.
- SHOULD use curly quotes and apostrophes (’ “ ”) in rendered text.
- SHOULD NOT use exclamation marks outside genuine celebration, such as a first successful deploy.
Why it works
Specific copy removes guesswork. A button that says "Delete project" cannot be misread, an error that ends with a fix gets people moving again, and one date format means nobody has to wonder which month it is. Consistency does the rest: once every label follows the same pattern, people stop reading words and start recognising them.
Sources
Accessibility
WCAG 2.2 AA turned into rules you can check in five minutes with a keyboard, a contrast checker and the browser's accessibility tree.
Structure and performance
Where tokens, components and content live in a Next.js project, and the budgets that keep it fast: LCP 2.5 s, INP 200 ms, CLS 0.1.