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.
- 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)”.
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”- ✅
<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> - ❌
<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)