# Tracking Is Size-Specific

**SHOULD** · **ID:** `content-tracking-is-size-specific` · **Category:** content
**Source:** [Emil Kowalski](https://emilkowalski.com/)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/content-tracking-is-size-specific

> SHOULD: Do not ship one `letter-spacing` for all sizes. Leave body near 0, tighten display type (~ -0.02em at 36px+), and give small caps/captions a small positive nudge. Prefer a variable font's optical-sizing axis (`font-optical-sizing: auto`) over a hardcoded value. This refines, not contradicts, "don't track body text".

Tighten letter-spacing as display type grows and leave body near zero — a single tracking value is wrong at some size

From the typography section of Emil Kowalski's apple-design skill, and it sits in DELIBERATE TENSION with content-ibelick-letter-spacing, which says never touch tracking unless asked. The corpus keeps both, because they are right about different sizes. ibelick's rule protects body text, where a face was hinted for reading and any manual tracking almost always makes it worse — that is the common case and the safe default. Emil's rule is about the extremes a text-optimised face was never spaced for: at display sizes (36px and up) the designer-intended spacing reads too loose, the letters drift apart, and the headline stops cohering, so it wants slightly NEGATIVE tracking; at caption sizes (11–12px) it wants a touch POSITIVE to hold the glyphs apart. The honest reconciliation is that the best tool is not manual tracking at all but optical sizing — `font-optical-sizing: auto` on a variable font with an `opsz` axis corrects spacing, contrast and detail per size automatically, which is exactly what "type is designed with proper spacing" should mean end to end. Reach for a manual `letter-spacing` step only when the face has no `opsz` axis. So: never randomly track body (that is ibelick's rule intact), do correct display and caption, and prefer `opsz` over a hardcoded value. Related: content-impeccable-type-scale-contrast, content-heading-size-descends, content-leading-tracks-size (its line-height counterpart).

## Rule snippet

```tsx
h1 { font-size: 48px; letter-spacing: -0.02em; }
.caption { font-size: 11px; letter-spacing: 0.04em; }
body { font-optical-sizing: auto; }
```

## Bad — do not do this

`content-tracking-is-size-specific-bad`

```tsx
export function TrackingIsSizeSpecificBad() {
  return (
    <div className="w-full max-w-sm">
      {/* One tracking value (0) applied across every size */}
      <div className="rounded-lg border border-border bg-card p-5" style={{ letterSpacing: '0' }}>
        <p className="text-foreground" style={{ fontSize: '40px', lineHeight: 1.05, fontWeight: 700 }}>
          Ship faster
        </p>
        <p className="mt-3 text-muted-foreground uppercase" style={{ fontSize: '11px', fontWeight: 600 }}>
          Updated 3 minutes ago
        </p>
      </div>
      <p className="mt-4 text-xs text-destructive">
        One tracking value for all sizes: the display line reads loose and airy, the tiny caps read cramped.
      </p>
    </div>
  );
}
```

## Good — do this

`content-tracking-is-size-specific-good`

```tsx
import { useState } from 'react';

/**
 * Interactive proof of "size-specific": one slider sets a single letter-spacing for BOTH
 * a 40px headline and an 11px caption. There is no value that flatters both at once —
 * tighten and the caption cramps, loosen and the headline drifts. The toggle applies
 * per-size values (display −0.02em, caption +0.05em) and both finally land.
 */
export function TrackingIsSizeSpecificGood() {
  const [ls, setLs] = useState(0.04);
  const [sizeSpecific, setSizeSpecific] = useState(false);

  const displayLs = sizeSpecific ? -0.02 : ls;
  const captionLs = sizeSpecific ? 0.05 : ls;
  const headlineLoose = !sizeSpecific && ls > -0.005;
  const captionCramped = !sizeSpecific && ls < 0.03;

  return (
    <div className="w-full max-w-sm">
      <div className="rounded-lg border border-border bg-card p-5">
        <p className="text-foreground" style={{ fontSize: '40px', lineHeight: 1.05, fontWeight: 700, letterSpacing: `${displayLs}em` }}>
          Ship faster
        </p>
        <p className="mt-3 text-muted-foreground uppercase" style={{ fontSize: '11px', fontWeight: 600, letterSpacing: `${captionLs}em` }}>
          Updated 3 minutes ago
        </p>

        <div className="mt-5 space-y-2 border-t border-border pt-4">
          <label className="flex items-center justify-between gap-3 text-xs text-muted-foreground">
            <span className="shrink-0">One value, all sizes</span>
            <input
              type="range"
              min={-0.04}
              max={0.08}
              step={0.005}
              value={ls}
              disabled={sizeSpecific}
              onChange={(e) => setLs(parseFloat(e.target.value))}
              className="w-full disabled:opacity-40"
            />
          </label>
          <p className="font-mono text-xs text-foreground">
            {sizeSpecific ? 'display −0.02em · caption +0.05em' : `${ls >= 0 ? '+' : ''}${ls.toFixed(3)}em everywhere`}
          </p>
          <p className="min-h-4 text-xs text-destructive">
            {!sizeSpecific && headlineLoose && 'Headline drifts loose'}
            {!sizeSpecific && headlineLoose && captionCramped && ' · '}
            {!sizeSpecific && captionCramped && 'Caption too cramped'}
          </p>
          <label className="flex items-center gap-2 text-xs text-foreground">
            <input
              type="checkbox"
              checked={sizeSpecific}
              onChange={(e) => setSizeSpecific(e.target.checked)}
              className="size-4 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
            />
            Use size-specific tracking
          </label>
        </div>
      </div>
      <p className="mt-4 text-xs text-success">
        Drag the slider — no single value flatters both sizes. Size-specific tracking (ideally via a variable font’s
        optical-sizing axis) is the only fix.
      </p>
    </div>
  );
}
```

## References

- [Emil Kowalski — apple-design SKILL.md](https://raw.githubusercontent.com/emilkowalski/skills/main/skills/apple-design/SKILL.md)
- [MDN — font-optical-sizing](https://developer.mozilla.org/en-US/docs/Web/CSS/font-optical-sizing)
- [MDN — letter-spacing](https://developer.mozilla.org/en-US/docs/Web/CSS/letter-spacing)
