Skip to content

DOMAIN:CONTENT — UX WRITING & MICROCOPY

OWNER: jouke, dinand
ALSO_USED_BY: benjamin, joost, floris, floor (Frontend), dima (Intake — public-facing)
UPDATED: 2026-03-24


UX_WRITING:ERROR_MESSAGES

RULE: every error message answers THREE questions
1. What happened? (specific, not vague)
2. Why did it happen? (cause)
3. How to fix it? (actionable next step)

Error Message Template

[What happened]. [Why / cause]. [How to fix].

EXAMPLE (good): "Your password must be at least 8 characters. You entered 5. Add 3 more characters to continue."
EXAMPLE (bad): "Invalid password."
EXAMPLE (good): "We couldn't save your changes because your session expired. Sign in again to continue."
EXAMPLE (bad): "Error 403."

Error Message Rules

RULE: never blame the user — say "That email address isn't valid" not "You entered an invalid email"
RULE: never use technical jargon — no "500 Internal Server Error" in UI
RULE: never use ALL CAPS for errors — it reads as shouting
RULE: use sentence case for error text
RULE: keep errors under 2 sentences for inline validation
RULE: for complex errors, offer a "Learn more" link
RULE: error messages MUST be translatable — no string concatenation

Error Severity Tones

Severity Tone Example Prefix
Validation Helpful, immediate "Enter a valid..."
Warning Cautious, informative "This will..."
Blocking Empathetic, solution-focused "We couldn't... Try..."
System error Calm, reassuring "Something's not working. We're looking into it."
Destructive Serious, confirming "This permanently deletes... This can't be undone."

ANTI_PATTERN: "Something went wrong" with no context
FIX: specific message + error code + "Contact support with code X if this continues"

ANTI_PATTERN: "Oops!" or cutesy language on serious errors (payment failure, data loss)
FIX: match tone to severity — humor is never appropriate for money or data

ANTI_PATTERN: error message that differs between platforms (web vs. mobile)
FIX: single source of truth for copy — i18n JSON file, not hardcoded strings


UX_WRITING:EMPTY_STATES

RULE: every empty state has THREE elements
1. Illustration or icon (visual, not just text)
2. Explanation (what this area will show)
3. Call to action (how to populate it)

Empty State Types

First-use (onboarding)
- Tone: encouraging, value-focused
- EXAMPLE: "No projects yet. Create your first project to start building."
- RULE: show the primary benefit, not the feature name

No results (search/filter)
- Tone: helpful, suggestive
- EXAMPLE: "No results for 'xyz'. Try a different search term or remove filters."
- RULE: suggest next actions — broaden search, clear filters, check spelling

User-cleared (all items done/deleted)
- Tone: celebratory or neutral
- EXAMPLE: "All caught up! No new notifications."
- RULE: acknowledge the achievement if context allows

Error state (failed to load)
- Tone: honest, recoverable
- EXAMPLE: "Couldn't load your projects. Check your connection and try again."
- RULE: always provide retry action

ANTI_PATTERN: blank white screen with no explanation
FIX: every empty container has an empty state component

ANTI_PATTERN: empty state that just says "No data"
FIX: explain what data will appear AND how to create it


UX_WRITING:BUTTONS_AND_CTAS

RULE: button labels use verb + object format — "Create project", "Send invoice", "Download report"
RULE: never use generic labels — "Submit", "OK", "Click here"
RULE: destructive actions use specific labels — "Delete project" not "Delete"
RULE: primary action is specific, secondary is generic — "Save changes" / "Cancel"
RULE: button text is max 3-4 words
RULE: use sentence case for buttons (not Title Case, not ALL CAPS)

Confirmation Dialogs

RULE: dialog title states the action — "Delete this project?"
RULE: dialog body states consequences — "This permanently deletes the project and all its data."
RULE: confirm button repeats the action verb — "Delete project" (not "Yes" or "OK")
RULE: cancel button says "Cancel" or "Keep project" (not "No")

Pattern Good Bad
Save "Save changes" "Submit"
Delete "Delete project" "OK"
Navigate "Go to dashboard" "Click here"
Create "Create account" "Next"
Send "Send message" "Done"
Confirm "Place order" "Confirm"

ANTI_PATTERN: "Are you sure?" dialogs with "Yes" / "No" buttons
FIX: title = action question, buttons = specific action / "Cancel"


UX_WRITING:FORM_LABELS_AND_VALIDATION

Labels

RULE: labels are nouns or noun phrases — "Email address", "Password", "Company name"
RULE: labels are always visible — never rely on placeholder text alone
RULE: required fields marked with asterisk (*) — explain convention once at form top
RULE: optional fields marked with "(optional)" suffix — when most fields are required

Placeholders

RULE: placeholders show format hints — "name@example.com", "DD/MM/YYYY"
RULE: placeholders are NOT labels — they disappear on focus
RULE: placeholders use lighter text color (AA contrast not required but recommended)

Help Text

RULE: help text appears below the field, not in tooltip
RULE: help text explains constraints — "Must be at least 8 characters"
RULE: help text is always visible, not hidden behind an icon

Inline Validation Messages

RULE: validate on blur (not on every keystroke)
RULE: show success state for validated fields (green check)
RULE: error messages appear directly below the field
RULE: error message replaces help text (don't show both)
RULE: error message is specific — "Enter a valid email address" not "Invalid input"

ANTI_PATTERN: red field with no message explaining what's wrong
FIX: every invalid field has a visible text message

ANTI_PATTERN: validation that triggers while user is still typing
FIX: validate on blur or on submit, not on input


UX_WRITING:ONBOARDING_FLOW

RULE: progressive disclosure — show only what's needed at each step
RULE: lead with value — "Set up your workspace to start collaborating" not "Step 1 of 5"
RULE: show progress — step indicator or progress bar
RULE: allow skip where possible — "Skip for now, set up later"
RULE: explain WHY you need information — "We use your timezone to schedule notifications correctly"

Onboarding Copy Patterns

Step Type Copy Pattern Example
Welcome Greeting + value prop "Welcome to [App]. Let's get you set up in 2 minutes."
Data collection Why + what "Add your company name. This appears on all invoices."
Feature intro Benefit + action "Invite your team to start collaborating in real time."
Completion Achievement + next step "You're all set! Here's your dashboard."

ANTI_PATTERN: 10-step onboarding with no skip option
FIX: max 3-5 essential steps, defer the rest to contextual discovery

ANTI_PATTERN: "Welcome to [App]! [App] is a platform that..." (feature-first)
FIX: "Welcome to [App]. [What you can achieve]." (value-first)


UX_WRITING:NOTIFICATIONS

Tone Per Channel

Channel Tone Length Urgency
Push notification Brief, actionable <50 chars title, <100 body High — interrupts user
In-app notification Informative, contextual 1-2 sentences Medium — user is already active
Email Complete, professional Full paragraphs OK Low — async
SMS/text Ultra-brief, urgent <160 chars Highest — reserved for critical

RULE: push notifications state the fact + action — "New comment on your project. View now."
RULE: never use push for marketing — only transactional/actionable content
RULE: in-app notifications are dismissible and have timestamps
RULE: email subject lines are specific — "Invoice #1234 is ready" not "Update from [App]"

ANTI_PATTERN: push notification that says "You have a new notification"
FIX: state WHAT happened — "Alex commented on your design"


UX_WRITING:TONE_OF_VOICE

Nielsen Norman Group Four Dimensions

Every piece of copy sits on four spectrums:
1. Funny ←→ Serious — how much humor is appropriate
2. Casual ←→ Formal — register and vocabulary level
3. Irreverent ←→ Respectful — attitude toward conventions
4. Enthusiastic ←→ Matter-of-fact — energy level

RULE: define brand position on each dimension
RULE: create a voice matrix — map tone per scenario

Voice Matrix Template

Scenario Humor Formality Energy Example
Success Light OK Casual Enthusiastic "Project created! Time to build."
Error (recoverable) None Neutral Matter-of-fact "Couldn't save. Check your connection."
Error (critical) Never Formal-ish Calm "Payment failed. Your card was not charged."
Onboarding Light OK Casual Enthusiastic "Let's get started!"
Destructive action Never Serious Calm "This permanently deletes all data."
Empty state Light OK Casual Encouraging "Nothing here yet. Create your first project."

RULE: voice is constant (brand personality), tone adapts to context
RULE: humor scales DOWN with severity — never joke about money, data loss, security
RULE: document the voice in a 1-page Voice Chart with 3-5 adjectives
RULE: include "we are / we are not" pairs — e.g., "Helpful, not patronizing"

Building a Client Voice Guide

VOICE GUIDE TEMPLATE:
1. Brand personality (3-5 adjectives)
2. "We are / We are not" pairs (5-8 pairs)
3. Voice matrix (scenario × tone dimensions)
4. Word list (preferred terms, banned terms)
5. Example copy per scenario (success, error, empty, CTA)
6. Platform adaptations (web vs. mobile vs. email)

UX_WRITING:ACCESSIBILITY

STANDARD: WCAG 2.1 Level AA

RULE: write at 8th-grade reading level (Flesch-Kincaid)
RULE: avoid idioms, metaphors, and culturally-specific references
RULE: use plain language — "use" not "utilize", "start" not "commence"
RULE: front-load important information in sentences
RULE: link text describes destination — "View billing settings" not "Click here"
RULE: alt text for images describes function, not decoration
RULE: screen readers read button labels — make them self-explanatory without visual context
RULE: never convey meaning through color alone — use text + color
RULE: aria-label on icon-only buttons — screen reader must know the action

TOOL: Hemingway Editor — readability scoring
TOOL: axe DevTools — accessibility testing
TOOL: VoiceOver (macOS/iOS) / NVDA (Windows) — screen reader testing
RUN: test every new screen with a screen reader before shipping

ANTI_PATTERN: "Click the red button" (relies on color)
FIX: "Click 'Delete project'" (relies on label)

ANTI_PATTERN: placeholder-only form fields (no visible label)
FIX: always pair visible label with input — placeholder is supplemental