# Em-Dashes Are an AI Tell

**SHOULD** · **ID:** `content-impeccable-em-dash-overuse` · **Category:** content
**Source:** [impeccable](https://impeccable.style/)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/content-impeccable-em-dash-overuse

> SHOULD: Punctuate with commas, colons, semicolons, periods, or parentheses instead of em-dashes (and never the ASCII `--`) — 5 or more in body text is a machine-written cadence readers register even when each individual dash is defensible.

Punctuate with commas, colons, semicolons, periods, or parentheses instead of em-dashes

The skill itself bans the em dash outright, but its detector is deliberately lenient: it only fires at 5 or more em-dashes (or the ASCII `--`) in body text. Be accurate about that distinction. A single dash is a style choice; a page where every paragraph pivots on the same mid-sentence interruption has a machine-written cadence, and it is the cadence readers register, not any individual mark. Repunctuating forces you to pick the mark that actually carries the relationship, which varies the rhythm as a side effect.

## Bad — do not do this

`content-impeccable-em-dash-overuse-bad`

```tsx
const PARAGRAPHS = [
  'Preview deployments are instant — every push gets its own URL — and the URL never changes.',
  'Your team reviews the branch — not a screenshot — so feedback lands on the real thing.',
  'Rollback is one click — no rebuild, no waiting — because the old output is still on disk.',
  'Pricing is usage-based — you pay for what you serve — and the free tier covers side projects.',
  'It is the same runtime locally and in production -- no drift, no surprises.',
];

const DASH_PATTERN = /—|--/g;
const dashCount = PARAGRAPHS.join(' ').match(DASH_PATTERN)?.length ?? 0;

function Highlighted({ text }: { text: string }) {
  const parts = text.split(DASH_PATTERN);
  const dashes = text.match(DASH_PATTERN) ?? [];
  return (
    <p className="text-sm leading-[1.6] text-foreground">
      {parts.map((part, i) => (
        <span key={i}>
          {part}
          {i < dashes.length && (
            <mark className="rounded bg-error/15 px-1 font-semibold text-error">{dashes[i]}</mark>
          )}
        </span>
      ))}
    </p>
  );
}

export function ImpeccableEmDashOveruseBad() {
  return (
    <div className="space-y-3">
      <div className="space-y-2 rounded-lg border border-border bg-card p-4">
        {PARAGRAPHS.map((p) => (
          <Highlighted key={p} text={p} />
        ))}
      </div>

      <div className="flex items-center gap-2 text-xs">
        <span className="rounded border border-border bg-muted px-2 py-1 font-mono text-error">
          {dashCount} em-dashes in body copy
        </span>
        <span className="text-muted-foreground">detector fires at 5 or more</span>
      </div>

      <p className="text-xs text-error">
        Every sentence pivots on the same interruption, including the ASCII{' '}
        <code className="rounded bg-muted px-1 font-mono">--</code> the detector also counts. impeccable
        bans the em dash outright, but the detector is deliberately lenient and only fires at 5+,
        because it is the repeated cadence that reads as machine-written, not any single dash.
      </p>
    </div>
  );
}
```

## Good — do this

`content-impeccable-em-dash-overuse-good`

```tsx
const PARAGRAPHS = [
  'Preview deployments are instant: every push gets its own URL, and that URL never changes.',
  'Your team reviews the branch itself, not a screenshot, so feedback lands on the real thing.',
  'Rollback is one click. No rebuild, no waiting, because the old output is still on disk.',
  'Pricing is usage-based (you pay for what you serve), and the free tier covers side projects.',
  'It is the same runtime locally and in production, so nothing drifts.',
];

const DASH_PATTERN = /—|--/g;
const dashCount = PARAGRAPHS.join(' ').match(DASH_PATTERN)?.length ?? 0;

export function ImpeccableEmDashOveruseGood() {
  return (
    <div className="space-y-3">
      <div className="space-y-2 rounded-lg border border-border bg-card p-4">
        {PARAGRAPHS.map((p) => (
          <p key={p} className="text-sm leading-[1.6] text-foreground">
            {p}
          </p>
        ))}
      </div>

      <div className="flex items-center gap-2 text-xs">
        <span className="rounded border border-border bg-muted px-2 py-1 font-mono text-success">
          {dashCount} em-dashes in body copy
        </span>
        <span className="text-muted-foreground">clean</span>
      </div>

      <p className="text-xs text-success">
        Same five claims, same length, punctuated with the marks that actually carry the meaning: a
        colon to introduce, commas to nest an aside, a period to stop, parentheses to whisper. The
        rhythm now varies from sentence to sentence, which is the part a reader registers as human.
      </p>
    </div>
  );
}
```

## References

- [impeccable](https://impeccable.style/)
- [pbakaus/impeccable](https://github.com/pbakaus/impeccable)
- [Practical Typography: Type composition](https://practicaltypography.com/type-composition.html)
