Patterns · composition guide
UX writing pattern
The WORDS in every product surface — sentence-case casing, one canonical label, and specific / actionable / blameless error-empty-feedback microcopy — applied to the labels and copy the existing components render.
◆ generated from guidelines/ux-writing.json + parts/ux-writing.guide.md — do not edit by hand
Based on
The rationale, worked examples, and the full assertion set live in the design skill — this pattern distils the enforceable subset and links each rule back to it.
aha-design:aha-design-ux-writing
judge aha-design:aha-design-ux-writing-judge
Choose the surface
| Surface | Use for | Example |
|---|---|---|
| Label / CTA / menu / tab | the name of an action or destination — sentence case, one canonical form reused everywhere | View report, Create presentation, Multiple choice |
| Heading / dialog title | section and dialog titles — sentence case; a title carries the OUTCOME | We couldn't open your presentation |
| Empty state | success-but-no-data — encouraging copy + a primary create action, never an apology | No presentations yet. Create your first one. |
| Error / load-failure message | a recoverable problem — name the object, give the next step, expose the recovery control | Couldn't load some presentations. Check your connection and try again. |
Composed of
What a compliant ux writing surface reuses from this design system — the pattern's link into the component graph.
| Reuses | Kind | For | In DS? |
|---|---|---|---|
button | component | action/CTA labels the rule cases (sentence case, canonical form) and the in-message recovery control (Retry/Reload/Log in again) | available |
icon | component | the glyph beside a message is functional only; the words, not the glyph, carry the outcome | available |
--aha-color-error | token | a destructive/data-loss or fatal failure message binds its emphasis to the error role — wording severity matches the colour, never a hardcoded red | available |
--aha-color-warning | token | a transient retryable hiccup reads as warning/info tone, not a blaring error — the word severity matches a calmer token | available |
alert | component | recovery-required copy lives in an inline Alert that survives, not an auto-dismissing toast — the shared DS V3 Alert, never a bare AntD Alert | missing |
toast | component | transient, no-recovery-needed confirmations only; recovery copy must not live here alone | missing |
empty-state | component | the success-no-data shell whose copy is encouraging + a primary create action, kept distinct from a failure | missing |
error-page | component | the full-page failure whose headline names the object + outcome and offers a next step | missing |
Rules
The shippable checklist — each rule traces to an assertion in aha-design:aha-design-ux-writing.
| Rule | Skill assertion |
|---|---|
| Sentence case is the house default for ALL product UI text — labels, CTAs, menu/tab/nav items, slide-type and interactive-component names, headings, tooltips, helper text, dialog titles, empty states, and messages. Capitalize the first letter only; keep proper nouns, brand names, and acronyms as-is. 'View report' not 'View Report'; 'Multiple choice' / 'Word cloud' / 'Open-ended' / 'Spinner wheel' / 'Q&A' not Title Case. | UXW-1 |
| Pick ONE canonical cased form per label and use it in every place it appears — button, menu, tooltip, analytics label. Resolve AI drift (card says 'View Report', menu says 'View report') to the sentence-case 'View report'; never ship both forms. | UXW-1 |
| The styled all-caps eyebrow / overline / chip label (the 'capitalized' typography role, owned by aha-design-typography) is intentionally uppercased — it is a deliberate style, not a casing mistake. Don't 'fix' it to sentence case, and don't cite it to justify Title Case elsewhere. | UXW-1 |
| A message names the OUTCOME (what happened, always), adds the CAUSE only when it changes what the user does, always states the NEXT step for anything recoverable, and exposes the ACTION when a one-tap recovery exists. Title = the OUTCOME; body = the NEXT step — never inverted. | UXW-2 UXW-3 UXW-7 |
| Name the specific object and what happened — a concrete count over 'some'. 'Couldn't load some presentations.' not 'Some items could not be loaded.' Never ship a standalone 'Something went wrong', 'An error occurred', 'Oops', 'Unexpected error', 'Some items could not be loaded', or bare 'Failed'. | UXW-2 |
| Any message about a recoverable problem must tell the user what to do now AND expose the recovery as a real control when a one-tap recovery exists (Retry, Reload, Log in again, Go back, Contact support). A message with an outcome but no next step is a dead-end. | UXW-3 |
| Be blameless, jargon-free, and front-loaded. Don't blame the user ('That file type isn't supported — try a PNG or JPG', not 'You entered an invalid file'). Keep raw codes / stack traces / HTTP status out of the headline — tuck any code into secondary detail. Lead with the user's goal, not the system's failure. | UXW-4 |
| Match wording severity to real impact. A transient, retryable hiccup is a calm info/warning with 'Try again', not a blaring red 'Error' — over-alarming trains users to ignore real errors. A destructive or data-loss outcome states the stakes plainly ('This permanently deletes 12 responses.') without crying wolf or burying the lede. | UXW-5 |
| An empty state (success, no data) is encouraging with a primary next action ('No presentations yet. Create your first one.') — never an apology or a red alarm. A load failure follows be-specific + never-dead-end (name it, offer Retry) and is never disguised as emptiness, which would hide the Retry the user needs. | UXW-6 |
| Recovery-required copy must not live only in an auto-dismissing toast — it belongs in an inline Alert or on the page so the words survive. When a message has a title + body, the title carries the outcome and the body the next step (a shapeless single-string message isn't penalised on shape — its vagueness is a be-specific problem). | UXW-7 |
Composition code
Doc-only — this pattern ships no wrapper from this repo. Doc-only pattern — ships no composition wrapper or primitive from this repo. It governs the WORDS inside components that already exist or are backlogged; it owns no code surface of its own. The feedback/empty/error COMPONENTS that host this copy (Alert, toast, ErrorPage, Empty) are owned by aha-design-feedback and aha-design-shared-components; the type SCALE and the all-caps label role by aha-design-typography; the brand VOICE by aha-branding. If a copy-linting helper ever graduates into this DS, add a reuse block and it is gated like a composite.
Guide
UX writing — composition guide
aha-design-ux-writing. The skill holds the full rationale, the worked before/after library (references/patterns.md), and all UXW-x assertions; this guide is the shippable checklist. The DS now OWNS this build ruleset — the judge and evals stay in the skill. When the two ever disagree, the skill wins and this file is regenerated.UX writing is the most cross-cutting surface in the product: the same words and casing appear on a button, a menu item, a tooltip, an empty state, and an error message, across the editor, dashboard, settings, and audience UIs. This pattern adds no new control — it reuses the Button label, the functional Icon, and the --aha-color* feedback tokens, and defines the wording conventions that keep every string reading as one product. It owns what the copy says and how it's cased — not how the surface looks (the component skills) and not the brand voice it's said in (aha-branding).
Pick the surface first
| Surface | Use for |
|---|---|
| Label / CTA / menu / tab | The name of an action or destination — sentence case, one canonical form reused everywhere |
| Heading / dialog title | Section and dialog titles — sentence case; the title carries the OUTCOME |
| Empty state | Success-but-no-data — encouraging copy + a primary create action, never an apology |
| Error / load-failure message | A recoverable problem — name the object, give the next step, expose the recovery control |
Casing — sentence case is the house default · UXW-1
Sentence case is the default for all AhaSlides product UI text: button & CTA labels, menu / tab / nav items, slide-type and interactive-component names, section headings, tooltips and helper text, dialog titles, empty states, and message copy.
- Capitalize the first letter only. Keep proper nouns, brand names, and acronyms as-is.
- ✅ "View report" ❌ "View Report" · "VIEW REPORT" · "view report"
- Slide-type & interactive-component names are sentence case too:
- ✅ "Multiple choice" · "Word cloud" · "Open-ended" · "Spinner wheel" · "Brainstorm" · "Rating scale"
- ✅ acronyms/brand kept: "Q&A" · "AI slide" · "AhaSlides"
- ❌ "Multiple Choice" · "Word Cloud" · "Open-Ended" · "Spinner Wheel"
- One canonical form, everywhere. Pick one cased form for a label and use it in every place it
appears — button, menu, tooltip, analytics label. AI-generated UI drifts ("View Report" on the card, "View report" in the menu); the canonical answer is sentence case "View report" — never ship both.
The one exception — a typography role, not a violation. The styled all-caps eyebrow /
overline / chip label (the capitalized role) is intentionally uppercased and is owned by aha-design-typography. It is a deliberate style, not a casing mistake — don't "fix" it to sentence case, and don't cite it to justify Title Case elsewhere.
Errors, empty states & feedback microcopy
The anatomy of a good message
| Part | What it is | When |
|---|---|---|
| OUTCOME | what happened, in the user's terms | always |
| CAUSE | why, in plain language | when it helps the user act |
| NEXT | what to do now | always, for anything recoverable |
| ACTION | the button/link that does the NEXT | whenever a one-tap recovery exists |
- Title = the OUTCOME. Body = the NEXT step. Not the reverse.
- Drop CAUSE when it's noise ("a network request failed") but keep it when it changes what the user
does ("You're offline", "That file is over 50 MB").
- A message with an OUTCOME but no NEXT is a dead-end — the "*Error? so what now?*" reaction.
Be specific — name the object and what happened · UXW-2
- ❌ "Some items could not be loaded." → ✅ "Couldn't load some presentations."
- ❌ "Something went wrong." / "An error occurred." / "Oops!" → name the object + verb.
- Prefer a concrete count ("3 presentations didn't load") over "some".
- Banned as a standalone message: *Something went wrong · An error occurred · Oops ·
Unexpected error · Some items could not be loaded · Failed.*
Never dead-end — always give the next step, and the action if one exists · UXW-3
Any message about a recoverable problem must tell the user what to do now and expose the recovery as a real control when a one-tap recovery exists (Retry, Reload, Log in again, Go back, Contact support). The recovery lives in the message, not only in a toast that vanishes.
Human, blameless, jargon-free · UXW-4
- Don't blame the user. ❌ "You entered an invalid file." → ✅ "That file type isn't
supported — try a PNG or JPG."
- No raw codes / stack / HTTP status as the headline — tuck any code into secondary detail.
- Lead with the user's goal, not the system's failure. ❌ "An exception was thrown" →
✅ "We couldn't open your presentation."
Match severity to reality · UXW-5
- A transient, retryable hiccup is an info/warning with a calm "Try again", not a blaring red
"Error" — over-alarming trains users to ignore real errors.
- A destructive or data-loss outcome states the stakes plainly ("This permanently deletes 12
responses."). Don't cry wolf; don't bury the lede.
An empty state is not an error · UXW-6
- Empty (success, no data): encouraging + a primary next action. ✅ "No presentations yet.
Create your first one." Never an apology, never a red alarm.
- Failure (couldn't load): follow UXW-2/3 — name it, offer Retry. Never disguise a failure as
emptiness ("No presentations" when the fetch errored hides the Retry the user needs).
Put the message where it survives — right home & shape · UXW-7
- (a) Survives. Recovery-required copy must not live only in an auto-dismissing toast; it
belongs in an inline Alert or on the page. (Surface mechanics owned by aha-design-feedback; this is about not stranding the *words*.)
- (b) Shape (only when there's a title + body). Title carries the outcome; body the next step —
not inverted. A shapeless single-string message is fine here — its vagueness is a UXW-2 problem, not a UXW-7 one.
Worked example
Vague object → named (UXW-2); dead-end → next step + Retry (UXW-3); calm retryable tone, not a fatal red error (UXW-5).
Universal principles (every string)
Applied by every area above; the areas make them concrete.
- Consistent — one canonical form per label/term, used everywhere.
- Specific — name the real object; no vague placeholders.
- Actionable — tell the user what to do next; labels name the action.
- Human & plain — blameless, jargon-free, front-loaded.
Areas still to fill
The skill is deliberately incomplete — these areas are owned there but not yet written. Until they are, apply the universal principles above; each lands here with its own rule + ref as it's written.
- Action-label style — verb-first, concise button/CTA labels ("Create presentation", not
"Presentation creation"); primary vs secondary phrasing.
- Terminology & glossary — one word per concept across the product (presentation vs deck vs
slideshow); the canonical AhaSlides term list.
- Tooltips & helper text — when to add, length, imperative vs descriptive.
- Onboarding & first-run copy — empty-first-run states, coach marks, checklists.
- Confirmation & consent copy — the words in destructive/consequential dialogs.
*Full detail, the before/after library, and the stubbed areas live in the aha-design-ux-writing
skill. Self-check any copy with aha-design-ux-writing-judge and fix every FAIL before shipping. Stay in your lane: the surface LOOK is aha-design-feedback / aha-design-shared-components, the type SCALE is aha-design-typography, and the brand VOICE is aha-branding:aha-branding-tone-voice.*