Skip to content

Latest commit

 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

UX writing

Turn a UI component or flow into interface copy that reads human, or strip AI-generated tells out of copy you already have - a Claude Code skill built for design teams and the engineers who ship their work.

CI GitHub stars Last commit License Claude Code

ux-writing takes a UI component or flow - a button, an error message, an empty state, a confirmation dialog - and returns copy ready to ship: a verb-led label, a plain-language error, a consequence-named confirmation. Given copy that already exists, it switches to strip mode and rewrites it line by line, naming the pattern each fix removed and the element rule the line failed. A pre-ship self-check keeps either mode from drifting into generic filler: nine yes/no questions run against every draft, the last of them the pattern table itself, and one "yes" sends the line back for a rewrite.

Table of contents

What it does

  • Writes microcopy for buttons, errors, empty states, confirmations, placeholders, tooltips, and toasts - one line per element, shippable as it stands or carrying a marked slot for a product fact the request never gave.
  • Rewrites existing copy that reads AI-generated, in strip mode, line by line, with Before/After pairs, naming the pattern each fix removed and the element rule the line failed.
  • Runs every draft through a nine-question pre-ship self-check, ending with the full 13-row pattern table; a failed check forces a rewrite, not a footnote.
  • Names the outcome in every button label instead of a bare verb like "Submit" or "OK".
  • Writes error copy as what happened plus how to fix it, with no blame and no bare apology.
  • Defers to a provided brand voice guide wherever its rules conflict with the defaults here.

Quick start

Personal - available in every project:

git clone https://github.com/humbleteam/ux-writing ~/.claude/skills/ux-writing

Project-scoped - checked into one repo:

git clone https://github.com/humbleteam/ux-writing .claude/skills/ux-writing

Any other agent: the skill is plain markdown in the Agent Skills format. Paste SKILL.md into the system prompt of Cursor, Codex, or any other LLM agent - no Claude-specific runtime required.

To verify: restart Claude Code, then ask it to list its skills. Skills load automatically from ~/.claude/skills/ (personal) and .claude/skills/ (project); ux-writing should appear.

Usage

  • "Write the button, confirmation, and error copy for this delete-project flow." - write mode: breaks the flow into its elements and writes each to its type's rule.
  • "This error message sounds like an AI wrote it: 'Oops! Something went wrong. Please try again later.'" - strip mode: rewrites the line and names the patterns removed.
  • "Draft the empty-state copy for a project list with zero projects." - write mode against a single element: what's missing, plus the one action that fills it.

Example output

Example write-mode output for a fictional delete-project flow. The request named the flow and nothing else - no data model, no failure modes:

## Delete project flow

**Button (destructive):** Delete project

**Confirmation dialog:** Delete "<project name>"? This removes the project and
all <n> <items> inside it. This can't be undone.

**Buttons:** Cancel / Delete project

**Error (delete failed):** The project didn't delete because <cause>. <Fix for
that cause>.

**Toast (success):** Project deleted.

**Needs:** Confirmation - what does a project hold, and how many of them go
with it; Error - what actually blocks a delete in this product.

The button and the toast are finished copy: their rules need nothing the request did not carry. The confirmation and the error are drafts, because a named consequence and a stated cause are facts about the product, and the slots mark the two the request never gave. Answering them - a project holds tasks, this one holds 14, and a delete is blocked while members are still assigned, 2 on this project - fills the slots, drops the Needs: line, and re-emits the block:

**Confirmation dialog:** Delete "Q3 Roadmap"? This removes the project and
all 14 tasks inside it. This can't be undone.

**Error (delete failed):** The project didn't delete because it still has
2 members assigned. Remove the members first, then try again.

Example strip-mode output, for the error message in Usage above. The user supplied the cause along with the string: this is the error shown when an upload exceeds the 25 MB limit.

### Error message

- Before: "Oops! Something went wrong. Please try again later."
- After: "The file didn't upload because it's over the 25 MB limit. Try a
  smaller file."
- Removed: apology with no fact

Without that stated limit the rewrite has no cause to name, and naming the cause is the fix for this row. The line comes back with a slot where the cause belongs and the question under it, the same shape write mode uses:

### Error message

- Before: "Oops! Something went wrong. Please try again later."
- After: "The file didn't upload because <cause>. <Fix for that cause>."
- Removed: apology with no fact
- Needs: what actually blocks an upload in this product

The rule is the same one that governs numbers: what the input does not say, the output does not say. Eleven of the 13 patterns are fixed by cutting something, so no slot can arise on them - only the two fixed by adding a fact, an apology with no fact and an unnamed consequence, can produce one.

The pattern table is one of two checks, though, and a slot can also come from the other. Strip mode reads the copy against its element's own rule as well, and a line can carry no tell at all and still fail the job its type has. A bare empty state is the clearest case: it has no adjective, no dash, no Title Case and no closer, so every one of the 13 patterns comes back clean, while the empty-state rule names that exact wording as the failure.

### Empty state

- Before: "Nothing here yet."
- After: "No <items> yet. <Action that creates the first one>."
- Rule: an empty state names what belongs in the space and the one action that
  fills it
- Needs: what belongs in this list, and what action puts the first one there

There is no Removed: line on that block, because nothing came out - the fix puts something in, and the Rule: line is what reports it. A block that ships a rewrite carries at least one of the two lines, and a block that carries neither is the skip branch: the copy reads fine on both checks.

That second check reads one rule, chosen by the element type, and the type is not always in the request. A string handed over on its own gets a type inferred from its shape, and for some strings the shape does not decide it:

### Element type unclear

- Before: "Save changes"
- Type: button, or the tooltip on a Save changes button? As a button this passes
  - verb first, outcome named, sentence case. As the tooltip on that button it
  restates its own label, which the tooltip rule forbids.

There is no After: and no Rule: line, because no rule failure has been established: the table came back clean, and the other check is waiting on which element this is. Shipping the button verdict would call the copy fine when it may be the tooltip case this skill already names as a failure, and shipping the tooltip verdict would rewrite a button label that was correct.

How it works

  • Mode detection first. A component or flow with no existing copy triggers write mode; a string of copy, or a request to fix copy that "sounds AI", triggers strip mode.
  • The pattern table loads before either mode runs. references/banned-patterns.md holds 13 AI-tell patterns, each with a bad and fixed example, checked before a draft is done.
  • The table is read against the copy going out, not only the copy coming in. Strip mode's first check reads it against the original; the pre-ship gate reads it against whatever this skill wrote - a written draft, or the rewrite that replaces a stripped line. The gate's first eight questions reach eight of the 13 rows, and the ninth reads the other five, so a draft cannot ship carrying a row this skill's own table names.
  • A rewrite names what it fixed, on one of two lines. Strip mode runs two checks: the pattern table, reported on Removed:, and the element's own rule, reported on Rule:. A block that ships a rewrite carries at least one of them. Both checks clean means the copy reads fine and the rewrite is skipped, not invented - a clean table pass alone does not, which is how a bare "Nothing here yet" used to come back as good copy.
  • The second check is only as good as the element type it runs against. Where the request states the type, it runs. Where the type was inferred from the shape of the string, the second plausible type's rule is read too: a bare "Save changes" passes the button rule and fails the tooltip rule, so the block asks which element it is instead of shipping either verdict as a finding about the copy.
  • Buttons name the outcome. Verb first, then the object, sentence case - never a bare "Submit" or "OK" when a concrete outcome exists to name.
  • Errors state cause, then fix. What happened, then how to fix it - no blame language, no meaningless apology.
  • Empty states name what's missing and the one action that fills it. Never a bare "Nothing here yet" with no next step.
  • Destructive confirmations name the consequence. What gets removed, how much, whether it's reversible - never a generic "Are you sure?"
  • Placeholder text is never a label's substitute. If removing it would leave a field unlabeled, that's a bug to flag, not a copy choice.
  • A fact the request never gave becomes a slot, not a guess. Both modes mark the missing count or cause where it belongs and ask for it on a Needs: line: write mode when an element's rule needs a fact the request skipped, strip mode when the pattern that applied is one of the two fixed by adding a fact rather than cutting one, or when the element rule the line failed is. Elements whose rules need no product fact still ship as finished copy in the same block.
  • The nine-question self-check runs before every output ships. One "yes" sends the line back for a rewrite.
  • Legal or compliance-reviewed copy gets flagged, not silently rewritten. A meaning-changing edit needs a human sign-off first.

How is this different from just asking the model?

A bare "write me a button label" prompt tends to return exactly the copy this skill exists to catch: "Submit", or an error that opens with "Oops! Something went wrong" and gives no next step. It also drifts toward promotional filler - "seamlessly", "effortlessly" - words that sound confident but tell the user nothing to do. This skill pins the shape down per element type (button = verb plus outcome, error = cause plus fix, confirmation = named consequence) and runs a fixed nine-question check before the draft ships, so the output holds shape run to run. It does not know your product's actual voice - a provided brand voice guide wins where its rules conflict with the defaults here.

FAQ

How do I make AI writing sound human? Cut patterns that read as generated before the copy ships: promotional adjectives (seamless, effortless), negative parallelism ("it's not just X - it's Y"), filler openers ("in order to"), and copula avoidance ("serves as" instead of "is"). This skill checks every draft against nine questions, the last of which is the full table; references/banned-patterns.md has it.

What are AI writing tells? Patterns that show up disproportionately in generated text: promotional adjectives, negative parallelism, vague attributions, forced rule-of-three lists, em dashes (one is enough to read as generated, which is why the check here fires at one, not at two), generic upbeat closers. In interface copy they also show up as Title Case buttons, "Oops!" errors with no fact, and "Are you sure?" dialogs that never say what happens if you are. references/banned-patterns.md lists all 13 with a before/after example each.

How do I write good error messages? State what happened, then how to fix it. "The project didn't delete because it still has 2 members assigned. Remove the members first, then try again" beats "Oops! Something went wrong" - it gives a fact and an action, not an apology. Never blame the user.

Should button labels be Title Case? No - sentence case. "Save changes", not "Save Changes". Title Case reads as a menu item or a headline, not an action. Sentence case is Material Design's default; Apple's Human Interface Guidelines leave the choice between title-style and sentence-style to each app, favoring title-style for navigation titles.

What is UX writing? The words in a product's interface - buttons, errors, empty states, tooltips, confirmations - chosen deliberately, not left as engineering placeholders. This skill works at the sentence level: what makes one label or error clear, and what makes copy read as generated instead of written for its screen.

Can Claude write microcopy for my app? Yes, with real product context. Describe the component or flow - what it does, what happens if it fails, what a destructive action removes - and this skill returns a label, error, or confirmation. Less context produces a more generic draft, same as for a person.

Related skills

Part of a 10-skill open-source kit for design teams by Humbleteam.

  • design-review - structured UX critique with a 0-4 score, Before/After/Why fixes, and a citation for every claim.
  • ascii-wireframes - three distinct layout hypotheses as ASCII wireframes before any hi-fi work.
  • html-mockup - census-first HTML mockups that match a reference screenshot: exact palette, item counts, component states.
  • extract-design-tokens - pull palette, type, spacing, radii, and shadows from a URL or screenshot into CSS variables and JSON.
  • audit-design-tokens - find token drift in a codebase: raw hex values, off-scale spacing, near-duplicate colors.
  • design-qa - a pre-ship design QA gate: states, contrast, touch targets, breakpoints, keyboard paths.
  • design-handoff - turn a finished mockup into a dev-ready spec: tokens, states, accessibility annotations, open questions.
  • accessibility-audit - WCAG 2.2-grounded accessibility review with success-criterion citations and severity levels.
  • design-brief - extract a 5-bullet design brief from messy project inputs, with a gap report for what is missing.

Who maintains this

Humbleteam is a digital product design and AI-engineering studio: founded in 2017, working from Prague and Dubai, with 80+ digital awards to the name, including 14 Awwwards wins, a Webby, and a Red Dot. We design digital products for startups and enterprises in fintech, healthtech, sports, and AI, and we build AI infrastructure for design teams - agents, workflows, and skills like this one.

This skill is distilled from the internal playbooks we run on client work: the same checklists behind the case studies at humbleteam.com/work, for clients like Tinder and Acronis.

Issues and PRs welcome.

MIT - see LICENSE.

About

Claude Code skill: interface microcopy rules plus an AI-tell strip pass for buttons and errors

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors