Content
Editorial Ink reads like a newsroom, not a dashboard: plain sentences, sentence case, verbs on buttons, and no exclamation marks. These rules are the defaults an agent uses when it writes UI text into a plan, and the checks an audit applies to labels, headings, and messages.
- Layer
- Guideline
- Rules
- 18 numbered
- Examples
- 13 do and don’t
- Updated
- Source
- content.md
Plain, calm, exact: copy in place
Buttons
Verb plus object. Cancel or “Keep” plus the object.
Destructive action
Names the object. No “Confirm”, no “Yes”.
Field error
Direct, no blame, says the fix.
Status words
Sentence case, written as read; the component owns the style.
The voice is the part that never changes. Editorial Ink sounds like a good copy editor: three attributes, in this order when they conflict.
| Attribute | Means | Not |
|---|---|---|
| Plain | Everyday words, short sentences, one idea per sentence | Jargon, codes, marketing adjectives |
| Calm | States facts without alarm or celebration | Exclamation marks, “Oops”, “Awesome”, urgency that is not real |
| Exact | Names the object and the outcome | “Something went wrong”, “Submit”, “Click here” |
Tone by situation
Section titled “Tone by situation”Tone is how the voice adjusts to the moment. The voice stays the same; the amount of warmth and detail changes.
| Situation | Tone | Example |
|---|---|---|
| Field error | Direct, no blame, says the fix | “Enter a date after today.” |
| System error | Owns the failure, gives the next step | “Could not save the story. Try again in a few minutes.” |
| Warning | Serious, names the consequence before it happens | “Publishing replaces the live version. Readers see the change immediately.” |
| Destructive confirmation | Serious and specific, no softening | “Delete article?” / “Delete article” |
| Success | Brief and factual, past tense | “Story published” |
| Empty state | Practical, points to the next action | “No stories yet” / “Stories you draft or import appear here.” |
| Help text | Neutral, adds what the label cannot | “Shown under the headline on the home page.” |
- Sentence case everywhere: headings (
Heading), button labels (Button,IconButtonaria-label), field labels (FormControl.Label), tags (Tag), tab labels, menu items, table headers, and dialog titles. Only the first word and proper nouns are capitalised: “Recent stories”, “Save changes”, “Publish date”. Title Case and ALL CAPS are never written in app code; the tracked-caps look ofTable.Headeris applied by the component, not by the text, andTagtext stays sentence case (“In review”, not “IN REVIEW”). - Button labels are verbs that name the outcome: “Save changes”, “Create story”, “Send invite”, “Add author”, “Export CSV”. “Submit”, “OK”, “Yes”, “No”, “Click here”, and “Continue” without an object are never labels. A label is one to three words.
- Destructive actions name the object, in the dialog title and on the button:
title="Delete article?"with<Button variant="danger">Delete article</Button>. “Are you sure?” and “Confirm” are not titles or labels. The cancelling action is “Cancel” or “Keep article”. - Empty-state copy follows one pattern:
titlenames what is empty in three to five words (“No stories yet”, “No authors match”);descriptionsays why it is empty or what to do, in one sentence (“Stories you draft or import appear here.”); theactionlabel is the verb that fills it (“Create story”). No exclamation marks, no “Oops”. - Error messages say what happened, then how to fix it, in one or two sentences: “Enter a valid email address.” “The image is larger than 10 MB. Choose a smaller file.” “Could not save the story. Try again in a few minutes.” They never blame the user (“You entered an invalid email”), never use jargon or codes alone (“Error 422”), and never apologise (“Sorry!”).
- Success and status copy states the fact in past or present tense: “Story published”, “Changes saved”, “3 stories imported”. No exclamation marks, no “Success!”, no “Awesome”.
- No exclamation marks anywhere in UI text: not in toasts, banners, empty states, headings, or captions.
- Help text (
FormControl.Caption) is one sentence that adds information the label does not: what the value is used for, a constraint, or a format. It never repeats the label and never starts with “Please”. - Placeholders are examples of format only (“name@example.com”, “12 Sep 2026”), never instructions (“Enter your email”) and never the label.
- Tags and statuses are one or two words, sentence case: “Published”, “In review”, “Needs edit”. Not “PUBLISHED”, not “published”, not “This story has been published”.
- Dates: formatted with
Intl.DateTimeFormatin the user’s locale (CalendarandDatePickertakelocale), never hand-assembled. Recent events use relative time in a muted caption (“3 minutes ago”, “Yesterday”) and switch to an absolute date after about a week (“12 Sep 2026”). Absolute dates spell the month (“12 Sep 2026”, “September 12, 2026”), never all-numeric (“09/12/2026”), so day and month cannot be confused. Times include a zone when readers may be elsewhere (“14:00 UTC”). - Numbers: formatted with
Intl.NumberFormatin the user’s locale (thousands separators, “12,480”); large counts abbreviate only inBadge(max, “99+”) and in summary tiles (“1.2M” with the exact value in aTooltipor caption). Numeric columns are right-aligned withalign="end". Units follow the number with a space (“10 MB”, “3 min”). - Product and proper nouns keep their own capitalisation (“tenet-ui”, “GitHub”, “Storybook”, “Newsroom” when it is the product name) and are otherwise the only capitals inside a sentence-case string. Generic features are lower case (“the dashboard”, “a story”, “your drafts”).
- People are addressed as “you”; the product does not refer to itself as “we” or “I”. Copy uses contractions where natural (“can’t”, “you’re”) and avoids idioms that do not translate.
- Links describe their destination (“View billing settings”, “Open the changelog”), never “here” or a bare URL.
Link externaladds the new-tab warning; the text does not need to say “(opens in new tab)”. - Unknown people are “they”. Copy and examples do not assume a gender (“Ask the author to review their draft”, not “his draft”), and groups are “everyone” or “the team”, never “guys”.
- Words name what they mean, not a loaded metaphor: “blocklist” and “allowlist”, “main branch”, “placeholder value”, “quick check”, “stop the job”. Not “blacklist”, “master”, “dummy value”, “sanity check”, “kill”.
- Example people, names, and places in placeholders, empty states, and docs come from more than one culture, and never stand for a real person.
Patterns
Section titled “Patterns”| Slot | Pattern | Example |
|---|---|---|
| Page title | noun or noun phrase | “Stories”, “Author settings” |
| Primary button | verb + object | “Create story”, “Save changes” |
| Destructive button | verb + object | “Delete article”, “Remove author” |
| Cancel button | “Cancel” or “Keep <object>” | “Cancel”, “Keep draft” |
| Empty-state title | “No <things> yet” or “No <things> match” | “No stories yet” |
| Empty-state description | why, or what to do | “Stories you draft or import appear here.” |
| Field error | what happened, how to fix | “Enter a date after today.” |
| Server error banner | title = what failed; body = why and next step | “Could not publish” / “The image is larger than 10 MB. Choose a smaller file.” |
| Success toast | title = fact; message = consequence | “Story published” / “Readers can see it now.” |
| Status tag | one or two words | “In review” |
Do / Don’t
Section titled “Do / Don’t”Do
<Button variant="primary">Create story</Button><Dialog title="Delete article?" description="Readers will lose access immediately." footer={<><Button variant="invisible">Cancel</Button><Button variant="danger">Delete article</Button></>} /><EmptyState title="No stories yet" description="Stories you draft or import appear here." action={<Button variant="primary">Create story</Button>} /><FormControl.Validation variant="error">Enter a valid email address.</FormControl.Validation>toast({ variant: 'success', title: 'Changes saved', message: 'The story updates for readers within a minute.' })<Text tone="muted" size="small">{new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(story.publishedAt)}</Text>Don’t
<Button variant="primary">Submit</Button>Not a verb that names the outcome
<Heading level={2}>Recent Stories</Heading>Title Case
<Dialog title="Are you sure?" footer={<Button variant="danger">Confirm</Button>} />Neither names the object
<EmptyState title="Oops! Nothing here" description="Looks like you haven't added anything yet!" />Exclamation marks, no named object, no next step
<FormControl.Validation variant="error">Invalid input</FormControl.Validation>No cause, no fix
<Tag variant="success">PUBLISHED</Tag>Caps written into the text; the component owns the style
<Text>{story.publishedAt.toLocaleDateString()}</Text>Rendering “9/12/2026” (all-numeric, ambiguous order)