| name | ux-writing |
| description | Write or review UX copy — microcopy, error messages, empty states, CTAs, onboarding flows. Use when writing interface text, button labels, or any user-facing copy. Integrates with UX Copy MCP for langpack search and AI generation when available. |
| argument-hint | <context, copy to review, or component name> |
| allowed-tools | ["mcp__claude_ai_Figma__get_design_context","mcp__claude_ai_Figma__get_screenshot","mcp__ux-copy__match_copy","mcp__ux-copy__generate_copy","mcp__ux-copy__process_screen","mcp__ux-copy__process_screens","Read","Bash(grep *)"] |
| paths | ["**/locales/**","**/i18n/**","**/translations/**","**/strings/**","**/copy/**","**/content/**","**/*.json"] |
UX Writing
Write clear, concise, and helpful interface copy.
When Triggered
Activate when the user says "ux copy", "write copy for", "error message", "empty state", "what should this button say", "microcopy", "CTA copy", "onboarding copy", or asks for help with any interface text.
Usage
/ux-writing $ARGUMENTS
Write or review UX copy for: @$1
What to Ask (if missing)
- Context: What screen, flow, or feature?
- User state: What is the user trying to do? How are they feeling?
- Tone: Formal, friendly, playful, reassuring?
- Constraints: Character limits, platform guidelines?
- Brand voice (optional): Any existing style guide?
Writing Principles
- Clear: Say exactly what you mean. No jargon, no ambiguity.
- Concise: Use the fewest words that convey the full meaning.
- Consistent: Same terms for the same things everywhere.
- Useful: Every word should help the user accomplish their goal.
- Human: Write like a helpful person, not a robot.
Copy Patterns
CTAs
- Start with a verb: "Start free trial", "Save changes", "Download report"
- Be specific: "Create account" not "Submit"
- Match the outcome to the label
Error Messages
Structure: What happened + Why + How to fix
- "Payment declined. Your card was declined by your bank. Try a different card or contact your bank."
Empty States
Structure: What this is + Why it's empty + How to start
- "No projects yet. Create your first project to start collaborating with your team."
Confirmation Dialogs
- Make the action clear: "Delete 3 files?" not "Are you sure?"
- Describe consequences: "This can't be undone"
- Label buttons with the action: "Delete files" / "Keep files" not "OK" / "Cancel"
Tooltips
- Concise, helpful, never obvious
- Answer "what does this do?" not "what is this?"
Loading States
- Set expectations: "Generating report... This usually takes about 30 seconds"
- Reduce anxiety with progress indicators
Voice and Tone Adaptation
Adapt tone to context:
- Success: Celebratory but not over the top
- Error: Empathetic and helpful
- Warning: Clear and actionable
- Neutral: Informative and concise
Output Format
## UX Copy: [Context]
### Recommended Copy
**[Element]**: [Copy]
### Alternatives
| Option | Copy | Tone | Best For |
|--------|------|------|----------|
| A | [Copy] | [Tone] | [When to use] |
| B | [Copy] | [Tone] | [When to use] |
| C | [Copy] | [Tone] | [When to use] |
### Rationale
[Why this copy works — user context, clarity, action-orientation]
### Localization Notes
[Anything translators should know — idioms to avoid, character expansion, cultural context]
Frontitude CSV Export
After bulk processing (process_screen / process_screens), generate a downloadable CSV file in Frontitude import format.
CSV Columns
Name,Unique key,Context,Value,Value (English - en),Value (Thai - th),Status,Tags,Copy guidelines,Updated at,Last Edited By,Frontitude link
Column Mapping
| Column | Rule | Example (reference) |
|---|
| Name | {platform} / {category} / {actual EN copy text or short name}: / {sequence} {Type}_{device} — ALWAYS use the platform prefix for ALL rows (including reuse). Never use matched paths from other hierarchies (e.g. "Admin Portal / ...") or raw i18n keys as Name | Krungthai Business / 03 Title / Rate Confirmation Summary: / 999 Title_web |
| Unique key | Same as Name | (same) |
| Context | If the copy has an existing langpack i18n key (from match_copy), put that key here so developers can link it. Otherwise leave empty | international_transfer_common_beneficiary_state_label |
| Value | The raw display value — may differ from EN copy (e.g. include sequence numbers or formatting) | Rate Confirmation Summary 999 |
| Value (English - en) | EN copy as it appears in the UI | Rate Confirmation Summary: |
| Value (Thai - th) | TH copy as it appears in the UI | สรุปการยืนยันอัตราแลกเปลี่ยน: |
| Status | Draft | Draft |
| Tags | Ask user for sprint/milestone tags. Format: {feature tag}, {sprint tag}, {platform}_{device} | OR Multiple Approve, KB_MVP5_D2, Krungthai Business_Mobile |
| Copy guidelines | Short note only (1-3 words). Not a rule explanation. | Test bug fixed |
| Updated at | Leave empty (Frontitude fills this) | (empty) |
| Last Edited By | Leave empty (Frontitude fills this) | (empty) |
| Frontitude link | Leave empty (Frontitude fills this) | (empty) |
Important Column Rules
- Name / Unique key: Use the actual EN copy text (or short descriptive name) as the copy name — NOT generic labels like "Page Title" or "Upload CTA Button"
- Value vs EN copy: Value is the raw default value which may include sequence numbers or differ from the display copy
- Context: Fill with the langpack i18n key when the copy is a reuse (see rule 5). Otherwise leave empty.
- Tags: Always ask the user for sprint/milestone tags (e.g.
KB_MVP5_D2). Do NOT invent tags like "AI Generated".
- Copy guidelines: Keep extremely short (1-3 words). Examples:
Bug fixed, New copy, Revised. Do NOT write rule explanations.
- Reuse vs New copy rule: if the copy has an existing langpack i18n key (found via
match_copy) → Reuse (and put the key in Context). If no langpack key exists → New copy. Frontitude-only matches (hash keys like frontitude-xxxx or "Admin Portal" paths, which usually have no TH) do NOT count as reuse.
- Updated at: Leave empty. Frontitude manages timestamps.
- Sequence number: Ask user for the starting sequence number, or use
999 as placeholder.
Category Mapping (for Name column)
| Placement | Category Code |
|---|
| Title | 03 Title — full format: {platform} / 03 Title / {copy} / 01 Title_{device} (e.g. copy "Account Details" → Krungthai Business / 03 Title / Account Details / 01 Title_web) |
| Button / CTA | 04 Action — full format: {platform} / 04 Action / {service} / {Copy}_{device} where {service} is the feature/service name and {Copy} is the button copy in Title Case (e.g. copy "Submit" in International Transfer Profile → Krungthai Business / 04 Action / International Transfer Profile / Submit_web) |
| Toast / Snackbar / Info banner | 08 Informing — full format: {platform} / 08 Informing / {short name} / 02 Message_{device} |
| Popup / Dialog | 07 Popup — title: {platform} / 07 Popup / {title copy} / 01 Title_{device}, body: {platform} / 07 Popup / {title copy} / 02 Message_{device}, sub-element (e.g. error code line): {platform} / 07 Popup / {title copy} / {copy} / 04 Subtext_{device}. Popup buttons use 04 Action |
| Empty State | 06 Empty State — title: {platform} / 06 Empty State / {copy} / 01 Header_{device}, description: {platform} / 06 Empty State / {copy} / 02 Message_{device} (e.g. Krungthai Business / 06 Empty State / Unable to view data / 01 Header_web) |
| Inline Error | 09 Error — full format: {platform} / 09 Error / {copy, replace "/" with "-"} / 02 Message_{device} (e.g. copy "Exceeded maximum characters for Address/District/Province/Postal Code" → Krungthai Business / 09 Error / Exceeded maximum characters for Address-District-Province-Postal Code / 02 Message_web) |
| Tooltip | 10 Tooltip — full format: {platform} / 10 Tooltip / {copy} / 01 Message_{device} |
| Label | 05 Form — full format: {platform} / 05 Form / {copy} / 01 Label_{device} (e.g. Krungthai Business / 05 Form / City / 01 Label_web) |
| Helper / Hint (subtext under a field or form) | 05 Form — full format: {platform} / 05 Form / {copy} / 04 Subtext_{device} (e.g. Krungthai Business / 05 Form / e.g. House number-Building-Street / 04 Subtext_web) |
| Placeholder | 05 Form — any copy starting with "Enter" (or dropdown "Select ...") is a placeholder. Full format: {platform} / 05 Form / {field name} / 02 Placeholder_{device} where {field name} is the copy without the verb prefix (e.g. copy "Enter City" → Krungthai Business / 05 Form / City / 02 Placeholder_web) |
| Push Notification | 11 Notification |
| Tab (navigation tab) | 01 Navigation — full format: {platform} / 01 Navigation / Tab / 01 {Copy}_{device} (e.g. copy "Drafts" → Krungthai Business / 01 Navigation / Tab / 01 Drafts_web) |
| Table header | 05 Form — same as Label: {platform} / 05 Form / {copy} / 01 Label_{device} (e.g. copy "Package Name" → Krungthai Business / 05 Form / Package Name / 01 Label_web) |
| Status value (e.g. Upload Success, Pending Validation) | 05 Form — nested under Status: {platform} / 05 Form / Status / {copy} / 01 Label_{device} (e.g. copy "Upload Success" → Krungthai Business / 05 Form / Status / Upload Success / 01 Label_web) |
| Field value (dropdown option, radio option, type name) | 05 Form — nested under its field: {platform} / 05 Form / {field} / {value copy} / 01 Label_{device} (e.g. value "Forward Rate" of FX Rate Type → Krungthai Business / 05 Form / FX Rate Type / Forward Rate / 01 Label_web) |
Copy Content Rules (learned conventions)
- Template variables: convert dynamic content to
{{x}}, {{y}}, {{date}}, {{datetime}} (e.g. "Total: 3 transactions" → "Total: {{x}} transactions"). Match variable style of existing langpack keys when reusing.
- Not copy — exclude: sample data (names, amounts, dates, reference numbers), master data from backend (currency names, income type lists per Revenue Dept, reject reasons), FX rates.
- Keep EN in TH column for: service/type names (e.g. "Bulk Open Package", "SWIFT (Auto)"), report names, FX rate type values (Counter/Forward/Pro Rata). Flag for team confirmation.
- Frontitude-only match that has TH: still
New copy per reuse rule, but copy the existing TH wording for consistency (note it in the report).
- Long copy Name: if the copy is a long sentence, use a short descriptive name in the Name column (e.g. "Public holiday disclaimer") instead of the full copy.
- CSV escaping: fields containing commas must be quoted (use a proper CSV writer, not string join).
Master File Workflow (multi-feature)
- Maintain ONE cumulative master CSV across features; every update saves as the NEXT running version (v11, v12, ...) — never overwrite earlier versions.
- Each feature/Figma file gets its own feature tag (ask the user, e.g.
Bulk_OR).
- Duplicate copy within the same feature → skip (do not re-add).
- Duplicate copy across features → do NOT add a new row; append the new feature tag to the existing row's Tags (e.g.
OR Hybrid Address, Bulk_OR) so Frontitude updates tags on import.
- When a previously "New copy" row is later found to have a langpack key, patch that row: add key to Context, change note to
Reuse.
Before Generating CSV — Ask User
Before producing CSV output, ask for these if not provided:
- Platform name: e.g.
Krungthai Business, Bank NEXT
- Device:
web or mobile (used in ID suffix like Title_web)
- Tags: Sprint/milestone tags (e.g.
KB_MVP5_D2, Krungthai Business_Mobile)
- Starting sequence number: or use
999 as placeholder
- Copy guidelines note: short note for this batch (e.g.
New feature, Bug fixed)
Output Instructions
After generating copy results, always produce the Frontitude CSV output:
Claude Code (CLI / Desktop IDE):
- Compile all fields into Frontitude CSV format
- Write the file to the working directory:
frontitude-export-{screen_name}-{date}.csv
- Encoding: Always save the CSV as UTF-8 with BOM (prepend
\xEF\xBB\xBF) — without BOM, Excel misreads Thai text as garbled characters. Verify with head -c 3 file.csv | xxd (should show efbb bf).
- Present the file path to the user
Claude Desktop / Claude Web (no filesystem access):
- Compile all fields into Frontitude CSV format
- Output the complete CSV as a code block so the user can copy it
- Tell the user to save as
.csv file for Frontitude import
Always produce the CSV regardless of platform — the delivery method changes, the content does not.
Workflow
Follow this workflow to ensure consistency before creating new copy.
Important: At the start of every session, check if UX Copy MCP tools are available by attempting to use match_copy. If it responds, use the MCP workflow. If not available, use the fallback workflow. Always state which mode you are using.
Step 1: Search existing copy
With UX Copy MCP (try this first):
- Call
match_copy with the text you need to write
- Check if the same or similar copy already exists in langpack/Frontitude
- Reuse existing i18n keys when possible
- Note the tone and terminology patterns in existing copy
Without MCP (fallback):
- Grep locale/i18n files for similar terms and keys
- Check existing JSON files for patterns
Step 2: Generate or write copy
With UX Copy MCP:
- Call
generate_copy with context parameters:
placement: button, toast, title, subtitle, description, error, empty-state, tooltip, modal, push-notification
intent: inform, confirm, warn, error, success, onboard, upsell
tone: formal, friendly, playful, reassuring (match brand voice)
max_length: character limit if constrained by design
- The MCP returns 3 options with TH/EN text and tone labels
Without MCP (fallback):
- Write copy manually following the Writing Principles and Copy Patterns above
- Provide 3 alternatives with tone labels
Step 3: Batch processing (full screens)
With UX Copy MCP:
- Single screen: call
process_screen with all fields at once
- Multi-screen flow: call
process_screens which adds consistency checks:
- Tone divergence detection
- CTA verb sprawl (too many different verbs for similar actions)
- Terminology consistency across the flow
Without MCP (fallback):
- Review each field manually and cross-reference with existing copy patterns
- Manually check consistency across screens
Step 4: Export Frontitude CSV
Always produce CSV output after bulk processing (see Frontitude CSV Export section above).
Figma Integration
If a Figma URL is provided, use the Figma MCP to view the screen context and understand the full user flow. Check character limits and layout constraints from the design.
When both Figma MCP and UX Copy MCP are available:
- Pull the screen design from Figma → identify all text fields
- Feed those fields into
process_screen → get existing matches + generated alternatives
- Return results with Frontitude CSV format for direct import
UX Copy MCP Integration
The UX Copy MCP server (ux-copy) provides four tools:
| Tool | When to Use |
|---|
match_copy | Search existing langpack/Frontitude copy before writing new |
generate_copy | AI-generate copy with placement, intent, and tone context |
process_screen | Batch all fields on a single screen (search + generate) |
process_screens | Batch multiple screens with cross-screen consistency checks |
When MCP is available
- Always search first: Call
match_copy before generating new copy to avoid duplicates
- Use batch tools for screens:
process_screen is more efficient than individual calls
- Leverage consistency checks:
process_screens catches tone drift and CTA sprawl automatically
- Output includes Frontitude format: Results are ready for direct import into the copy management system
When MCP is NOT available (fallback)
The skill works fully without the MCP — use these local alternatives:
| MCP Tool | Local Fallback |
|---|
match_copy | Grep locale/i18n JSON files for similar terms and keys |
generate_copy | Write manually following Writing Principles + Copy Patterns |
process_screen | Read component file → identify text fields → write copy for each |
process_screens | Manually audit copy across screen files for consistency |
Brand Voice Integration
This skill provides the universal workflow for writing UX copy. For platform-specific rules (capitalization, punctuation, component formats), pair with the brand-voice skill.
| Skill | Responsibility |
|---|
| ux-writing | HOW to write — principles, workflow, MCP integration, search/generate |
| brand-voice | WHAT rules to follow — capitalization, punctuation, component patterns, vocab |
When both skills are active:
- ux-writing handles search → generate → batch workflow
- brand-voice validates output against platform guideline rules
- Final copy passes both universal quality AND brand compliance
Local Code Integration (Claude Code)
When working with local code in Claude Code:
- Read existing copy from locale/i18n files to maintain consistency with current terminology
- Grep for existing copy patterns to ensure the new copy matches the product's voice
- Read component files to understand the UI context where copy will appear
- Check for hardcoded strings that should be extracted to locale files
- When writing copy, output in the project's i18n format if one exists (JSON, YAML, etc.)
- If UX Copy MCP is connected, cross-reference local i18n keys with the centralized langpack to detect drift