Skip to content

Microcopy

A button that says "OK" makes you read the whole dialog again to find out what OK will do. The words in an interface are controls, just like the buttons they sit on.

Interface words are interface: buttons name the action, labels name the thing, and errors say what happened and how to fix it.

Project Settings

Toggle on/off

Press Remove in both modes. "Are you sure?" with Yes and No asks you to remember what you clicked. "Delete project" says the verb and the object, so the button still makes sense if it's the only thing you read. Whether to ask at all is a separate question, covered in destructive actions.

Labels are nouns, not questions: "Project name", not "What do you want to call it?". Pick one word for each thing and use it everywhere. Generic mode calls it a project, then a workspace, and the button says Remove while the dialog says deleted. Two names for one thing make people wonder whether it's two things.

Write in sentence case. GOV.UK asks for it on buttons. Apple lets you choose title or sentence case per element, as long as you stay consistent. I prefer sentence case everywhere: it reads like speech, and nobody has to argue about which words get a capital.

Errors

An error has two jobs: say what happened, and say what to do next.

  • beach-day.heic24 MB

    An error occurred.

  • Visa •••• 4242$68.00

    Error 402: Payment Required

  • @janeUsername

    You entered an invalid username!

"An error occurred" does neither. "Error 402" is for the server log. "You entered an invalid username!" blames the person for a rule they couldn't see. NN/g and GOV.UK both warn against "invalid", and GOV.UK adds "please", "sorry" and "oops" to the list. None of them help fix anything. For when to show an error, see input details.

Counts and ellipses

"1 files" is what happens when a string is built with ${n} files. Plurals differ by language: English has two forms, Polish has four, Arabic has six. Intl.PluralRules knows them all.

1 files selected
Uploading 1 files...

Format big numbers with separators and use tabular numbers for counts that change. Zero is its own case: "No files selected" reads better than "0 files selected", and an empty list deserves a real empty state.

Use the real ellipsis "…", one character, not three periods. On a button or menu item it means the action needs more input before it runs, like "Rename…". On a status it means something is in progress, like "Saving…".

Usage

app.tsx
<label for="name" class="text-sm">Project name</label>
<input id="name" aria-invalid="true" aria-describedby="name-error" />
<p id="name-error" class="text-sm text-red-600">Enter a project name</p>

<button class="rounded-full bg-black px-3 py-1.5 text-sm text-white">
  Save changes
</button>
const plural = new Intl.PluralRules("en-US");
const number = new Intl.NumberFormat("en-US");
const forms: Partial<Record<Intl.LDMLPluralRule, string>> = {
  one: "file",
  other: "files",
};

function files(count: number) {
  if (count === 0) return "No files";
  const form = forms[plural.select(count)] ?? forms.other;
  return `${number.format(count)} ${form}`;
}

<span className="tabular-nums">{files(1284)} selected</span>;
// 1,284 files selected

Keep whole sentences in one string with placeholders, never glue fragments together like "Delete " + noun. Word order changes between languages, and a translator can't fix a sentence they only see in pieces.

Resources

Search concepts

Search for a command to run...