# Default to Positive Language

**SHOULD** · **ID:** `content-positive-language` · **Category:** content
**Source:** [Vercel](https://github.com/vercel-labs/agent-skills/blob/main/skills/web-design-guidelines/SKILL.md)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/content-positive-language

> SHOULD: Frame messages around what can be done next, not what the person did wrong — drop "you failed to", "invalid", "aborted". Stay specific about the cause and the limit; positive framing is a change of frame, not a loss of detail.

Frame messages around what can be done next rather than what the person did wrong

This is about framing and tone, not content: the separate rule on actionable errors says the message must include the fix, while this one says the sentence around that fix must not read as an accusation. "You failed to upload" and "Operation aborted" blame the reader and describe a dead end, which is both unpleasant and useless. Same facts, constructive frame: name what happened without a culprit, then point forward. Positive framing is not vagueness — stay specific about the cause and the limit, just drop the "you failed", the "invalid", and the "aborted".

## Bad — do not do this

`content-positive-language-bad`

```tsx
// Blame words and dead-end words — the framing failure, not the missing fix.
const NEGATIVE_PATTERN = /\b(You failed|Invalid|aborted|Denied|cannot|Failure|illegal)\b/g;

const MESSAGES = [
  'You failed to upload avatar.png.',
  'Invalid file. Operation aborted.',
  'Denied: you cannot use that file type.',
  'Failure: illegal file size.',
];

const offenders = MESSAGES.flatMap((m) => m.match(NEGATIVE_PATTERN) ?? []);

function Highlighted({ text }: { text: string }) {
  const parts = text.split(NEGATIVE_PATTERN);
  return (
    <span className="text-sm text-foreground">
      {parts.map((part, i) =>
        i % 2 === 1 ? (
          <mark key={i} className="rounded bg-error/15 px-1 font-semibold text-error">
            {part}
          </mark>
        ) : (
          <span key={i}>{part}</span>
        ),
      )}
    </span>
  );
}

export function PositiveLanguageBad() {
  return (
    <div className="w-full max-w-md space-y-3">
      <div className="space-y-2 rounded-lg border border-error/40 bg-card p-4">
        <h4 className="text-sm font-medium text-error">Upload failed</h4>
        {MESSAGES.map((message) => (
          <p key={message}>
            <Highlighted text={message} />
          </p>
        ))}
      </div>

      <div className="flex flex-wrap items-center gap-2 text-xs">
        <span className="rounded border border-border bg-muted px-2 py-1 font-mono text-error">
          {offenders.length} blame / dead-end words
        </span>
        <span className="text-muted-foreground">0 forward paths</span>
      </div>

      <p className="text-xs text-error">
        Every sentence makes the reader the culprit and then stops. “Aborted” closes the door;
        “invalid” and “illegal” judge without saying what the limit actually is.
      </p>
    </div>
  );
}
```

## Good — do this

`content-positive-language-good`

```tsx
const NEGATIVE_PATTERN = /\b(You failed|Invalid|aborted|Denied|cannot|Failure|illegal)\b/g;

const MESSAGES = [
  'avatar.png is 12 MB — a little over the 8 MB limit.',
  'Compress it, or upload a JPEG or PNG under 8 MB.',
  'Nothing else was lost; your other files are still here.',
];

const offenders = MESSAGES.flatMap((m) => m.match(NEGATIVE_PATTERN) ?? []);

export function PositiveLanguageGood() {
  return (
    <div className="w-full max-w-md space-y-3">
      <div className="space-y-2 rounded-lg border border-border bg-card p-4">
        <h4 className="text-sm font-medium text-foreground">This file needs a little trimming</h4>
        {MESSAGES.map((message) => (
          <p key={message} className="text-sm text-muted-foreground">
            {message}
          </p>
        ))}
        <button
          type="button"
          className="mt-1 rounded-md border border-border bg-muted px-3 py-1.5 text-sm font-medium text-foreground"
        >
          Choose Another File
        </button>
      </div>

      <div className="flex flex-wrap items-center gap-2 text-xs">
        <span className="rounded border border-border bg-muted px-2 py-1 font-mono text-success">
          {offenders.length} blame / dead-end words
        </span>
        <span className="text-muted-foreground">1 forward path</span>
      </div>

      <p className="text-xs text-success">
        Same facts — same file, same 8 MB limit — with no culprit and an exit. Positive framing is
        not vagueness: the number, the cause, and the accepted formats are all still on screen.
      </p>
    </div>
  );
}
```

## References

- [Vercel Web Interface Guidelines](https://github.com/vercel-labs/web-interface-guidelines)
- [Google Style: Tone and Voice](https://developers.google.com/style/tone)
- [NN/g: Error Message Guidelines](https://www.nngroup.com/articles/error-message-guidelines/)
