content-composer
Skill4 starsChanged 6 months agoMITView on GitHub
Use whenever writing or editing user-facing copy in the app — button labels, headers, section titles, card text, toasts, empty states, onboarding, paywalls, notifications, App Store metadata, release notes. Covers the voice, tone, and mechanics of mobile UI microcopy: length caps per element, banned characters (em-dash never, ellipsis rarely), banned AI phrases (unlock, discover, embark, dive in), active-verb CTAs, sentence case vs Title Case, and copy-review checklist. Trigger on any word change to a SwiftUI `Text`, `Label`, `Button` label, navigation title, or any string literal that will be displayed to a user.
In this repository76 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .cursor/rules/components.mdcCursor rule
- .cursor/rules/currency.mdcCursor rule
- .cursor/rules/design-system.mdcCursor rule
- .cursor/rules/file-structure.mdcCursor rule
- .cursor/rules/forbidden-patterns.mdcCursor rule
- .cursor/rules/mvvm-architecture.mdcCursor rule
- .cursor/rules/pricing-display.mdcCursor rule
- .cursor/rules/scope.mdcCursor rule
- .cursor/rules/swift-conventions.mdcCursor rule
- accessibilitySkill
- animationsSkill
- apple-developer-authSkill
- apple-signinSkill
- app-store-preflightSkill
- asc-app-create-uiSkill
- asc-aso-auditSkill
- asc-build-lifecycleSkill
- asc-cli-usageSkill
- asc-crash-triageSkill
- asc-id-resolverSkill
- asc-localize-metadataSkill
- asc-metadata-syncSkill
- asc-metadata-yamlSkill
- asc-notarizationSkill
- asc-ppp-pricingSkill
- asc-release-flowSkill
- asc-revenuecat-catalog-syncSkill
- asc-screenshot-resizeSkill
- asc-shots-pipelineSkill
- asc-signing-setupSkill
- ascSkill
- asc-submission-healthSkill
- asc-subscription-localizationSkill
- asc-testflight-orchestrationSkill
- asc-wall-submitSkill
- asc-whats-new-writerSkill
- asc-workflowSkill
- asc-xcode-buildSkill
- billing-finance-calculatorSkill
- buildSkill
- business-modelSkill
- chartsSkill
- consolidate-errorsSkill
- crashSkill
- debuggingSkill
- feature-directory-structureSkill
- feedback-statesSkill
- fix-errorSkill
- formsSkill
- foundation-modelsSkill
- hapticsSkill
- healthkitSkill
- layoutSkill
- listsSkill
- navigationSkill
- notificationsSkill
- onboarding-questionnaireSkill
- onboardingSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 3 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 8
Every user-facing string in this app must pass this skill's rules.
- Very specific: line 17
Ellipsis: use a real … (U+2026), not three dots ....
- Very specific: line 52
Eyebrows / kickers / section labels: ALL CAPS with kerning (kerning(1.2)).
- Distinctive: line 88
Always start with a verb.
- Distinctive: line 106
Run this on every new string before committing:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Content Composer — Mobile UI Copy Rules
Every user-facing string in this app must pass this skill's rules. The point is simple, human, skimmable copy that fits a small screen and doesn't sound like a marketing template or an LLM.
The One Rule About Characters
No em-dashes (—). Ever.
LLMs love em-dashes. Humans typing on phones do not. Every — in UI copy is a tell that the text came from an AI and wasn't revised. Replace with a period, comma, or split into two sentences. The same applies to en-dashes in prose (–); keep en-dashes only for numeric ranges (3–5 min).
Other character rules:
- Ellipsis: use a real
…(U+2026), not three dots.... Use only for "in progress" indicators (Loading…). Never in button labels or headers. - Smart quotes (
""'') for prose inside cards, never in code identifiers. - Middle dot (
·) is allowed as a separator (Level 17 · Run 3). Keep it short. - No emoji in body copy. Icons belong in
Image(systemName:), not inline text.
Voice
- Second person. "You're on fire." not "The user is on fire."
- Active verbs in every CTA. "Continue drills", "Start speaking", "Retry run". Never "Continuation of drills".
- Present tense by default. "You practiced today." not "You have practiced today."
- Concrete over abstract. "3 runs to Level 18" beats "More progress awaits".
- No marketing dust. Ban: "unlock", "discover", "embark", "dive in", "journey awaits", "unleash". These are AI filler.
- No hedging. Ban: "Feel free to…", "You can now…", anything ending in "!" more than once per screen.
- No clichés. "A thousand-mile journey…", "Rome wasn't built in a day…" — kill on sight.
Length Caps (Mobile)
| Element | Max length | Example |
| Primary CTA button | 3 words, ≤ 20 chars | Continue drills |
| Secondary button | 2 words, ≤ 14 chars | Try again |
| Card title / headline | 5 words, ≤ 38 chars | Enterprise tier pre-brief |
| Card eyebrow (ALL CAPS kicker) | 3 words, ≤ 20 chars | NEXT CHALLENGE |
| Card subtitle | 1 sentence, ≤ 64 chars | 2 of 3 drills passed |
| Section header | 2–3 words | Recent activity |
| Inline toast / snackbar | 1 short sentence, ≤ 60 chars | Saved to your library |
| Empty state title | ≤ 6 words | No sessions yet |
| Empty state body | 1–2 short sentences, ≤ 120 chars | Finish a challenge to see it here. |
| Navigation title | 1–3 words | Your Journey |
The test: can a skimmer understand the screen in under 2 seconds? If the eye lingers, copy is too long.
Casing
- Buttons, titles, headings: sentence case. "Continue drills", not "Continue Drills".
- Eyebrows / kickers / section labels: ALL CAPS with kerning (
kerning(1.2)). "NEXT CHALLENGE". - Run/level names retain the product-specific capitalization from the data ("Enterprise tier pre-brief").
- Product names stay exact. For example: "RevenueCat", "App Store Connect", and the app's configured product name.
Banned Phrase Cheat Sheet
| Ban | Why | Replace with |
| "Unlock your potential" | Marketing filler | Start the next run |
| "Embark on your journey" | Cliché | Open the roadmap |
| "Discover…", "Explore…" alone | Vague verbs | Concrete verb: See your progress |
| "Feel free to…" | Hedging | Direct imperative |
| "Seamlessly", "intuitively" | Marketing adjectives | Delete the word |
| "—" | AI tell | ., ,, or split |
"Loading..." (three dots) | Typography | "Loading…" (real ellipsis) |
| "Oops!", "Uh oh!" | Fake casual | Describe the problem directly |
| "We're sorry but…" | Apologetic fluff | Say what to do |
Error Messages — Microcopy Rules
Three parts, in order:
- What happened — short, no blame.
- What the user can do — single clear action.
- Optional retry / help — one button.
Bad: "Oops! Something went wrong — please try again later." Good: "Can't complete that right now. Check your connection, then retry."
Empty / Loading / Error States
- Empty: one sentence + one CTA. No illustration caption.
- Loading: one word (
Loading…) or skeleton. No spinner text. - Error: see Error Messages rule.
CTAs — Button Label Discipline
- Always start with a verb.
- Match the user's mental model, not implementation terminology. "Continue" not "Advance state".
- Same action = same label everywhere in the app. If it says "Continue drills" on Home, it must say "Continue drills" in the detail sheet, not "Resume drills".
- Disabled state still has a label. Don't change wording — only the state.
Before / After Examples (real app)
| Before | After | Why |
"On fire — tap for stats" | Remove line; let icon + number speak | Em-dash + redundant instruction |
"Every tier, every run. Embark on your journey." | "6 tiers · every run on one map" | Concrete, shorter, no cliché |
"You've unlocked Level 17!" | "Level 17 unlocked" | No exclam, fewer words |
"Analyzing your session…" | Keep — real ellipsis, verb-first | Good |
"Results update: new pricing at 60 days" | Keep if user-facing and factual | Good |
"Oops! Recording failed. Let's try again." | "Recording failed. Retry." | Direct, no apology |
Review Checklist
Run this on every new string before committing:
- Zero
—characters - Zero
...(use…if needed) - Starts with a verb if it's a CTA
- Under the length cap for its element
- Sentence case (not Title Case) unless it's an ALL-CAPS eyebrow
- No banned phrases from the cheat sheet
- Same action uses the same label elsewhere in the app
- Read aloud: sounds like a person, not marketing copy
- Second person (
you/your) where the user is the subject - Present tense unless history is the point
What This Skill Is Not For
- Code comments, DocC, API docs — keep natural engineering prose.
- Server-returned user-generated content (session titles, run names). Those are data; don't rewrite them.
- Log messages (
logger.info). Those go to Logger, not users.
Sources
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

