# CSS Containment for Performance

**SHOULD** · **ID:** `performance-css-containment` · **Category:** performance
**Source:** [Web Platform](https://web.dev/)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/performance-css-containment

> SHOULD: Use CSS `contain: layout paint` on reusable cards/list items to isolate layout/paint scope. Use `content-visibility: auto` with `contain-intrinsic-size` for offscreen content (up to 7x render improvement).

Use CSS contain and content-visibility to isolate layout/paint scope

CSS containment tells the browser that an element's layout/paint is independent from the rest of the page. content-visibility: auto skips rendering offscreen content entirely, dramatically improving initial render for long lists.

## Bad — do not do this

`performance-css-containment-bad`

```tsx
export function CssContainmentBad() {
  const items = Array.from({ length: 5 }, (_, i) => ({
    id: i,
    title: `Item ${i + 1}`,
    description: 'Card description text',
  }));

  return (
    <div className="space-y-4">
      <div className="space-y-2 max-h-40 overflow-y-auto">
        {items.map((item) => (
          <div
            key={item.id}
            className="p-3 bg-muted rounded-lg"
            // No CSS containment - layout changes affect entire document
          >
            <h4 className="font-medium text-sm">{item.title}</h4>
            <p className="text-xs text-muted-foreground">{item.description}</p>
          </div>
        ))}
      </div>
      <p className="text-xs text-destructive">
        ✗ No CSS containment - layout changes in cards affect entire document tree
      </p>
    </div>
  );
}
```

## Good — do this

`performance-css-containment-good`

```tsx
const cards = Array.from({ length: 40 }, (_, i) => i);

export function CssContainmentGood() {
  return (
    <div className="space-y-4">
      <p className="text-sm text-muted-foreground">Scroll the list.</p>
      <div className="space-y-2 max-h-40 overflow-y-auto overscroll-contain pr-1">
        {cards.map((i) => (
          <div
            key={i}
            className="p-3 bg-muted rounded-lg [contain:layout_paint] [content-visibility:auto] [contain-intrinsic-size:auto_64px]"
          >
            <h4 className="font-medium text-sm">Item {i + 1}</h4>
            <p className="text-xs text-muted-foreground">Card description text</p>
          </div>
        ))}
      </div>
      <p className="text-xs text-success">
        <code>contain: layout paint</code> isolates each card's layout scope, and <code>content-visibility: auto</code>
        {' '}skips rendering the cards that are scrolled out of view. <code>contain-intrinsic-size</code> reserves their
        height so the scrollbar does not jump
      </p>
    </div>
  );
}
```

## References

- [MDN CSS contain](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/contain)
- [web.dev content-visibility](https://web.dev/articles/content-visibility)
