Pular para o conteúdo

Content design — UX writing

foundations specs/foundations/ux-writing.kmd

Voice, tone, vocabulary, and pattern guide for every user-facing string in Koder products. Material parity (`/foundations/content-design/style-guide/ux-writing-best-practices`). Extends `errors/user-facing-messages.kmd` (which covers error copy only) to all UI text — buttons, labels, hints, empty states, success messages.

Quando esta spec se aplica

Triggers primários

Todos os triggers

Corpo da especificação

Spec — Content design / UX writing

Facet Visual do Koder Design. Material parity: https://m3.material.io/foundations/content-design/style-guide/ux-writing-best-practices.

Voice

Koder products speak as:

  • Knowledgeable but not condescending — explain WHAT and (when helpful) WHY, never assume the user is wrong
  • Direct, not bossy — "Save" not "You should save now"
  • Warm, not sappy — friendly without faux-cheer ("Yay!" is out)
  • Specific — "Saved at 14:32" beats "Saved successfully"
  • Confident — declarative, present tense

Tone variants

Tone shifts by context but voice stays constant.

ContextToneExample
SuccessBrief + specific"Profile updated."
ErrorHelpful + non-blaming"We couldn't reach the server. Check your connection and try again."
Empty stateInviting"No projects yet — create your first one to get started."
OnboardingEncouraging + brief"Welcome to Koder Talk. Let's pick a wake word."
Destructive confirmSerious + clear"Delete 12 messages? This can't be undone."
LoadingQuiet"Loading…" (avoid blank fluff like "Please wait while we…")

R1 — Lengths

ElementMax chars (en-US baseline)Notes
Button label24Verb-first ("Save changes", "Delete")
Tab label16Noun ("Profile", "Activity")
Field label32Noun phrase ("Display name")
Hint text80Helpful one-liner
Error message140Brief + actionable (errors/user-facing-messages.kmd)
Section title40Noun phrase
Tooltip body80Brief reinforcement
Empty state body200Inviting + 1 CTA

pt-BR translations may run +30%; design for the longer language and truncate gracefully (no clipping).

R2 — Vocabulary

Canonical terms

Koder canonicalAvoid
Sign in (not "log in")"Login", "Log in"
Sign out"Logout", "Log out"
Tenant / workspace"Account" (ambiguous with billing)
You (second person)"User", "Users" (third person) in UI
Save"Submit" (for non-form workflows)
Cancel"Discard", "Close" (use only for actually closing)
Delete"Remove" (different semantics — remove from view, not data)
Undo"Cancel" (after the fact)
Send"Post" (in non-social contexts)

Forbidden words

  • "Simply", "just", "easy" — they trivialize user effort
  • "Wrong", "invalid", "error" in body text — symbol + spec text per user-facing-messages.kmd
  • "Whoops", "oops" — friendly to a fault; use serious calm
  • "Click", "tap" — surface-neutral language ("Select", "Open")
  • "Awesome", "great", "perfect" as success acknowledgment — over-praise

R3 — Capitalization

Sentence case for everything except product names and proper nouns:

  • ✅ "Save changes"
  • ❌ "Save Changes"
  • ✅ "Sign in to Koder"
  • ❌ "Sign In To Koder"

Exception: company/product names retain Title Case ("Koder Design", "Koder Talk", "Koder Flow").

R4 — Punctuation

  • Period at end of complete sentences in body text
  • No period at end of short titles / buttons / labels / list items
  • Em dash with thin spaces: " — " (HTML entity )
  • Curly quotes for prose: " '
  • Straight quotes for code only
  • Oxford comma: yes (en-US convention; pt-BR follows standard pt comma rules)

R5 — Numbers

  • Cardinal numerals < 10: spell out in prose ("nine items", "ten items")
  • ≥ 10: numerals ("10 items", "1,234 items")
  • Counts in UI labels: always numerals ("3 unread")
  • Time: 24h format in en-US ("14:32"); pt-BR same
  • Dates: ISO 8601 in technical contexts; localized friendly form in UI

R6 — Internationalization

  • Strings in code: en-US source of truth
  • Translations: human-translated for pt-BR; machine fallback marked visibly until human pass
  • ICU keys per i18n/contract.kmd R6
  • Plurals: ICU {count, plural, one {1 file} other {# files}}
  • Variables in translations: {name}, never positional %s

R7 — Accessibility

  • Visible text MUST also be readable by screen readers (no CSS-tricks where the visible label differs from the semantic one)
  • Icon-only buttons MUST have aria-label
  • Error text MUST be associated with the failing field (aria-describedby)
  • Section titles MUST use semantic headings (<h2>-<h6>, never styled divs)

R8 — Examples

Button label

Why
"OK""Save"Verb-specific
"Click here to save""Save"Concise + neutral
"SUBMIT""Send message"Sentence case + specific verb

Error

"Invalid input""Email must include @ — try name@example.com"
"Wrong password""We couldn't sign you in. Check your password or reset it."
"Server error""Couldn't save right now. Your changes are safe locally — we'll retry automatically."

Empty state

"No data""No projects yet — create your first to get started."
"Empty""You have no scheduled messages. Schedule one from any conversation."

R9 — Microcopy patterns by element (auditable)

Each user-facing element type has a prescriptive pattern with a checkable criterion. k-test-gen (content pass) and content review audit strings against these; a string that fails its criterion is a defect, not a style preference. This is what turns R8's examples into enforceable rules — the gov.br writing repo is prescriptive here and KDS matches it. Every pattern holds in en-US (source) and pt-BR (human translation, +30% length budget per R1).

ElementPattern (rule)Auditable check✅ en-US / pt-BR
Button / actionInfinitive/imperative verb + object (the result) — never a bare "OK"/"Yes"first token is a verb; anchor ∉ {OK, Yes, No, Submit, "Click here"}"Save document" / "Salvar documento"
Destructive buttonVerb names the exact consequence + countverb ∈ {Delete, Remove, Discard, …} and object/count present"Delete 3 files" / "Excluir 3 arquivos"
Errorwhat-went-wrong + how-to-fix; never blames the user; no humor, no ALL-CAPS, no "error/invalid/wrong" in the bodyhas a corrective clause; no R2 forbidden word; no CAPS run ≥3; the user is not named as the fault"We couldn't reach the server. Check your connection and try again." / "Não foi possível conectar ao servidor. Verifique sua conexão e tente de novo."
Link textDescribes the destination — never "click here"/"here"/"read more" as the whole anchoranchor text ∉ {click here, here, read more, this link, link}; ≥1 descriptive noun"Read the privacy policy" / "Leia a política de privacidade"
Field labelNoun phrase, sentence case (only the first letter capital), placed above the field; no trailing colonfirst-letter-only capitalization (R3); no : suffix; positioned above the input"Display name" / "Nome de exibição"
Empty stateContext (why it's empty) + the next action (a CTA)≥1 sentence of context and ≥1 actionable CTA verb"No projects yet — create your first to get started." / "Nenhum projeto ainda — crie o primeiro para começar."
Alt text1–2 sentences describing the image's meaning — not "image of…"length 1–2 sentences; does not start with "image/picture of"; conveys meaning"A doctor reviewing a chest X-ray on a tablet." / "Um médico analisando um raio-X de tórax no tablet."
SuccessBrief + specific (what, when) — never a bare "Success!"/"Done!"states the object/result; anchor ∉ {Success, Done, OK}"Saved at 14:32." / "Salvo às 14:32."
PlaceholderAn example of the format, not the label repeated; never the only labeldiffers from the field label; shows a format example; a real <label> still exists"name@example.com"

The criteria in the third column are the checklist for k-test-gen's content pass and the content sub-score of the UQS (design/ui-quality-score.kmd). A surface whose strings pass R1–R8 but violate an R9 criterion is not content-conformant.

  • errors/user-facing-messages.kmd — error message format (R9 Error row defers here)
  • policies/language.kmd — pt-BR / en-US per surface
  • i18n/contract.kmd — translation pipeline
  • design/ui-quality-score.kmd — R9 feeds the UQS content sub-score

Referências