# Small Touch Targets

**MUST** · **ID:** `interactions-rams-touch-targets` · **Category:** interactions
**Source:** [RAMS](https://www.rams.ai/)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/interactions-rams-touch-targets

> MUST: Clickable elements must be at least 44x44px for touch accessibility (WCAG 2.5.5 AAA). Minimum 24x24px (WCAG 2.5.8 AA). Small targets cause misclicks.

Interactive elements should be at least 44x44 pixels for touch

Rams ships this as a Serious checklist row, not prose, and cites SC 2.5.5 Target Size (Enhanced) — the 44x44px bar, which is level AAA and matches Apple's Human Interface Guidelines. Read it alongside SC 2.5.8 Target Size (Minimum), the newer WCAG 2.2 criterion: 24x24px is the level AA floor every target must clear, and 44x44px is the AAA/mobile bar Rams holds you to. They are two rungs of the same ladder, not a contradiction — clear 24px to be conformant, hit 44px on anything thumb-driven. Either way, padding can grow the hit area without changing the visual size.

## Bad — do not do this

`interactions-rams-touch-targets-bad`

```tsx
import { useState } from 'react';
import { HugeiconsIcon } from '@hugeicons/react';
import { Delete02Icon, PencilEdit02Icon, Copy01Icon } from '@hugeicons/core-free-icons';

const actions = [
  { label: 'Edit', icon: PencilEdit02Icon, tone: '' },
  { label: 'Copy', icon: Copy01Icon, tone: '' },
  { label: 'Delete', icon: Delete02Icon, tone: 'text-destructive' },
];

export function RamsTouchTargetsBad() {
  const [showArea, setShowArea] = useState(true);

  return (
    <div className="w-full max-w-sm space-y-4">
      <div className="bg-card border border-border rounded-lg p-4">
        <div className="mb-3 flex items-center justify-between">
          <h4 className="font-medium">Small Touch Targets</h4>
          <label className="flex cursor-pointer items-center gap-2 text-xs text-muted-foreground select-none">
            <input
              type="checkbox"
              checked={showArea}
              onChange={(e) => setShowArea(e.target.checked)}
              className="accent-error"
            />
            Show hit area
          </label>
        </div>
        <div className="space-y-3">
          <div className="flex items-center gap-1 p-3 bg-muted rounded-lg">
            {actions.map(({ label, icon, tone }) => (
              <button
                key={label}
                aria-label={label}
                className={`rounded p-1 transition-colors hover:bg-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring ${tone} ${
                  showArea ? 'bg-error/20 ring-2 ring-inset ring-error' : ''
                }`}
              >
                <HugeiconsIcon icon={icon} size={14} />
              </button>
            ))}
            <span className="ml-2 text-xs text-muted-foreground">~22px targets</span>
          </div>
          <div className="bg-muted rounded p-2 font-mono text-xs">
            <code className="text-error">p-1 with 14px icon = ~22px total</code>
          </div>
        </div>
      </div>
      <p className="text-xs text-error">
        Toggle the boxes: each target is only ~22px, packed tight — hard to tap accurately on mobile.
      </p>
    </div>
  );
}
```

## Good — do this

`interactions-rams-touch-targets-good`

```tsx
import { useState } from 'react';
import { HugeiconsIcon } from '@hugeicons/react';
import { Delete02Icon, PencilEdit02Icon, Copy01Icon } from '@hugeicons/core-free-icons';

const actions = [
  { label: 'Edit', icon: PencilEdit02Icon, tone: '' },
  { label: 'Copy', icon: Copy01Icon, tone: '' },
  { label: 'Delete', icon: Delete02Icon, tone: 'text-destructive' },
];

export function RamsTouchTargetsGood() {
  const [showArea, setShowArea] = useState(true);

  return (
    <div className="w-full max-w-sm space-y-4">
      <div className="bg-card border border-border rounded-lg p-4">
        <div className="mb-3 flex items-center justify-between">
          <h4 className="font-medium">44px+ Touch Targets</h4>
          <label className="flex cursor-pointer items-center gap-2 text-xs text-muted-foreground select-none">
            <input
              type="checkbox"
              checked={showArea}
              onChange={(e) => setShowArea(e.target.checked)}
              className="accent-success"
            />
            Show hit area
          </label>
        </div>
        <div className="space-y-3">
          <div className="flex gap-2 p-3 bg-muted rounded-lg">
            {actions.map(({ label, icon, tone }) => (
              <button
                key={label}
                aria-label={label}
                className={`flex min-h-[44px] min-w-[44px] items-center justify-center rounded-md p-3 transition-colors hover:bg-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring ${tone} ${
                  showArea ? 'bg-success/20 ring-2 ring-inset ring-success' : ''
                }`}
              >
                <HugeiconsIcon icon={icon} size={20} />
              </button>
            ))}
          </div>
          <div className="bg-muted rounded p-2 font-mono text-xs">
            <code>min-w-[44px] min-h-[44px]</code>
          </div>
        </div>
      </div>
      <p className="text-xs text-success">
        Toggle the boxes: each target is a full 44px — easy to tap, meets WCAG 2.5.5 AAA.
      </p>
    </div>
  );
}
```

## References

- [Rams review checklist](https://www.rams.ai/rams.md)
- [WCAG 2.5.5 Target Size (Enhanced) — AAA](https://www.w3.org/WAI/WCAG21/Understanding/target-size.html)
- [WCAG 2.5.8 Target Size (Minimum) — AA](https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html)
